diff options
Diffstat (limited to 'support/texlab')
85 files changed, 2136 insertions, 599 deletions
diff --git a/support/texlab/CHANGELOG.md b/support/texlab/CHANGELOG.md index 8008720d9b..526ec6889c 100644 --- a/support/texlab/CHANGELOG.md +++ b/support/texlab/CHANGELOG.md @@ -5,6 +5,65 @@ 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.21.0] - 2024-10-26 + +### Added + +- Support starred variants in "Go to References" ([#1234](https://github.com/latex-lsp/texlab/issues/1234)) +- Add `texlab.latexindent.replacement` setting to allow passing a replacement flag to `latexindent` ([#1222](https://github.com/latex-lsp/texlab/issues/1222)) +- Don't require a label to show section numbers for document symbols ([#910](https://github.com/latex-lsp/texlab/issues/910)) +- Support navigating to files that are part of the `TEXINPUTS` similar to `BIBINPUTS` ([#1228](https://github.com/latex-lsp/texlab/discussions/1228)) + +### Fixed + +- Fix opening `untitled` documents ([#1242](https://github.com/latex-lsp/texlab/issues/1242)) +- Handle `\bibitem` when checking for undefined references ([#1171](https://github.com/latex-lsp/texlab/issues/1171)) +- Fix false-positive syntax error when using a command inside a `\label` ([#879](https://github.com/latex-lsp/texlab/issues/879)) + +## [5.20.0] - 2024-10-08 + +### Added + +- Add `texlab.inlayHints.maxLength` setting to allow limiting inlay hint text length ([#1212](https://github.com/latex-lsp/texlab/issues/1212)) +- Allow suppressing diagnostics using `% texlab: ignore` magic comments ([#1211](https://github.com/latex-lsp/texlab/pull/1211)) + +### Fixed + +- Fix enabling `texlab.build.useFileList` setting +- Make "Goto Definition" work correctly with starred commands ([#1197](https://github.com/latex-lsp/texlab/issues/1197)) + +## [5.19.0] - 2024-07-08 + +### Added + +- Add `texlab.build.useFileList` setting to allow controlling whether to use the `.fls` files + +### Changed + +- Disable using `.fls` files for project detection by default + +## [5.18.0] - 2024-07-06 + +### Added + +- Parse `.fls` files to make the project detection more reliable ([#1145](https://github.com/latex-lsp/texlab/issues/1145)) + +### Fixed + +- Fix parsing commands with unicode characters inside BibTeX entries + ([#1147](https://github.com/latex-lsp/texlab/issues/1147)) +- Improve detection of included files when non-ASCII characters are used ([#923](https://github.com/latex-lsp/texlab/issues/923)) +- Fix resolving includes starting from files included using `\subimport` ([#1145](https://github.com/latex-lsp/texlab/issues/1145)) + +## [5.17.0] - 2024-06-23 + +### Added + +- Add label commands from `zref` and `zref-clever` to the list of default label commands + ([#1140](https://github.com/latex-lsp/texlab/pull/1140)) +- Add `texlab.experimental.labelDefinitionPrefixes` and + `texlab.experimental.labelReferencePrefixes` options ([#1139](https://github.com/latex-lsp/texlab/pull/1139)) + ## [5.16.1] - 2024-05-25 ### Fixed diff --git a/support/texlab/Cargo.lock b/support/texlab/Cargo.lock index 9d651234e1..aca7dcd943 100644 --- a/support/texlab/Cargo.lock +++ b/support/texlab/Cargo.lock @@ -3,10 +3,10 @@ version = 3 [[package]] -name = "adler" -version = "1.0.2" +name = "adler2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "aho-corasick" @@ -39,9 +39,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.3" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b84bf0a05bbb2a83e5eb6fa36bb6e87baa08193c35ff52bbf6b38d8af2890e46" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" [[package]] name = "anstyle-parse" @@ -73,9 +73,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.86" +version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +checksum = "37bf3594c4c988a53154954629820791dde498571819ae4ca50ca811e060cc95" [[package]] name = "autocfg" @@ -90,7 +90,7 @@ dependencies = [ "bibtex-utils", "dirs", "distro", - "itertools 0.12.1", + "itertools 0.13.0", "line-index", "log", "notify", @@ -99,7 +99,7 @@ dependencies = [ "percent-encoding", "regex", "rowan", - "rustc-hash", + "rustc-hash 2.0.0", "shellexpand", "syntax", "titlecase", @@ -130,10 +130,10 @@ dependencies = [ "chrono", "expect-test", "human_name", - "itertools 0.12.1", + "itertools 0.13.0", "parser", "rowan", - "rustc-hash", + "rustc-hash 2.0.0", "syntax", "unicode-normalization", ] @@ -152,9 +152,9 @@ checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" [[package]] name = "bstr" -version = "1.9.1" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" +checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" dependencies = [ "memchr", "regex-automata", @@ -232,10 +232,10 @@ dependencies = [ "bibtex-utils", "expect-test", "isocountry", - "itertools 0.12.1", + "itertools 0.13.0", "parser", "rowan", - "rustc-hash", + "rustc-hash 2.0.0", "syntax", "titlecase", "unicode-normalization", @@ -244,9 +244,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.4" +version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" dependencies = [ "clap_builder", "clap_derive", @@ -254,9 +254,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.2" +version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" dependencies = [ "anstream", "anstyle", @@ -266,9 +266,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.4" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" dependencies = [ "heck", "proc-macro2", @@ -298,11 +298,11 @@ dependencies = [ "crossbeam-channel", "distro", "expect-test", - "itertools 0.12.1", + "itertools 0.13.0", "libc", "log", "rowan", - "rustc-hash", + "rustc-hash 2.0.0", "syntax", "test-utils", "thiserror", @@ -334,9 +334,10 @@ dependencies = [ "expect-test", "fuzzy-matcher", "line-index", + "parser", "rayon", "rowan", - "rustc-hash", + "rustc-hash 2.0.0", "syntax", "test-utils", "url", @@ -347,9 +348,9 @@ name = "completion-data" version = "0.0.0" dependencies = [ "flate2", - "itertools 0.12.1", + "itertools 0.13.0", "once_cell", - "rustc-hash", + "rustc-hash 2.0.0", "serde", "serde_json", ] @@ -407,9 +408,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.12" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" dependencies = [ "crossbeam-utils", ] @@ -449,9 +450,9 @@ name = "definition" version = "0.0.0" dependencies = [ "base-db", - "itertools 0.12.1", + "itertools 0.13.0", "rowan", - "rustc-hash", + "rustc-hash 2.0.0", "syntax", "test-utils", ] @@ -464,14 +465,14 @@ dependencies = [ "encoding_rs", "encoding_rs_io", "expect-test", - "itertools 0.12.1", + "itertools 0.13.0", "line-index", "log", "multimap", "once_cell", "regex", "rowan", - "rustc-hash", + "rustc-hash 2.0.0", "syntax", "test-utils", "url", @@ -509,7 +510,7 @@ name = "distro" version = "0.0.0" dependencies = [ "anyhow", - "rustc-hash", + "rustc-hash 2.0.0", ] [[package]] @@ -558,15 +559,15 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" [[package]] name = "fern" -version = "0.6.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9f0c14694cbd524c8720dd69b0e3179344f04ebb5f90f2e4a440c6ea3b2f1ee" +checksum = "69ff9c9d5fb3e6da8ac2f77ab76fe7e8087d512ce095200f8f29ac5b656cf6dc" dependencies = [ "log", ] @@ -594,9 +595,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.30" +version = "1.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" +checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" dependencies = [ "crc32fast", "miniz_oxide", @@ -648,9 +649,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.10" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" dependencies = [ "cfg-if", "libc", @@ -682,6 +683,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" [[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + +[[package]] name = "highlights" version = "0.0.0" dependencies = [ @@ -707,9 +714,9 @@ dependencies = [ [[package]] name = "human_name" -version = "2.0.3" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d40d3a11bc31949eb1d5070deb0cbb481bbd17d9423ecd3afdb3db5d81499b9" +checksum = "fbd70ad9620f3a33a270cedfa47114c9566660c28ff8d0b3772b5c8dc1dac085" dependencies = [ "compact_str", "crossbeam-utils", @@ -741,7 +748,7 @@ dependencies = [ "base-db", "expect-test", "rowan", - "rustc-hash", + "rustc-hash 2.0.0", "test-utils", ] @@ -780,13 +787,13 @@ dependencies = [ [[package]] name = "is-terminal" -version = "0.4.9" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" dependencies = [ - "hermit-abi", - "rustix", - "windows-sys 0.48.0", + "hermit-abi 0.4.0", + "libc", + "windows-sys 0.52.0", ] [[package]] @@ -810,9 +817,9 @@ dependencies = [ [[package]] name = "itertools" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ "either", ] @@ -860,15 +867,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.154" +version = "0.2.161" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" +checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" [[package]] name = "line-index" version = "0.0.0" dependencies = [ - "rustc-hash", + "rustc-hash 2.0.0", "text-size", ] @@ -883,9 +890,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "lock_api" @@ -899,24 +906,24 @@ dependencies = [ [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "logos" -version = "0.14.0" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "161971eb88a0da7ae0c333e1063467c5b5727e7fb6b710b8db4814eade3a42e8" +checksum = "1c6b6e02facda28ca5fb8dbe4b152496ba3b1bd5a4b40bb2b1b2d8ad74e0f39b" dependencies = [ "logos-derive", ] [[package]] name = "logos-codegen" -version = "0.14.0" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e31badd9de5131fdf4921f6473d457e3dd85b11b7f091ceb50e4df7c3eeb12a" +checksum = "b32eb6b5f26efacd015b000bfc562186472cd9b34bdba3f6b264e2a052676d10" dependencies = [ "beef", "fnv", @@ -929,18 +936,18 @@ dependencies = [ [[package]] name = "logos-derive" -version = "0.14.0" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c2a69b3eb68d5bd595107c9ee58d7e07fe2bb5e360cc85b0f084dedac80de0a" +checksum = "3e5d0c5463c911ef55624739fc353238b4e310f0144be1f875dc42fec6bfd5ec" dependencies = [ "logos-codegen", ] [[package]] name = "lsp-server" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248f65b78f6db5d8e1b1604b4098a28b43d21a8eb1deeca22b1c421b276c7095" +checksum = "550446e84739dcaf6d48a4a093973850669e13e8a34d8f8d64851041be267cd9" dependencies = [ "crossbeam-channel", "log", @@ -984,11 +991,11 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ - "adler", + "adler2", ] [[package]] @@ -1043,9 +1050,9 @@ dependencies = [ [[package]] name = "notify-debouncer-full" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f5dab59c348b9b50cf7f261960a20e389feb2713636399cd9082cd4b536154" +checksum = "fb7fd166739789c9ff169e654dc1501373db9d80a4c3f972817c8a4d7cf8f34e" dependencies = [ "crossbeam-channel", "file-id", @@ -1070,15 +1077,15 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.3", "libc", ] [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "oorandom" @@ -1094,9 +1101,9 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "parking_lot" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -1126,7 +1133,7 @@ dependencies = [ "pathdiff", "regex", "rowan", - "rustc-hash", + "rustc-hash 2.0.0", "syntax", "tempfile", "versions", @@ -1134,9 +1141,9 @@ dependencies = [ [[package]] name = "pathdiff" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" +checksum = "d61c5ce1153ab5b689d0c074c4e7fc613e942dfb7dd9eea5ab202d2ad91fe361" [[package]] name = "percent-encoding" @@ -1298,16 +1305,16 @@ version = "0.0.0" dependencies = [ "base-db", "rowan", - "rustc-hash", + "rustc-hash 2.0.0", "syntax", "test-utils", ] [[package]] name = "regex" -version = "1.10.4" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" dependencies = [ "aho-corasick", "memchr", @@ -1317,9 +1324,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.6" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" dependencies = [ "aho-corasick", "memchr", @@ -1328,31 +1335,31 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.2" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "rename" version = "0.0.0" dependencies = [ "base-db", + "parser", "rowan", - "rustc-hash", + "rustc-hash 2.0.0", "syntax", "test-utils", ] [[package]] name = "rowan" -version = "0.15.15" +version = "0.15.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a58fa8a7ccff2aec4f39cc45bf5f985cec7125ab271cf681c279fd00192b49" +checksum = "0a542b0253fa46e632d27a1dc5cf7b930de4df8659dc6e720b647fc72147ae3d" dependencies = [ "countme", "hashbrown", - "memoffset", - "rustc-hash", + "rustc-hash 1.1.0", "text-size", ] @@ -1363,10 +1370,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] +name = "rustc-hash" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" + +[[package]] name = "rustix" -version = "0.38.31" +version = "0.38.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" dependencies = [ "bitflags 2.4.0", "errno", @@ -1404,18 +1417,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.202" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.202" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", @@ -1424,11 +1437,12 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.117" +version = "1.0.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] @@ -1494,7 +1508,7 @@ dependencies = [ "base-db", "distro", "expect-test", - "itertools 0.12.1", + "itertools 0.13.0", "line-index", "regex", "rowan", @@ -1519,20 +1533,22 @@ dependencies = [ name = "syntax" version = "0.0.0" dependencies = [ - "itertools 0.12.1", + "itertools 0.13.0", "rowan", + "rustc-hash 2.0.0", ] [[package]] name = "tempfile" -version = "3.10.1" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" dependencies = [ "cfg-if", "fastrand", + "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1548,7 +1564,7 @@ dependencies = [ [[package]] name = "texlab" -version = "5.16.1" +version = "5.21.0" dependencies = [ "anyhow", "base-db", @@ -1582,7 +1598,7 @@ dependencies = [ "regex", "rename", "rowan", - "rustc-hash", + "rustc-hash 2.0.0", "serde", "serde_json", "serde_regex", @@ -1601,18 +1617,18 @@ checksum = "f18aa187839b2bdb1ad2fa35ead8c4c2976b64e4363c386d45ac0f7ee85c9233" [[package]] name = "thiserror" -version = "1.0.59" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.59" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", @@ -1665,9 +1681,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "titlecase" -version = "3.0.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb3223c7b6ee54ec8521af7b060ad616cb90a66d0b94d05af340de30cf02c514" +checksum = "e0e20e744fbec1913fa168f3ffbef64324bbcb152c6cda8394baa79fa5ec9142" dependencies = [ "regex", ] @@ -1703,9 +1719,9 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" dependencies = [ "tinyvec", ] @@ -1724,9 +1740,9 @@ checksum = "402bb19d8e03f1d1a7450e2bd613980869438e0666331be3e073089124aa1adc" [[package]] name = "url" -version = "2.5.0" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", "idna", @@ -1742,11 +1758,11 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "versions" -version = "6.2.0" +version = "6.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38a8931f8d167b6448076020e70b9de46dcf5ea1731212481a092d0071c4ac5b" +checksum = "f25d498b63d1fdb376b4250f39ab3a5ee8d103957346abacd911e2d8b612c139" dependencies = [ - "itertools 0.12.1", + "itertools 0.13.0", "nom", ] @@ -1768,9 +1784,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.87" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1778,9 +1794,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.87" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", @@ -1793,9 +1809,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.87" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1803,9 +1819,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.87" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", @@ -1816,9 +1832,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.87" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "web-sys" @@ -1876,7 +1892,16 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.4", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", ] [[package]] @@ -1896,17 +1921,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.4", - "windows_aarch64_msvc 0.52.4", - "windows_i686_gnu 0.52.4", - "windows_i686_msvc 0.52.4", - "windows_x86_64_gnu 0.52.4", - "windows_x86_64_gnullvm 0.52.4", - "windows_x86_64_msvc 0.52.4", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -1917,9 +1943,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -1929,9 +1955,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -1941,9 +1967,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.4" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -1953,9 +1985,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -1965,9 +1997,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -1977,9 +2009,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -1989,6 +2021,6 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/support/texlab/Cargo.toml b/support/texlab/Cargo.toml index 3894cb649f..c898466e4e 100644 --- a/support/texlab/Cargo.toml +++ b/support/texlab/Cargo.toml @@ -3,13 +3,33 @@ members = ["crates/*"] resolver = "2" [workspace.package] +version = "5.21.0" license = "GPL-3.0" authors = [ "Eric Förster <eric.foerster@outlook.com>", "Patrick Förster <patrick.foerster@outlook.de>", ] edition = "2021" -rust-version = "1.75" +rust-version = "1.80" + +[workspace.dependencies] +anyhow = "1.0.90" +criterion = "0.5.1" +crossbeam-channel = "0.5.13" +expect-test = "1.5.0" +itertools = "0.13.0" +log = "0.4.22" +notify = "6.1.1" +once_cell = "1.20.2" +regex = "1.11.0" +rowan = "0.15.16" +rustc-hash = "2.0.0" +serde = { version = "1.0.210", features = ["derive"] } +serde_json = "1.0.132" +tempfile = "3.13.0" +titlecase = "3.3.0" +unicode-normalization = "0.1.24" +url = "2.5.2" [profile.release] lto = "thin" diff --git a/support/texlab/crates/base-db/Cargo.toml b/support/texlab/crates/base-db/Cargo.toml index ca477c496c..04fde63b56 100644 --- a/support/texlab/crates/base-db/Cargo.toml +++ b/support/texlab/crates/base-db/Cargo.toml @@ -10,20 +10,20 @@ rust-version.workspace = true bibtex-utils = { path = "../bibtex-utils" } dirs = "5.0.1" distro = { path = "../distro" } -itertools = "0.12.1" +itertools.workspace = true line-index = { path = "../line-index" } -log = "0.4.21" -notify = "6.0.1" -once_cell = "1.19.0" +log.workspace = true +notify.workspace = true +once_cell.workspace = true parser = { path = "../parser" } percent-encoding = "2.3.0" -regex = "1.10.4" -rowan = "0.15.15" -rustc-hash = "1.1.0" +regex.workspace = true +rowan.workspace = true +rustc-hash.workspace = true shellexpand = "3.1.0" syntax = { path = "../syntax" } -titlecase = "3.0.0" -url = "2.5.0" +titlecase.workspace = true +url.workspace = true [lib] doctest = false diff --git a/support/texlab/crates/base-db/src/config.rs b/support/texlab/crates/base-db/src/config.rs index e4c827e61c..5774f2a0ec 100644 --- a/support/texlab/crates/base-db/src/config.rs +++ b/support/texlab/crates/base-db/src/config.rs @@ -68,6 +68,7 @@ pub enum Formatter { pub struct LatexIndentConfig { pub local: Option<String>, pub modify_line_breaks: bool, + pub replacement: Option<String>, } #[derive(Debug, Default)] @@ -80,6 +81,7 @@ pub struct SymbolConfig { pub struct InlayHintConfig { pub label_definitions: bool, pub label_references: bool, + pub max_length: Option<usize>, } #[derive(Debug)] @@ -140,6 +142,7 @@ impl Default for InlayHintConfig { Self { label_definitions: true, label_references: true, + max_length: None, } } } diff --git a/support/texlab/crates/base-db/src/deps/discover.rs b/support/texlab/crates/base-db/src/deps/discover.rs index cbcab4d8fb..3039d7b0f4 100644 --- a/support/texlab/crates/base-db/src/deps/discover.rs +++ b/support/texlab/crates/base-db/src/deps/discover.rs @@ -15,7 +15,7 @@ pub fn watch( ) { let roots = workspace .iter() - .map(|document| &document.dir) + .filter_map(|document| document.dir.as_ref()) .filter(|dir| dir.scheme() == "file") .unique() .map(|dir| ProjectRoot::walk_and_find(workspace, dir)); diff --git a/support/texlab/crates/base-db/src/deps/graph.rs b/support/texlab/crates/base-db/src/deps/graph.rs index 4b2399b7f8..aec5cea92b 100644 --- a/support/texlab/crates/base-db/src/deps/graph.rs +++ b/support/texlab/crates/base-db/src/deps/graph.rs @@ -1,4 +1,4 @@ -use std::{ffi::OsStr, path::PathBuf, rc::Rc}; +use std::{ffi::OsStr, path::PathBuf, rc::Rc, sync::Arc}; use distro::Language; use itertools::Itertools; @@ -23,6 +23,7 @@ pub struct Edge { #[derive(Debug, PartialEq, Eq, Clone, Hash)] pub enum EdgeData { DirectLink(Box<DirectLinkData>), + FileList(Arc<ProjectRoot>), AdditionalFiles, Artifact, } @@ -54,7 +55,11 @@ impl Graph { start: start.uri.clone(), }; - let root = ProjectRoot::walk_and_find(workspace, &start.dir); + let Some(start_dir) = &start.dir else { + return graph; + }; + + let root = ProjectRoot::walk_and_find(workspace, start_dir); let mut stack = vec![(start, Rc::new(root))]; let mut visited = FxHashSet::default(); @@ -74,6 +79,7 @@ impl Graph { if visited.insert(edge.target.clone()) { let new_root = match &edge.data { EdgeData::DirectLink(data) => data.new_root.clone(), + EdgeData::FileList(root) => Some(root.as_ref().clone()), _ => None, }; @@ -101,6 +107,10 @@ impl Graph { self.add_direct_links(workspace, start); self.add_artifacts(workspace, start); self.add_additional_files(workspace, start); + + if workspace.config().syntax.use_file_list { + self.add_file_list_links(workspace, start); + } } fn add_additional_files(&mut self, workspace: &Workspace, start: Start) { @@ -120,6 +130,43 @@ impl Graph { } } + fn add_file_list_links(&mut self, workspace: &Workspace, start: Start) -> Option<()> { + let file_list = start.source.data.as_file_list()?; + let home_dir = HOME_DIR.as_deref(); + + let working_dir = file_list + .working_dir + .as_deref() + .and_then(|dir| Url::from_directory_path(dir).ok()); + + let working_dir = working_dir.as_ref().or(start.source.dir.as_ref())?; + let new_root = Arc::new(ProjectRoot::from_config(workspace, working_dir)); + + for target_uri in file_list + .inputs + .iter() + .chain(file_list.outputs.iter()) + .filter(|path| { + path.is_relative() + || Language::from_path(&path) == Some(Language::Bib) + || home_dir.map_or(false, |home_dir| path.starts_with(home_dir)) + }) + .filter_map(|path| working_dir.join(path.to_str()?).ok()) + { + if workspace.lookup(&target_uri).is_some() { + self.edges.push(Edge { + source: start.source.uri.clone(), + target: target_uri, + data: EdgeData::FileList(Arc::clone(&new_root)), + }); + } else { + self.missing.push(target_uri); + } + } + + Some(()) + } + fn add_direct_links(&mut self, workspace: &Workspace, start: Start) -> Option<()> { let data = start.source.data.as_tex()?; @@ -169,7 +216,7 @@ impl Graph { .base_dir .as_deref() .and_then(|path| start.root.src_dir.join(path).ok()) - .map(|dir| ProjectRoot::walk_and_find(workspace, &dir)); + .map(|dir| ProjectRoot::from_config(workspace, &dir)); let link_data = DirectLinkData { link: link.clone(), @@ -197,27 +244,33 @@ impl Graph { } let root = start.root; - let relative_path = root.compile_dir.make_relative(&start.source.uri).unwrap(); - - self.add_artifact( - workspace, - start.source, - &root.aux_dir.join(&relative_path).unwrap(), - "aux", - ); + self.add_artifact_group(workspace, start, &root.aux_dir, "aux"); + self.add_artifact_group(workspace, start, &root.log_dir, "log"); + self.add_artifact_group(workspace, start, &root.aux_dir, "fls"); + } - self.add_artifact(workspace, start.source, &root.aux_dir, "aux"); - self.add_artifact(workspace, start.source, &root.compile_dir, "aux"); + fn add_artifact_group( + &mut self, + workspace: &Workspace, + start: Start, + dir: &Url, + extension: &str, + ) { + let relative_path = start + .root + .compile_dir + .make_relative(&start.source.uri) + .unwrap(); self.add_artifact( workspace, start.source, - &root.log_dir.join(&relative_path).unwrap(), - "log", + &dir.join(&relative_path).unwrap(), + extension, ); - self.add_artifact(workspace, start.source, &root.log_dir, "log"); - self.add_artifact(workspace, start.source, &root.compile_dir, "log"); + self.add_artifact(workspace, start.source, &dir, extension); + self.add_artifact(workspace, start.source, &start.root.compile_dir, extension); } fn add_artifact( diff --git a/support/texlab/crates/base-db/src/deps/root.rs b/support/texlab/crates/base-db/src/deps/root.rs index 03fd41a9a0..aa3969353d 100644 --- a/support/texlab/crates/base-db/src/deps/root.rs +++ b/support/texlab/crates/base-db/src/deps/root.rs @@ -45,7 +45,7 @@ impl ProjectRoot { pub fn from_tectonic(workspace: &Workspace, dir: &Url) -> Option<Self> { let exists = workspace .iter() - .filter(|document| document.dir == *dir) + .filter(|document| document.dir.as_ref() == Some(dir)) .any(|document| matches!(document.data, DocumentData::Tectonic)); if !exists { @@ -77,7 +77,7 @@ impl ProjectRoot { let config = workspace.config(); let rcfile = workspace .iter() - .filter(|document| document.dir == *dir) + .filter(|document| document.dir.as_ref() == Some(dir)) .find_map(|document| document.data.as_latexmkrc())?; let compile_dir = dir.clone(); @@ -121,7 +121,7 @@ impl ProjectRoot { pub fn from_rootfile(workspace: &Workspace, dir: &Url) -> Option<Self> { let exists = workspace .iter() - .filter(|document| document.dir == *dir) + .filter(|document| document.dir.as_ref() == Some(dir)) .any(|document| matches!(document.data, DocumentData::Root)); if !exists { diff --git a/support/texlab/crates/base-db/src/document.rs b/support/texlab/crates/base-db/src/document.rs index 7a388129fd..0b81fa2871 100644 --- a/support/texlab/crates/base-db/src/document.rs +++ b/support/texlab/crates/base-db/src/document.rs @@ -3,7 +3,7 @@ use std::path::PathBuf; use distro::Language; use line_index::{LineCol, LineIndex}; use rowan::TextRange; -use syntax::{bibtex, latex, latexmkrc::LatexmkrcData, BuildError}; +use syntax::{bibtex, file_list::FileList, latex, latexmkrc::LatexmkrcData, BuildError}; use url::Url; use crate::{semantics, Config}; @@ -28,7 +28,7 @@ pub struct DocumentParams<'a> { #[derive(Clone)] pub struct Document { pub uri: Url, - pub dir: Url, + pub dir: Option<Url>, pub path: Option<PathBuf>, pub text: String, pub line_index: LineIndex, @@ -42,7 +42,7 @@ impl Document { pub fn parse(params: DocumentParams) -> Self { let DocumentParams { uri, text, .. } = params; - let dir = uri.join(".").unwrap(); + let dir = uri.join(".").ok(); let path = if uri.scheme() == "file" { uri.to_file_path().ok() @@ -56,7 +56,10 @@ impl Document { Language::Tex => { let green = parser::parse_latex(&text, ¶ms.config.syntax); let mut semantics = semantics::tex::Semantics::default(); - semantics.process_root(&latex::SyntaxNode::new_root(green.clone())); + semantics.process_root( + ¶ms.config.syntax, + &latex::SyntaxNode::new_root(green.clone()), + ); DocumentData::Tex(TexDocumentData { green, semantics }) } Language::Bib => { @@ -86,6 +89,7 @@ impl Document { DocumentData::Latexmkrc(data) } Language::Tectonic => DocumentData::Tectonic, + Language::FileList => DocumentData::FileList(parser::parse_file_list(&text)), }; Self { @@ -143,6 +147,7 @@ pub enum DocumentData { Root, Latexmkrc(LatexmkrcData), Tectonic, + FileList(FileList), } impl DocumentData { @@ -185,6 +190,14 @@ impl DocumentData { None } } + + pub fn as_file_list(&self) -> Option<&FileList> { + if let DocumentData::FileList(data) = self { + Some(data) + } else { + None + } + } } #[derive(Debug, Clone)] diff --git a/support/texlab/crates/base-db/src/semantics/auxiliary.rs b/support/texlab/crates/base-db/src/semantics/auxiliary.rs index 8c9fe012e6..bb9a2c065c 100644 --- a/support/texlab/crates/base-db/src/semantics/auxiliary.rs +++ b/support/texlab/crates/base-db/src/semantics/auxiliary.rs @@ -5,6 +5,7 @@ use syntax::latex::{self, HasCurly}; #[derive(Debug, Clone, Default)] pub struct Semantics { pub label_numbers: FxHashMap<String, String>, + pub section_numbers: FxHashMap<String, String>, } impl Semantics { @@ -18,6 +19,9 @@ impl Semantics { if let Some(label_number) = latex::LabelNumber::cast(node.clone()) { self.process_label_number(&label_number); } + if let Some(toc_line) = latex::TocContentsLine::cast(node.clone()) { + self.process_toc_line(&toc_line); + } } fn process_label_number(&mut self, label_number: &latex::LabelNumber) -> Option<()> { @@ -40,4 +44,36 @@ impl Semantics { self.label_numbers.insert(name, text); Some(()) } + + fn process_toc_line(&mut self, toc_line: &latex::TocContentsLine) -> Option<()> { + let unit = toc_line.unit().and_then(|u| u.content_text())?.to_string(); + + if [ + "part", + "chapter", + "section", + "subsection", + "subsubsection", + "paragraph", + "subparagraph", + ] + .contains(&unit.as_str()) + { + let line = toc_line.line()?; + let name = line.syntax().children().find_map(|child| { + latex::Text::cast(child.clone())?; + Some(child) + })?; + let name = name.to_string(); + + let num_line = line + .syntax() + .children() + .find_map(|child| latex::TocNumberLine::cast(child.clone()))?; + let number = num_line.number()?; + let number = number.content_text()?.replace(['{', '}'], ""); + self.section_numbers.insert(name, number); + } + Some(()) + } } diff --git a/support/texlab/crates/base-db/src/semantics/tex.rs b/support/texlab/crates/base-db/src/semantics/tex.rs index 7e4c926f5d..7d150fc4b3 100644 --- a/support/texlab/crates/base-db/src/semantics/tex.rs +++ b/support/texlab/crates/base-db/src/semantics/tex.rs @@ -3,7 +3,28 @@ use rustc_hash::FxHashSet; use syntax::latex::{self, HasBrack, HasCurly}; use titlecase::titlecase; +use parser::SyntaxConfig; + use super::Span; +use crate::semantics::tex::latex::SyntaxToken; + +fn maybe_prepend_prefix( + map: &Vec<(String, String)>, + command: &Option<SyntaxToken>, + name: &Span, +) -> Span { + match command { + Some(x) => Span::new( + map.iter() + .find_map(|(k, v)| if k == &x.text()[1..] { Some(v) } else { None }) + .unwrap_or(&String::new()) + .to_owned() + + &name.text, + name.range, + ), + None => name.clone(), + } +} #[derive(Debug, Clone, Default)] pub struct Semantics { @@ -16,20 +37,26 @@ pub struct Semantics { pub graphics_paths: FxHashSet<String>, pub can_be_root: bool, pub can_be_compiled: bool, + pub diagnostic_suppressions: Vec<TextRange>, + pub bibitems: FxHashSet<Span>, } impl Semantics { - pub fn process_root(&mut self, root: &latex::SyntaxNode) { + pub fn process_root(&mut self, conf: &SyntaxConfig, root: &latex::SyntaxNode) { for node in root.descendants_with_tokens() { match node { latex::SyntaxElement::Node(node) => { - self.process_node(&node); + self.process_node(conf, &node); } - latex::SyntaxElement::Token(token) => { - if token.kind() == latex::COMMAND_NAME { - self.commands.push(Span::command(&token)); + latex::SyntaxElement::Token(token) => match token.kind() { + latex::COMMAND_NAME => { + self.commands.push(Span::command(&token)); } - } + latex::COMMENT if token.text().contains("texlab: ignore") => { + self.diagnostic_suppressions.push(token.text_range()); + } + _ => {} + }, }; } @@ -40,17 +67,17 @@ impl Semantics { .any(|link| link.kind == LinkKind::Cls && link.path.text == "subfiles"); } - fn process_node(&mut self, node: &latex::SyntaxNode) { + fn process_node(&mut self, conf: &SyntaxConfig, node: &latex::SyntaxNode) { if let Some(include) = latex::Include::cast(node.clone()) { self.process_include(include); } else if let Some(import) = latex::Import::cast(node.clone()) { self.process_import(import); } else if let Some(label) = latex::LabelDefinition::cast(node.clone()) { - self.process_label_definition(label); + self.process_label_definition(conf, label); } else if let Some(label) = latex::LabelReference::cast(node.clone()) { - self.process_label_reference(label); + self.process_label_reference(conf, label); } else if let Some(label) = latex::LabelReferenceRange::cast(node.clone()) { - self.process_label_reference_range(label); + self.process_label_reference_range(conf, label); } else if let Some(citation) = latex::Citation::cast(node.clone()) { self.process_citation(citation); } else if let Some(environment) = latex::Environment::cast(node.clone()) { @@ -59,6 +86,8 @@ impl Semantics { self.process_theorem_definition(theorem_def); } else if let Some(graphics_path) = latex::GraphicsPath::cast(node.clone()) { self.process_graphics_path(graphics_path); + } else if let Some(bibitem) = latex::BibItem::cast(node.clone()) { + self.process_bibitem(bibitem); } } @@ -111,7 +140,7 @@ impl Semantics { }); } - fn process_label_definition(&mut self, label: latex::LabelDefinition) { + fn process_label_definition(&mut self, conf: &SyntaxConfig, label: latex::LabelDefinition) { let Some(name) = label.name().and_then(|group| group.key()) else { return; }; @@ -190,13 +219,14 @@ impl Semantics { self.labels.push(Label { kind: LabelKind::Definition, - name, + cmd: label.command().map(|x| x.text()[1..].to_string()), + name: maybe_prepend_prefix(&conf.label_definition_prefixes, &label.command(), &name), targets: objects, full_range, }); } - fn process_label_reference(&mut self, label: latex::LabelReference) { + fn process_label_reference(&mut self, conf: &SyntaxConfig, label: latex::LabelReference) { let Some(name_list) = label.name_list() else { return; }; @@ -207,7 +237,12 @@ impl Semantics { if !name.text.contains('#') { self.labels.push(Label { kind: LabelKind::Reference, - name, + cmd: label.command().map(|x| x.text()[1..].to_string()), + name: maybe_prepend_prefix( + &conf.label_reference_prefixes, + &label.command(), + &name, + ), targets: Vec::new(), full_range, }); @@ -215,14 +250,23 @@ impl Semantics { } } - fn process_label_reference_range(&mut self, label: latex::LabelReferenceRange) { + fn process_label_reference_range( + &mut self, + conf: &SyntaxConfig, + label: latex::LabelReferenceRange, + ) { let full_range = latex::small_range(&label); if let Some(from) = label.from().and_then(|group| group.key()) { let name = Span::from(&from); if !name.text.contains('#') { self.labels.push(Label { kind: LabelKind::ReferenceRange, - name, + cmd: label.command().map(|x| x.text()[1..].to_string()), + name: maybe_prepend_prefix( + &conf.label_reference_prefixes, + &label.command(), + &name, + ), targets: Vec::new(), full_range, }); @@ -234,7 +278,12 @@ impl Semantics { if !name.text.contains('#') { self.labels.push(Label { kind: LabelKind::ReferenceRange, - name, + cmd: label.command().map(|x| x.text()[1..].to_string()), + name: maybe_prepend_prefix( + &conf.label_reference_prefixes, + &label.command(), + &name, + ), targets: Vec::new(), full_range, }); @@ -288,6 +337,16 @@ impl Semantics { self.graphics_paths.insert(path.to_string()); } } + + fn process_bibitem(&mut self, bibitem: latex::BibItem) { + if let Some(name) = bibitem.name() { + if let Some(key) = name.key() { + self.bibitems.insert( + Span::from(&key) + ); + } + } + } } #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)] @@ -336,6 +395,7 @@ pub enum LabelKind { #[derive(Debug, Clone)] pub struct Label { pub kind: LabelKind, + pub cmd: Option<String>, pub name: Span, pub targets: Vec<LabelTarget>, pub full_range: TextRange, diff --git a/support/texlab/crates/bibfmt/Cargo.toml b/support/texlab/crates/bibfmt/Cargo.toml index a712908d7d..c4931f87bb 100644 --- a/support/texlab/crates/bibfmt/Cargo.toml +++ b/support/texlab/crates/bibfmt/Cargo.toml @@ -7,7 +7,7 @@ edition.workspace = true rust-version.workspace = true [dependencies] -rowan = "0.15.15" +rowan.workspace = true syntax = { path = "../syntax" } line-index = { path = "../line-index" } @@ -15,5 +15,5 @@ line-index = { path = "../line-index" } doctest = false [dev-dependencies] -expect-test = "1.5.0" +expect-test.workspace = true parser = { path = "../parser" } diff --git a/support/texlab/crates/bibtex-utils/Cargo.toml b/support/texlab/crates/bibtex-utils/Cargo.toml index c20508107c..1271823eac 100644 --- a/support/texlab/crates/bibtex-utils/Cargo.toml +++ b/support/texlab/crates/bibtex-utils/Cargo.toml @@ -8,16 +8,16 @@ rust-version.workspace = true [dependencies] chrono = { version = "0.4.38", default-features = false, features = ["std"] } -human_name = "2.0.3" -itertools = "0.12.1" -rowan = "0.15.15" -rustc-hash = "1.1.0" +human_name = "2.0.4" +itertools.workspace = true +rowan.workspace = true +rustc-hash.workspace = true syntax = { path = "../syntax" } -unicode-normalization = "0.1.23" +unicode-normalization.workspace = true [lib] doctest = false [dev-dependencies] -expect-test = "1.5.0" +expect-test.workspace = true parser = { path = "../parser" } diff --git a/support/texlab/crates/citeproc/Cargo.toml b/support/texlab/crates/citeproc/Cargo.toml index 9e187f88e6..9ad0c82e56 100644 --- a/support/texlab/crates/citeproc/Cargo.toml +++ b/support/texlab/crates/citeproc/Cargo.toml @@ -10,17 +10,17 @@ rust-version.workspace = true bibtex-utils = { path = "../bibtex-utils" } base-db = { path = "../base-db" } isocountry = "0.3.2" -itertools = "0.12.1" -rowan = "0.15.15" -rustc-hash = "1.1.0" +itertools.workspace = true +rowan.workspace = true +rustc-hash.workspace = true syntax = { path = "../syntax" } -titlecase = "3.0.0" -unicode-normalization = "0.1.23" -url = "2.5.0" +titlecase.workspace = true +unicode-normalization.workspace = true +url.workspace = true [lib] doctest = false [dev-dependencies] -expect-test = "1.5.0" +expect-test.workspace = true parser = { path = "../parser" } diff --git a/support/texlab/crates/commands/Cargo.toml b/support/texlab/crates/commands/Cargo.toml index 095828fbc5..b348ad2b87 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.86" +anyhow.workspace = true base-db = { path = "../base-db" } -bstr = "1.9.1" -crossbeam-channel = "0.5.12" -itertools = "0.12.1" -libc = "0.2.154" -log = "0.4.21" -rowan = "0.15.15" -rustc-hash = "1.1.0" +bstr = "1.10.0" +crossbeam-channel.workspace = true +itertools.workspace = true +libc = "0.2.161" +log.workspace = true +rowan.workspace = true +rustc-hash.workspace = true syntax = { path = "../syntax" } -thiserror = "1.0.59" -url = "2.5.0" +thiserror = "1.0.64" +url.workspace = true [dev-dependencies] distro = { path = "../distro" } -expect-test = "1.5.0" +expect-test.workspace = true test-utils = { path = "../test-utils" } [lib] diff --git a/support/texlab/crates/commands/src/build.rs b/support/texlab/crates/commands/src/build.rs index 34d39f26e4..23a6e16b7b 100644 --- a/support/texlab/crates/commands/src/build.rs +++ b/support/texlab/crates/commands/src/build.rs @@ -47,6 +47,10 @@ impl BuildCommand { .next() .unwrap_or(document); + let Some(document_dir) = &document.dir else { + return Err(BuildError::NotLocal(document.uri.clone())); + }; + let Some(path) = document.path.as_deref().and_then(Path::to_str) else { return Err(BuildError::NotLocal(document.uri.clone())); }; @@ -55,7 +59,7 @@ impl BuildCommand { let program = config.program.clone(); let args = replace_placeholders(&config.args, &[('f', path)]); - let root = ProjectRoot::walk_and_find(workspace, &document.dir); + let root = ProjectRoot::walk_and_find(workspace, document_dir); let Ok(working_dir) = root.compile_dir.to_file_path() else { return Err(BuildError::NotLocal(document.uri.clone())); diff --git a/support/texlab/crates/commands/src/clean.rs b/support/texlab/crates/commands/src/clean.rs index d4ec6b24f4..e429d1afa2 100644 --- a/support/texlab/crates/commands/src/clean.rs +++ b/support/texlab/crates/commands/src/clean.rs @@ -21,7 +21,11 @@ impl CleanCommand { anyhow::bail!("document '{}' is not a local file", document.uri) }; - let root = ProjectRoot::walk_and_find(workspace, &document.dir); + let Some(document_dir) = &document.dir else { + anyhow::bail!("document '{}' is not a local file", document.uri) + }; + + let root = ProjectRoot::walk_and_find(workspace, document_dir); let flag = match target { CleanTarget::Auxiliary => "-c", diff --git a/support/texlab/crates/commands/src/dep_graph.rs b/support/texlab/crates/commands/src/dep_graph.rs index fe9ff3829c..86b49da0ac 100644 --- a/support/texlab/crates/commands/src/dep_graph.rs +++ b/support/texlab/crates/commands/src/dep_graph.rs @@ -48,6 +48,7 @@ pub fn show_dependency_graph(workspace: &Workspace) -> Result<String> { base_db::deps::EdgeData::DirectLink(data) => &data.link.path.text, base_db::deps::EdgeData::AdditionalFiles => "<project>", base_db::deps::EdgeData::Artifact => "<artifact>", + base_db::deps::EdgeData::FileList(_) => "<fls>", }; writeln!(&mut writer, "\t{source} -> {target} [label=\"{label}\"];")?; diff --git a/support/texlab/crates/commands/src/fwd_search.rs b/support/texlab/crates/commands/src/fwd_search.rs index f3480ef3e2..8b6227d8c2 100644 --- a/support/texlab/crates/commands/src/fwd_search.rs +++ b/support/texlab/crates/commands/src/fwd_search.rs @@ -84,7 +84,11 @@ impl ForwardSearch { } fn find_pdf(workspace: &Workspace, document: &Document) -> Result<PathBuf, ForwardSearchError> { - let root = ProjectRoot::walk_and_find(workspace, &document.dir); + let Some(document_dir) = &document.dir else { + return Err(ForwardSearchError::NotLocal(document.uri.clone())); + }; + + let root = ProjectRoot::walk_and_find(workspace, document_dir); log::debug!("[FwdSearch] root={root:#?}"); diff --git a/support/texlab/crates/completion-data/Cargo.toml b/support/texlab/crates/completion-data/Cargo.toml index 970cd6a578..c06a4994c8 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.30" -itertools = "0.12.1" -once_cell = "1.19.0" -rustc-hash = "1.1.0" -serde = { version = "1.0.202", features = ["derive"] } -serde_json = "1.0.117" +flate2 = "1.0.34" +itertools.workspace = true +once_cell.workspace = true +rustc-hash.workspace = true +serde.workspace = true +serde_json.workspace = true [lib] doctest = false diff --git a/support/texlab/crates/completion/Cargo.toml b/support/texlab/crates/completion/Cargo.toml index e28e04dfc7..e31f254e1c 100644 --- a/support/texlab/crates/completion/Cargo.toml +++ b/support/texlab/crates/completion/Cargo.toml @@ -12,16 +12,17 @@ completion-data = { path = "../completion-data" } fuzzy-matcher = { version = "0.3.7", features = ["compact"] } line-index = { path = "../line-index" } rayon = "1.10.0" -rowan = "0.15.15" -rustc-hash = "1.1.0" +rowan.workspace = true +rustc-hash.workspace = true syntax = { path = "../syntax" } -url = "2.5.0" +url.workspace = true [dev-dependencies] -criterion = "0.5.1" +criterion.workspace = true distro = { path = "../distro" } -expect-test = "1.5.0" +expect-test.workspace = true test-utils = { path = "../test-utils" } +parser = { path = "../parser" } [lib] doctest = false diff --git a/support/texlab/crates/completion/src/lib.rs b/support/texlab/crates/completion/src/lib.rs index b1050052f0..5bfd94d944 100644 --- a/support/texlab/crates/completion/src/lib.rs +++ b/support/texlab/crates/completion/src/lib.rs @@ -72,7 +72,7 @@ impl<'a> CompletionItemData<'a> { Self::Citation(data) => &data.entry.name.text, Self::Environment(data) => data.name, Self::GlossaryEntry(data) => &data.name, - Self::Label(data) => data.name, + Self::Label(data) => &data.name, Self::Color(name) => name, Self::ColorModel(name) => name, Self::File(name) => name, diff --git a/support/texlab/crates/completion/src/providers/include.rs b/support/texlab/crates/completion/src/providers/include.rs index d0a042252f..f3deb3a108 100644 --- a/support/texlab/crates/completion/src/providers/include.rs +++ b/support/texlab/crates/completion/src/providers/include.rs @@ -118,7 +118,7 @@ fn current_dir( .next() .map_or(params.document, Clone::clone); - let root = ProjectRoot::walk_and_find(workspace, &parent.dir); + let root = ProjectRoot::walk_and_find(workspace, parent.dir.as_ref()?); let mut path = PathBuf::new(); if let Some(graphics_path) = graphics_path { diff --git a/support/texlab/crates/completion/src/providers/label_ref.rs b/support/texlab/crates/completion/src/providers/label_ref.rs index 6f71ae3241..8b262f13ca 100644 --- a/support/texlab/crates/completion/src/providers/label_ref.rs +++ b/support/texlab/crates/completion/src/providers/label_ref.rs @@ -11,12 +11,30 @@ use crate::{ CompletionItem, CompletionItemData, CompletionParams, }; +fn trim_prefix<'a>(prefix: Option<&'a str>, text: &'a str) -> &'a str { + prefix + .and_then(|pref| text.strip_prefix(pref)) + .unwrap_or(text) +} + pub fn complete_label_references<'a>( params: &'a CompletionParams<'a>, builder: &mut CompletionBuilder<'a>, ) -> Option<()> { - let FindResult { cursor, is_math } = - find_reference(params).or_else(|| find_reference_range(params))?; + let FindResult { + cursor, + is_math, + command, + } = find_reference(params).or_else(|| find_reference_range(params))?; + let ref_pref = params + .feature + .workspace + .config() + .syntax + .label_reference_prefixes + .iter() + .find_map(|(k, v)| if *k == command { Some(v) } else { None }) + .map(|x| x.as_str()); for document in ¶ms.feature.project.documents { let DocumentData::Tex(data) = &document.data else { @@ -29,6 +47,10 @@ pub fn complete_label_references<'a>( .iter() .filter(|label| label.kind == LabelKind::Definition) { + if ref_pref.map_or(false, |pref| !label.name.text.starts_with(pref)) { + continue; + } + let labeltext = trim_prefix(ref_pref, &label.name.text); match render_label(params.feature.workspace, ¶ms.feature.project, label) { Some(rendered_label) => { if is_math && !matches!(rendered_label.object, RenderedObject::Equation) { @@ -41,11 +63,12 @@ pub fn complete_label_references<'a>( _ => None, }; - let keywords = format!("{} {}", label.name.text, rendered_label.reference()); + let keywords = format!("{} {}", labeltext, rendered_label.reference()); if let Some(score) = builder.matcher.score(&keywords, &cursor.text) { + let name = trim_prefix(ref_pref, &label.name.text); let data = CompletionItemData::Label(crate::LabelData { - name: &label.name.text, + name, header, footer, object: Some(rendered_label.object), @@ -59,12 +82,13 @@ pub fn complete_label_references<'a>( } None => { if let Some(score) = builder.matcher.score(&label.name.text, &cursor.text) { + let name = trim_prefix(ref_pref, &label.name.text); let data = CompletionItemData::Label(crate::LabelData { - name: &label.name.text, + name, header: None, footer: None, object: None, - keywords: label.name.text.clone(), + keywords: labeltext.to_string(), }); builder @@ -82,20 +106,26 @@ pub fn complete_label_references<'a>( struct FindResult { cursor: Span, is_math: bool, + command: String, } fn find_reference(params: &CompletionParams) -> Option<FindResult> { let (cursor, group) = find_curly_group_word_list(params)?; let reference = latex::LabelReference::cast(group.syntax().parent()?)?; let is_math = reference.command()?.text() == "\\eqref"; - Some(FindResult { cursor, is_math }) + Some(FindResult { + cursor, + is_math, + command: reference.command()?.text()[1..].to_string(), + }) } fn find_reference_range(params: &CompletionParams) -> Option<FindResult> { let (cursor, group) = find_curly_group_word(params)?; - latex::LabelReferenceRange::cast(group.syntax().parent()?)?; + let refrange = latex::LabelReferenceRange::cast(group.syntax().parent()?)?; Some(FindResult { cursor, is_math: false, + command: refrange.command()?.text()[1..].to_string(), }) } diff --git a/support/texlab/crates/completion/src/tests.rs b/support/texlab/crates/completion/src/tests.rs index c6bed6e08e..8c68b71dd9 100644 --- a/support/texlab/crates/completion/src/tests.rs +++ b/support/texlab/crates/completion/src/tests.rs @@ -1,11 +1,17 @@ -use base_db::FeatureParams; +use base_db::{Config, FeatureParams}; use expect_test::{expect, Expect}; +use parser::SyntaxConfig; use rowan::TextRange; use crate::CompletionParams; -fn check(input: &str, expect: Expect) { - let fixture = test_utils::fixture::Fixture::parse(input); +fn check_with_syntax_config(config: SyntaxConfig, input: &str, expect: Expect) { + let mut fixture = test_utils::fixture::Fixture::parse(input); + fixture.workspace.set_config(Config { + syntax: config, + ..Config::default() + }); + let fixture = fixture; let (offset, spec) = fixture .documents @@ -37,6 +43,10 @@ fn check(input: &str, expect: Expect) { expect.assert_debug_eq(&items); } +fn check(input: &str, expect: Expect) { + check_with_syntax_config(SyntaxConfig::default(), input, expect) +} + #[test] fn acronym_ref_simple() { check( @@ -2134,3 +2144,200 @@ fn issue_885() { "#]], ); } + +#[test] +fn test_custom_label_prefix_ref() { + let mut config = SyntaxConfig::default(); + config.label_definition_commands.insert("asm".to_string()); + config + .label_definition_prefixes + .push(("asm".to_string(), "asm:".to_string())); + + check_with_syntax_config( + config, + r#" +%! main.tex + \begin{enumerate}\label{baz} + \asm{foo}{what} + \end{enumerate} + + \ref{} + | +% Comment"#, + expect![[r#" + [ + Label( + LabelData { + name: "asm:foo", + header: None, + footer: None, + object: None, + keywords: "asm:foo", + }, + ), + Label( + LabelData { + name: "baz", + header: None, + footer: None, + object: None, + keywords: "baz", + }, + ), + ] + "#]], + ); +} + +#[test] +fn test_custom_label_prefix_custom_ref() { + let mut config = SyntaxConfig::default(); + config.label_definition_commands.insert("asm".to_string()); + config + .label_definition_prefixes + .push(("asm".to_string(), "asm:".to_string())); + config.label_reference_commands.insert("asmref".to_string()); + config + .label_reference_prefixes + .push(("asmref".to_string(), "asm:".to_string())); + + check_with_syntax_config( + config, + r#" +%! main.tex + \begin{enumerate}\label{baz} + \asm{foo}{what} + \end{enumerate} + + \asmref{} + | +% Comment"#, + expect![[r#" + [ + Label( + LabelData { + name: "foo", + header: None, + footer: None, + object: None, + keywords: "foo", + }, + ), + ] + "#]], + ); +} + +#[test] +fn test_custom_label_multiple_prefix_custom_ref() { + let mut config = SyntaxConfig::default(); + config + .label_definition_commands + .extend(vec!["asm", "goal"].into_iter().map(String::from)); + config.label_definition_prefixes.extend( + vec![("asm", "asm:"), ("goal", "goal:")] + .into_iter() + .map(|(x, y)| (String::from(x), String::from(y))), + ); + config + .label_reference_commands + .extend(vec!["asmref", "goalref"].into_iter().map(String::from)); + config.label_reference_prefixes.extend( + vec![("asmref", "asm:"), ("goalref", "goal:")] + .into_iter() + .map(|(x, y)| (String::from(x), String::from(y))), + ); + + check_with_syntax_config( + config, + r#" +%! main.tex + \begin{enumerate}\label{baz} + \asm{foo}{what} + \goal{foo}{what} + \end{enumerate} + + \goalref{} + | +% Comment"#, + expect![[r#" + [ + Label( + LabelData { + name: "foo", + header: None, + footer: None, + object: None, + keywords: "foo", + }, + ), + ] + "#]], + ); +} + +#[test] +fn test_custom_label_multiple_prefix_ref() { + let mut config = SyntaxConfig::default(); + config + .label_definition_commands + .extend(vec!["asm", "goal"].into_iter().map(String::from)); + config.label_definition_prefixes.extend( + vec![("asm", "asm:"), ("goal", "goal:")] + .into_iter() + .map(|(x, y)| (String::from(x), String::from(y))), + ); + config + .label_reference_commands + .extend(vec!["asmref", "goalref"].into_iter().map(String::from)); + config.label_reference_prefixes.extend( + vec![("asmref", "asm:"), ("goalref", "goal:")] + .into_iter() + .map(|(x, y)| (String::from(x), String::from(y))), + ); + + check_with_syntax_config( + config, + r#" +%! main.tex + \begin{enumerate}\label{baz} + \asm{foo}{what} + \goal{foo}{what} + \end{enumerate} + + \ref{} + | +% Comment"#, + expect![[r#" + [ + Label( + LabelData { + name: "asm:foo", + header: None, + footer: None, + object: None, + keywords: "asm:foo", + }, + ), + Label( + LabelData { + name: "baz", + header: None, + footer: None, + object: None, + keywords: "baz", + }, + ), + Label( + LabelData { + name: "goal:foo", + header: None, + footer: None, + object: None, + keywords: "goal:foo", + }, + ), + ] + "#]], + ); +} diff --git a/support/texlab/crates/definition/Cargo.toml b/support/texlab/crates/definition/Cargo.toml index 63ba15c308..20bf3ed5fc 100644 --- a/support/texlab/crates/definition/Cargo.toml +++ b/support/texlab/crates/definition/Cargo.toml @@ -8,12 +8,12 @@ rust-version.workspace = true [dependencies] base-db = { path = "../base-db" } -rowan = "0.15.15" -rustc-hash = "1.1.0" +rowan.workspace = true +rustc-hash.workspace = true syntax = { path = "../syntax" } [dev-dependencies] -itertools = "0.12.1" +itertools.workspace = true 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 ea7ef8c2c2..4892dad257 100644 --- a/support/texlab/crates/definition/src/command.rs +++ b/support/texlab/crates/definition/src/command.rs @@ -27,7 +27,7 @@ pub(super) fn goto_definition(context: &mut DefinitionContext) -> Option<()> { .filter_map(|node| { process_old_definition(node.clone()).or_else(|| process_new_definition(node)) }) - .filter(|(_, command)| command.text() == name.text()) + .filter(|(_, command)| command.text() == name.text().trim_end_matches("*")) .map(|(target_range, command)| DefinitionResult { origin_selection_range, target: document, diff --git a/support/texlab/crates/diagnostics/Cargo.toml b/support/texlab/crates/diagnostics/Cargo.toml index 1cb09fdec9..51bf19c0a3 100644 --- a/support/texlab/crates/diagnostics/Cargo.toml +++ b/support/texlab/crates/diagnostics/Cargo.toml @@ -10,19 +10,19 @@ rust-version.workspace = true base-db = { path = "../base-db" } encoding_rs = "0.8.34" encoding_rs_io = "0.1.7" -itertools = "0.12.1" +itertools.workspace = true line-index = { path = "../line-index" } -log = "0.4.21" +log.workspace = true multimap = "0.10.0" -once_cell = "1.19.0" -regex = "1.10.4" -rowan = "0.15.15" -rustc-hash = "1.1.0" +once_cell.workspace = true +regex.workspace = true +rowan.workspace = true +rustc-hash.workspace = true syntax = { path = "../syntax" } -url = "2.5.0" +url.workspace = true [dev-dependencies] -expect-test = "1.5.0" +expect-test.workspace = true test-utils = { path = "../test-utils" } [lib] diff --git a/support/texlab/crates/diagnostics/src/chktex.rs b/support/texlab/crates/diagnostics/src/chktex.rs index a3f539440d..b7cd80d67b 100644 --- a/support/texlab/crates/diagnostics/src/chktex.rs +++ b/support/texlab/crates/diagnostics/src/chktex.rs @@ -36,7 +36,7 @@ impl Command { return None; } - let root = ProjectRoot::walk_and_find(workspace, &parent.dir); + let root = ProjectRoot::walk_and_find(workspace, parent.dir.as_ref()?); let working_dir = root.src_dir.to_file_path().ok()?; log::debug!("Calling ChkTeX from directory: {}", working_dir.display()); diff --git a/support/texlab/crates/diagnostics/src/citations.rs b/support/texlab/crates/diagnostics/src/citations.rs index 43d5f495d2..2678d7e456 100644 --- a/support/texlab/crates/diagnostics/src/citations.rs +++ b/support/texlab/crates/diagnostics/src/citations.rs @@ -18,13 +18,17 @@ pub fn detect_undefined_citations<'a>( ) -> Option<()> { let data = document.data.as_tex()?; + let bibitems: FxHashSet<&str> = data.semantics.bibitems.iter() + .map(|bib| bib.text.as_str()) + .collect(); + let entries: FxHashSet<&str> = Entry::find_all(project) .map(|(_, entry)| entry.name_text()) .collect(); for citation in &data.semantics.citations { let name = citation.name_text(); - if name != "*" && !entries.contains(name) { + if name != "*" && !entries.contains(name) && !bibitems.contains(name) { let diagnostic = Diagnostic::Tex(citation.name.range, TexError::UndefinedCitation); results .entry(document.uri.clone()) @@ -41,24 +45,38 @@ pub fn detect_unused_entries<'a>( document: &'a Document, results: &mut FxHashMap<Url, Vec<Diagnostic>>, ) -> Option<()> { - let data = document.data.as_bib()?; - - // If this is a huge bibliography, then don't bother checking for unused entries. - if data.semantics.entries.len() > MAX_UNUSED_ENTRIES { - return None; - } let citations: FxHashSet<&str> = Citation::find_all(project) .map(|(_, citation)| citation.name_text()) .collect(); - for entry in &data.semantics.entries { - if !citations.contains(entry.name.text.as_str()) { - let diagnostic = Diagnostic::Bib(entry.name.range, BibError::UnusedEntry); - results - .entry(document.uri.clone()) - .or_default() - .push(diagnostic); + if let Some(data) = document.data.as_bib() + { + // If this is a huge bibliography, then don't bother checking for unused entries. + if data.semantics.entries.len() > MAX_UNUSED_ENTRIES { + return None; + } + + for entry in &data.semantics.entries { + if !citations.contains(entry.name.text.as_str()) { + let diagnostic = Diagnostic::Bib(entry.name.range, BibError::UnusedEntry); + results + .entry(document.uri.clone()) + .or_default() + .push(diagnostic); + } + } + }; + + if let Some(tex_data) = document.data.as_tex(){ + for bibitem in &tex_data.semantics.bibitems { + if !citations.contains(bibitem.text.as_str()) { + let diagnostic = Diagnostic::Bib(bibitem.range, BibError::UnusedEntry); + results + .entry(document.uri.clone()) + .or_default() + .push(diagnostic); + } } } diff --git a/support/texlab/crates/diagnostics/src/manager.rs b/support/texlab/crates/diagnostics/src/manager.rs index 500dc99c13..2829286a17 100644 --- a/support/texlab/crates/diagnostics/src/manager.rs +++ b/support/texlab/crates/diagnostics/src/manager.rs @@ -1,5 +1,8 @@ -use base_db::{deps::Project, util::filter_regex_patterns, Document, Owner, Workspace}; +use base_db::{ + deps::Project, util::filter_regex_patterns, Document, DocumentData, Owner, Workspace, +}; use multimap::MultiMap; +use rowan::TextRange; use rustc_hash::{FxHashMap, FxHashSet}; use url::Url; @@ -16,7 +19,7 @@ pub struct Manager { impl Manager { /// Updates the syntax-based diagnostics for the given document. pub fn update_syntax(&mut self, workspace: &Workspace, document: &Document) { - if !Self::is_relevant(document) { + if !Self::is_relevant_document(document) { return; } @@ -76,7 +79,7 @@ impl Manager { for document in workspace .iter() - .filter(|document| Self::is_relevant(document)) + .filter(|document| Self::is_relevant_document(document)) { let project = Project::from_child(workspace, document); super::citations::detect_undefined_citations(&project, document, &mut results); @@ -87,28 +90,78 @@ impl Manager { super::labels::detect_duplicate_labels(workspace, &mut results); super::labels::detect_undefined_and_unused_labels(workspace, &mut results); - let config = &workspace.config().diagnostics; - - results.retain(|uri, _| workspace.lookup(uri).map_or(false, Self::is_relevant)); + results.retain(|uri, _| { + workspace + .lookup(uri) + .map_or(false, Self::is_relevant_document) + }); - for diagnostics in results.values_mut() { - diagnostics.retain(|diagnostic| { - filter_regex_patterns( - diagnostic.message(), - &config.allowed_patterns, - &config.ignored_patterns, - ) - }); + for (uri, diagnostics) in results.iter_mut() { + diagnostics + .retain_mut(|diagnostic| Self::filter_diagnostic(workspace, uri, diagnostic)); } results } - fn is_relevant(document: &Document) -> bool { + fn is_relevant_document(document: &Document) -> bool { match document.owner { Owner::Client => true, Owner::Server => true, Owner::Distro => false, } } + + fn filter_diagnostic(workspace: &Workspace, uri: &Url, diagnostic: &mut Diagnostic) -> bool { + let config = &workspace.config().diagnostics; + + if !filter_regex_patterns( + diagnostic.message(), + &config.allowed_patterns, + &config.ignored_patterns, + ) { + return false; + } + + let Some(document) = workspace.lookup(uri) else { + return false; + }; + + let Some(primary_range) = diagnostic.range(&document.line_index) else { + return false; + }; + + if Self::is_ignored(workspace, &document.uri, &primary_range) { + return false; + } + + let Some(additional_locations) = diagnostic.additional_locations_mut() else { + return true; + }; + + additional_locations.retain(|(uri, range)| !Self::is_ignored(workspace, uri, range)); + if additional_locations.is_empty() { + return false; + } + + true + } + + fn is_ignored(workspace: &Workspace, uri: &Url, diag_range: &TextRange) -> bool { + let Some(document) = workspace.lookup(uri) else { + return false; + }; + + let DocumentData::Tex(data) = &document.data else { + return false; + }; + + let diag_line_col = document.line_index.line_col(diag_range.start()); + + data.semantics + .diagnostic_suppressions + .iter() + .map(|r| document.line_index.line_col(r.start())) + .any(|r| r.line == diag_line_col.line || r.line + 1 == diag_line_col.line) + } } diff --git a/support/texlab/crates/diagnostics/src/tests.rs b/support/texlab/crates/diagnostics/src/tests.rs index 05ad67fc8b..ff5f53ec3e 100644 --- a/support/texlab/crates/diagnostics/src/tests.rs +++ b/support/texlab/crates/diagnostics/src/tests.rs @@ -284,6 +284,33 @@ fn test_citation_undefined() { } #[test] +fn test_citation_undefined_ignore() { + check( + r#" +%! main.tex +% texlab: ignore +\cite{foo} +"#, + expect![[r#" + [] + "#]], + ) +} + +#[test] +fn test_citation_undefined_ignore_single_line() { + check( + r#" +%! main.tex +\cite{foo} % texlab: ignore +"#, + expect![[r#" + [] + "#]], + ) +} + +#[test] fn test_citation_unused() { check( r#" @@ -306,3 +333,46 @@ fn test_citation_unused() { "#]], ) } + +#[test] +fn test_label_duplicate() { + check( + r#" +%! main.tex +\label{foo} + ^^^ +\label{foo} + ^^^ +\label{foo} % texlab: ignore + +\ref{foo} +"#, + expect![[r#" + [ + ( + "file:///texlab/main.tex", + [ + Tex( + 7..10, + DuplicateLabel( + ( + "file:///texlab/main.tex", + 19..22, + ), + ), + ), + Tex( + 19..22, + DuplicateLabel( + ( + "file:///texlab/main.tex", + 7..10, + ), + ), + ), + ], + ), + ] + "#]], + ) +} diff --git a/support/texlab/crates/diagnostics/src/types.rs b/support/texlab/crates/diagnostics/src/types.rs index 9cbbf8936f..9cc195863b 100644 --- a/support/texlab/crates/diagnostics/src/types.rs +++ b/support/texlab/crates/diagnostics/src/types.rs @@ -1,9 +1,9 @@ -use line_index::LineCol; +use line_index::{LineCol, LineIndex}; use rowan::TextRange; use syntax::BuildError; use url::Url; -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(PartialEq, Eq, Clone)] pub enum TexError { UnexpectedRCurly, ExpectingRCurly, @@ -14,7 +14,28 @@ pub enum TexError { DuplicateLabel(Vec<(Url, TextRange)>), } -#[derive(Debug, PartialEq, Eq, Clone)] +impl std::fmt::Debug for TexError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::UnexpectedRCurly => write!(f, "UnexpectedRCurly"), + Self::ExpectingRCurly => write!(f, "ExpectingRCurly"), + Self::MismatchedEnvironment => write!(f, "MismatchedEnvironment"), + Self::UnusedLabel => write!(f, "UnusedLabel"), + Self::UndefinedLabel => write!(f, "UndefinedLabel"), + Self::UndefinedCitation => write!(f, "UndefinedCitation"), + Self::DuplicateLabel(locations) => { + let mut t = f.debug_tuple("DuplicateLabel"); + for (uri, range) in locations { + t.field(&(uri.as_str(), range)); + } + + t.finish() + } + } + } +} + +#[derive(PartialEq, Eq, Clone)] pub enum BibError { ExpectingLCurly, ExpectingKey, @@ -25,6 +46,27 @@ pub enum BibError { DuplicateEntry(Vec<(Url, TextRange)>), } +impl std::fmt::Debug for BibError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::ExpectingLCurly => write!(f, "ExpectingLCurly"), + Self::ExpectingKey => write!(f, "ExpectingKey"), + Self::ExpectingRCurly => write!(f, "ExpectingRCurly"), + Self::ExpectingEq => write!(f, "ExpectingEq"), + Self::ExpectingFieldValue => write!(f, "ExpectingFieldValue"), + Self::UnusedEntry => write!(f, "UnusedEntry"), + Self::DuplicateEntry(locations) => { + let mut t = f.debug_tuple("DuplicateEntry"); + for (uri, range) in locations { + t.field(&(uri.as_str(), range)); + } + + t.finish() + } + } + } +} + #[derive(Debug, PartialEq, Eq, Clone)] pub struct ChktexError { pub start: LineCol, @@ -74,4 +116,42 @@ impl Diagnostic { Diagnostic::Chktex(error) => &error.message, } } + + pub fn range(&self, line_index: &LineIndex) -> Option<TextRange> { + Some(match self { + Diagnostic::Tex(range, _) => *range, + Diagnostic::Bib(range, _) => *range, + Diagnostic::Build(range, _) => *range, + Diagnostic::Chktex(error) => { + let start = line_index.offset(error.start)?; + let end = line_index.offset(error.end)?; + TextRange::new(start, end) + } + }) + } + + pub fn additional_locations_mut(&mut self) -> Option<&mut Vec<(Url, TextRange)>> { + match self { + Diagnostic::Tex(_, err) => match err { + TexError::UnexpectedRCurly + | TexError::ExpectingRCurly + | TexError::MismatchedEnvironment + | TexError::UnusedLabel + | TexError::UndefinedLabel + | TexError::UndefinedCitation => None, + TexError::DuplicateLabel(locations) => Some(locations), + }, + Diagnostic::Bib(_, err) => match err { + BibError::ExpectingLCurly + | BibError::ExpectingKey + | BibError::ExpectingRCurly + | BibError::ExpectingEq + | BibError::ExpectingFieldValue + | BibError::UnusedEntry => None, + BibError::DuplicateEntry(locations) => Some(locations), + }, + Diagnostic::Chktex(_) => None, + Diagnostic::Build(_, _) => None, + } + } } diff --git a/support/texlab/crates/distro/Cargo.toml b/support/texlab/crates/distro/Cargo.toml index dea6a56979..de89de935f 100644 --- a/support/texlab/crates/distro/Cargo.toml +++ b/support/texlab/crates/distro/Cargo.toml @@ -7,8 +7,8 @@ edition.workspace = true rust-version.workspace = true [dependencies] -anyhow = "1.0.86" -rustc-hash = "1.1.0" +anyhow.workspace = true +rustc-hash.workspace = true [lib] doctest = false diff --git a/support/texlab/crates/distro/src/language.rs b/support/texlab/crates/distro/src/language.rs index 201ed0702b..0a94a93604 100644 --- a/support/texlab/crates/distro/src/language.rs +++ b/support/texlab/crates/distro/src/language.rs @@ -9,6 +9,7 @@ pub enum Language { Root, Latexmkrc, Tectonic, + FileList, } impl Language { @@ -32,6 +33,7 @@ impl Language { "bib" | "bibtex" => Some(Self::Bib), "aux" => Some(Self::Aux), "log" => Some(Self::Log), + "fls" => Some(Self::FileList), _ => None, } } diff --git a/support/texlab/crates/distro/src/lib.rs b/support/texlab/crates/distro/src/lib.rs index 5c0dbe51fb..cf1420baa9 100644 --- a/support/texlab/crates/distro/src/lib.rs +++ b/support/texlab/crates/distro/src/lib.rs @@ -4,7 +4,10 @@ mod language; mod miktex; mod texlive; -use std::process::{Command, Stdio}; +use std::{ + env, + process::{Command, Stdio}, +}; use anyhow::Result; @@ -70,8 +73,14 @@ impl Distro { DistroKind::Tectonic | DistroKind::Unknown => FileNameDB::default(), }; - if let Some(bibinputs) = std::env::var_os("BIBINPUTS") { - for dir in std::env::split_paths(&bibinputs) { + Self::read_env_dir(&mut file_name_db, "TEXINPUTS"); + Self::read_env_dir(&mut file_name_db, "BIBINPUTS"); + Ok(Self { kind, file_name_db }) + } + + fn read_env_dir(file_name_db: &mut FileNameDB, env_var: &str) { + if let Some(paths) = env::var_os(env_var) { + for dir in env::split_paths(&paths) { if let Ok(entries) = std::fs::read_dir(dir) { for file in entries .flatten() @@ -83,7 +92,5 @@ impl Distro { } } } - - Ok(Self { kind, file_name_db }) } } diff --git a/support/texlab/crates/folding/Cargo.toml b/support/texlab/crates/folding/Cargo.toml index 684ddeb1b9..862f2301d1 100644 --- a/support/texlab/crates/folding/Cargo.toml +++ b/support/texlab/crates/folding/Cargo.toml @@ -8,11 +8,11 @@ rust-version.workspace = true [dependencies] base-db = { path = "../base-db" } -rowan = "0.15.15" +rowan.workspace = true syntax = { path = "../syntax" } [dev-dependencies] -expect-test = "1.5.0" +expect-test.workspace = true test-utils = { path = "../test-utils" } [lib] diff --git a/support/texlab/crates/highlights/Cargo.toml b/support/texlab/crates/highlights/Cargo.toml index 785c282e3a..d509c7f89f 100644 --- a/support/texlab/crates/highlights/Cargo.toml +++ b/support/texlab/crates/highlights/Cargo.toml @@ -8,10 +8,10 @@ rust-version.workspace = true [dependencies] base-db = { path = "../base-db" } -rowan = "0.15.15" +rowan.workspace = true [dev-dependencies] -expect-test = "1.5.0" +expect-test.workspace = true test-utils = { path = "../test-utils" } [lib] diff --git a/support/texlab/crates/hover/Cargo.toml b/support/texlab/crates/hover/Cargo.toml index 946ed734fe..683da15e43 100644 --- a/support/texlab/crates/hover/Cargo.toml +++ b/support/texlab/crates/hover/Cargo.toml @@ -11,11 +11,11 @@ base-db = { path = "../base-db" } bibtex-utils = { path = "../bibtex-utils" } citeproc = { path = "../citeproc" } completion-data = { path = "../completion-data" } -rowan = "0.15.15" +rowan.workspace = true syntax = { path = "../syntax" } [dev-dependencies] -expect-test = "1.5.0" +expect-test.workspace = true 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 ede4ca6188..07e0172de2 100644 --- a/support/texlab/crates/inlay-hints/Cargo.toml +++ b/support/texlab/crates/inlay-hints/Cargo.toml @@ -8,12 +8,12 @@ rust-version.workspace = true [dependencies] base-db = { path = "../base-db" } -rowan = "0.15.15" -rustc-hash = "1.1.0" +rowan.workspace = true +rustc-hash.workspace = true [dev-dependencies] test-utils = { path = "../test-utils" } -expect-test = "1.5.0" +expect-test.workspace = true [lib] doctest = false diff --git a/support/texlab/crates/ipc/Cargo.toml b/support/texlab/crates/ipc/Cargo.toml index 1e1ff7a822..c00b98ff12 100644 --- a/support/texlab/crates/ipc/Cargo.toml +++ b/support/texlab/crates/ipc/Cargo.toml @@ -7,15 +7,15 @@ edition.workspace = true rust-version.workspace = true [dependencies] -crossbeam-channel = "0.5.12" -serde = "1.0.202" -serde_json = "1.0.117" -log = "0.4.21" +crossbeam-channel.workspace = true +serde.workspace = true +serde_json.workspace = true +log.workspace = true uds_windows = "1.1.0" [lib] doctest = false [dev-dependencies] -expect-test = "1.5.0" +expect-test.workspace = true parser = { path = "../parser" } diff --git a/support/texlab/crates/line-index/Cargo.toml b/support/texlab/crates/line-index/Cargo.toml index 73d2766d60..3b385ca2bf 100644 --- a/support/texlab/crates/line-index/Cargo.toml +++ b/support/texlab/crates/line-index/Cargo.toml @@ -7,7 +7,7 @@ edition.workspace = true rust-version.workspace = true [dependencies] -rustc-hash = "1.1.0" +rustc-hash.workspace = true text-size = "1.1.1" [lib] diff --git a/support/texlab/crates/links/Cargo.toml b/support/texlab/crates/links/Cargo.toml index 74378483a9..f36edf1e20 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.5.0" +expect-test.workspace = true [lib] doctest = false diff --git a/support/texlab/crates/parser/Cargo.toml b/support/texlab/crates/parser/Cargo.toml index 39fa34ef63..e1edb782c5 100644 --- a/support/texlab/crates/parser/Cargo.toml +++ b/support/texlab/crates/parser/Cargo.toml @@ -7,19 +7,19 @@ edition.workspace = true rust-version.workspace = true [dependencies] -log = "0.4.21" -logos = "0.14.0" -once_cell = "1.19.0" -pathdiff = "0.2.1" -regex = "1.10.4" -rowan = "0.15.15" -rustc-hash = "1.1.0" +log.workspace = true +logos = "0.14.2" +once_cell.workspace = true +pathdiff = "0.2.2" +regex.workspace = true +rowan.workspace = true +rustc-hash.workspace = true syntax = { path = "../syntax" } -tempfile = "3.10.1" -versions = "6.2.0" +tempfile.workspace = true +versions = "6.3.2" [dev-dependencies] -expect-test = "1.5.0" +expect-test.workspace = true [lib] doctest = false diff --git a/support/texlab/crates/parser/src/bibtex.rs b/support/texlab/crates/parser/src/bibtex.rs index e2cde27dec..4d1e561dbe 100644 --- a/support/texlab/crates/parser/src/bibtex.rs +++ b/support/texlab/crates/parser/src/bibtex.rs @@ -2,6 +2,8 @@ use logos::Logos; use rowan::{GreenNode, GreenNodeBuilder}; use syntax::bibtex::SyntaxKind::{self, *}; +use crate::util::lex_command_name; + pub fn parse_bibtex(input: &str) -> GreenNode { let mut ptr = TokenPtr { builder: GreenNodeBuilder::new(), @@ -135,8 +137,8 @@ fn curly_group(mut ptr: TokenPtr<ContentToken>) -> TokenPtr<ContentToken> { | ContentToken::Quote | ContentToken::Word => ptr.bump(), ContentToken::LCurly => ptr = curly_group(ptr), - ContentToken::AccentName => ptr = accent(ptr), - ContentToken::CommandName => ptr = command(ptr), + ContentToken::CommandName(CommandName::Accent) => ptr = accent(ptr), + ContentToken::CommandName(CommandName::Generic) => ptr = command(ptr), }; } @@ -160,8 +162,8 @@ fn quote_group(mut ptr: TokenPtr<ContentToken>) -> TokenPtr<ContentToken> { | ContentToken::Integer | ContentToken::Word => ptr.bump(), ContentToken::LCurly => ptr = curly_group(ptr), - ContentToken::AccentName => ptr = accent(ptr), - ContentToken::CommandName => ptr = command(ptr), + ContentToken::CommandName(CommandName::Accent) => ptr = accent(ptr), + ContentToken::CommandName(CommandName::Generic) => ptr = command(ptr), }; } @@ -181,7 +183,7 @@ fn accent(mut ptr: TokenPtr<ContentToken>) -> TokenPtr<ContentToken> { ptr.expect(ContentToken::Whitespace); } - if ptr.at(ContentToken::Word) || ptr.at(ContentToken::CommandName) { + if ptr.at(ContentToken::Word) || ptr.at(ContentToken::CommandName(CommandName::Generic)) { ptr.bump(); } @@ -355,31 +357,29 @@ enum ContentToken { #[token(r#"~"#)] Nbsp, - #[token(r#"\`"#)] - #[token(r#"\'"#)] - #[token(r#"\^"#)] - #[token(r#"\""#)] - #[token(r#"\H"#)] - #[token(r#"\~"#)] - #[token(r#"\c"#)] - #[token(r#"\k"#)] - #[token(r#"\="#)] - #[token(r#"\b"#)] - #[token(r#"\."#)] - #[token(r#"\d"#)] - #[token(r#"\r"#)] - #[token(r#"\u"#)] - #[token(r#"\v"#)] - #[token(r#"\t"#)] - AccentName, - - #[regex(r"\\([^\r\n]|[@a-zA-Z:_]+\*?)?")] - CommandName, + #[regex(r"\\", |lexer| { CommandName::from(lex_command_name(lexer)) })] + CommandName(CommandName), #[regex(r#"[^\s"\{\}\\~,]+"#)] Word, } +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)] +enum CommandName { + Generic, + Accent, +} + +impl From<&str> for CommandName { + fn from(value: &str) -> Self { + match value { + "`" | "'" | "^" | "\"" | "H" | "~" | "c" | "k" | "=" | "b" | "." | "d" | "r" | "u" + | "v" | "t" => CommandName::Accent, + _ => CommandName::Generic, + } + } +} + impl From<RootToken> for SyntaxKind { fn from(token: RootToken) -> Self { match token { @@ -428,8 +428,8 @@ impl From<ContentToken> for SyntaxKind { ContentToken::Quote => QUOTE, ContentToken::Integer => INTEGER, ContentToken::Nbsp => NBSP, - ContentToken::AccentName => ACCENT_NAME, - ContentToken::CommandName => COMMAND_NAME, + ContentToken::CommandName(CommandName::Accent) => ACCENT_NAME, + ContentToken::CommandName(CommandName::Generic) => COMMAND_NAME, ContentToken::Word => WORD, } } diff --git a/support/texlab/crates/parser/src/bibtex/tests.rs b/support/texlab/crates/parser/src/bibtex/tests.rs index e2bb1314d8..f0872b4ca6 100644 --- a/support/texlab/crates/parser/src/bibtex/tests.rs +++ b/support/texlab/crates/parser/src/bibtex/tests.rs @@ -3992,3 +3992,280 @@ fn test_rivest_1978() { "#]], ); } + +#[test] +fn test_cuesta_2002() { + check( + r#"@INPROCEEDINGS{Cuesta02, + author = {Cuesta, Carlos E. and de la Fuente, Pablo and Barrio-Sol\órzano, Manuel and Beato, Encarnaci\ón}, + title = {{Coordination in a Reflective Architecture Description Language}}, + booktitle = {{Proceedings of the 5th International Conference on Coordination Models and Languages (COORDINATION'02)}}, + year = 2002, + editor = {Arbab, Fahrad and Talcott, Carolyn}, + pages = {141--148}, + address = {York, United Kingdom}, + organization = {}, + publisher = {Springer}, + volume = {}, + number = {}, + series = {Lecture Notes in Computer Science 2315}, + month = apr, + note = {} +}"#, + expect![[r#" + ROOT@0..617 + ENTRY@0..617 + TYPE@0..14 "@INPROCEEDINGS" + L_DELIM@14..15 "{" + NAME@15..23 "Cuesta02" + COMMA@23..24 "," + WHITESPACE@24..27 "\n " + FIELD@27..136 + NAME@27..33 "author" + WHITESPACE@33..34 " " + EQ@34..35 "=" + WHITESPACE@35..36 " " + CURLY_GROUP@36..135 + L_CURLY@36..37 "{" + WORD@37..43 "Cuesta" + COMMA@43..44 "," + WHITESPACE@44..45 " " + WORD@45..51 "Carlos" + WHITESPACE@51..52 " " + WORD@52..54 "E." + WHITESPACE@54..55 " " + WORD@55..58 "and" + WHITESPACE@58..59 " " + WORD@59..61 "de" + WHITESPACE@61..62 " " + WORD@62..64 "la" + WHITESPACE@64..65 " " + WORD@65..71 "Fuente" + COMMA@71..72 "," + WHITESPACE@72..73 " " + WORD@73..78 "Pablo" + WHITESPACE@78..79 " " + WORD@79..82 "and" + WHITESPACE@82..83 " " + WORD@83..93 "Barrio-Sol" + COMMAND@93..101 + COMMAND_NAME@93..101 "\\órzano" + COMMA@101..102 "," + WHITESPACE@102..103 " " + WORD@103..109 "Manuel" + WHITESPACE@109..110 " " + WORD@110..113 "and" + WHITESPACE@113..114 " " + WORD@114..119 "Beato" + COMMA@119..120 "," + WHITESPACE@120..121 " " + WORD@121..130 "Encarnaci" + COMMAND@130..134 + COMMAND_NAME@130..134 "\\ón" + R_CURLY@134..135 "}" + COMMA@135..136 "," + WHITESPACE@136..139 "\n " + FIELD@139..214 + NAME@139..144 "title" + WHITESPACE@144..145 " " + EQ@145..146 "=" + WHITESPACE@146..147 " " + CURLY_GROUP@147..213 + L_CURLY@147..148 "{" + CURLY_GROUP@148..212 + L_CURLY@148..149 "{" + WORD@149..161 "Coordination" + WHITESPACE@161..162 " " + WORD@162..164 "in" + WHITESPACE@164..165 " " + WORD@165..166 "a" + WHITESPACE@166..167 " " + WORD@167..177 "Reflective" + WHITESPACE@177..178 " " + WORD@178..190 "Architecture" + WHITESPACE@190..191 " " + WORD@191..202 "Description" + WHITESPACE@202..203 " " + WORD@203..211 "Language" + R_CURLY@211..212 "}" + R_CURLY@212..213 "}" + COMMA@213..214 "," + WHITESPACE@214..217 "\n " + FIELD@217..336 + NAME@217..226 "booktitle" + WHITESPACE@226..227 " " + EQ@227..228 "=" + WHITESPACE@228..229 " " + CURLY_GROUP@229..335 + L_CURLY@229..230 "{" + CURLY_GROUP@230..334 + L_CURLY@230..231 "{" + WORD@231..242 "Proceedings" + WHITESPACE@242..243 " " + WORD@243..245 "of" + WHITESPACE@245..246 " " + WORD@246..249 "the" + WHITESPACE@249..250 " " + WORD@250..253 "5th" + WHITESPACE@253..254 " " + WORD@254..267 "International" + WHITESPACE@267..268 " " + WORD@268..278 "Conference" + WHITESPACE@278..279 " " + WORD@279..281 "on" + WHITESPACE@281..282 " " + WORD@282..294 "Coordination" + WHITESPACE@294..295 " " + WORD@295..301 "Models" + WHITESPACE@301..302 " " + WORD@302..305 "and" + WHITESPACE@305..306 " " + WORD@306..315 "Languages" + WHITESPACE@315..316 " " + WORD@316..333 "(COORDINATION'02)" + R_CURLY@333..334 "}" + R_CURLY@334..335 "}" + COMMA@335..336 "," + WHITESPACE@336..339 "\n " + FIELD@339..351 + NAME@339..343 "year" + WHITESPACE@343..344 " " + EQ@344..345 "=" + WHITESPACE@345..346 " " + LITERAL@346..350 + INTEGER@346..350 "2002" + COMMA@350..351 "," + WHITESPACE@351..354 "\n " + FIELD@354..400 + NAME@354..360 "editor" + WHITESPACE@360..361 " " + EQ@361..362 "=" + WHITESPACE@362..363 " " + CURLY_GROUP@363..399 + L_CURLY@363..364 "{" + WORD@364..369 "Arbab" + COMMA@369..370 "," + WHITESPACE@370..371 " " + WORD@371..377 "Fahrad" + WHITESPACE@377..378 " " + WORD@378..381 "and" + WHITESPACE@381..382 " " + WORD@382..389 "Talcott" + COMMA@389..390 "," + WHITESPACE@390..391 " " + WORD@391..398 "Carolyn" + R_CURLY@398..399 "}" + COMMA@399..400 "," + WHITESPACE@400..403 "\n " + FIELD@403..422 + NAME@403..408 "pages" + WHITESPACE@408..409 " " + EQ@409..410 "=" + WHITESPACE@410..411 " " + CURLY_GROUP@411..421 + L_CURLY@411..412 "{" + WORD@412..420 "141--148" + R_CURLY@420..421 "}" + COMMA@421..422 "," + WHITESPACE@422..425 "\n " + FIELD@425..458 + NAME@425..432 "address" + WHITESPACE@432..433 " " + EQ@433..434 "=" + WHITESPACE@434..435 " " + CURLY_GROUP@435..457 + L_CURLY@435..436 "{" + WORD@436..440 "York" + COMMA@440..441 "," + WHITESPACE@441..442 " " + WORD@442..448 "United" + WHITESPACE@448..449 " " + WORD@449..456 "Kingdom" + R_CURLY@456..457 "}" + COMMA@457..458 "," + WHITESPACE@458..461 "\n " + FIELD@461..479 + NAME@461..473 "organization" + WHITESPACE@473..474 " " + EQ@474..475 "=" + WHITESPACE@475..476 " " + CURLY_GROUP@476..478 + L_CURLY@476..477 "{" + R_CURLY@477..478 "}" + COMMA@478..479 "," + WHITESPACE@479..482 "\n " + FIELD@482..505 + NAME@482..491 "publisher" + WHITESPACE@491..492 " " + EQ@492..493 "=" + WHITESPACE@493..494 " " + CURLY_GROUP@494..504 + L_CURLY@494..495 "{" + WORD@495..503 "Springer" + R_CURLY@503..504 "}" + COMMA@504..505 "," + WHITESPACE@505..508 "\n " + FIELD@508..520 + NAME@508..514 "volume" + WHITESPACE@514..515 " " + EQ@515..516 "=" + WHITESPACE@516..517 " " + CURLY_GROUP@517..519 + L_CURLY@517..518 "{" + R_CURLY@518..519 "}" + COMMA@519..520 "," + WHITESPACE@520..523 "\n " + FIELD@523..535 + NAME@523..529 "number" + WHITESPACE@529..530 " " + EQ@530..531 "=" + WHITESPACE@531..532 " " + CURLY_GROUP@532..534 + L_CURLY@532..533 "{" + R_CURLY@533..534 "}" + COMMA@534..535 "," + WHITESPACE@535..538 "\n " + FIELD@538..588 + NAME@538..544 "series" + WHITESPACE@544..545 " " + EQ@545..546 "=" + WHITESPACE@546..547 " " + CURLY_GROUP@547..587 + L_CURLY@547..548 "{" + WORD@548..555 "Lecture" + WHITESPACE@555..556 " " + WORD@556..561 "Notes" + WHITESPACE@561..562 " " + WORD@562..564 "in" + WHITESPACE@564..565 " " + WORD@565..573 "Computer" + WHITESPACE@573..574 " " + WORD@574..581 "Science" + WHITESPACE@581..582 " " + INTEGER@582..586 "2315" + R_CURLY@586..587 "}" + COMMA@587..588 "," + WHITESPACE@588..591 "\n " + FIELD@591..603 + NAME@591..596 "month" + WHITESPACE@596..597 " " + EQ@597..598 "=" + WHITESPACE@598..599 " " + LITERAL@599..602 + NAME@599..602 "apr" + COMMA@602..603 "," + WHITESPACE@603..606 "\n " + FIELD@606..616 + NAME@606..610 "note" + WHITESPACE@610..611 " " + EQ@611..612 "=" + WHITESPACE@612..613 " " + CURLY_GROUP@613..615 + L_CURLY@613..614 "{" + R_CURLY@614..615 "}" + WHITESPACE@615..616 "\n" + R_DELIM@616..617 "}" + + "#]], + ) +} diff --git a/support/texlab/crates/parser/src/config.rs b/support/texlab/crates/parser/src/config.rs index f81083f132..2c5380448f 100644 --- a/support/texlab/crates/parser/src/config.rs +++ b/support/texlab/crates/parser/src/config.rs @@ -3,12 +3,15 @@ use rustc_hash::FxHashSet; #[derive(Debug)] pub struct SyntaxConfig { pub follow_package_links: bool, + pub use_file_list: bool, pub math_environments: FxHashSet<String>, pub enum_environments: FxHashSet<String>, pub verbatim_environments: FxHashSet<String>, pub citation_commands: FxHashSet<String>, pub label_definition_commands: FxHashSet<String>, + pub label_definition_prefixes: Vec<(String, String)>, pub label_reference_commands: FxHashSet<String>, + pub label_reference_prefixes: Vec<(String, String)>, } impl Default for SyntaxConfig { @@ -38,19 +41,32 @@ impl Default for SyntaxConfig { .map(ToString::to_string) .collect(); + let label_definition_prefixes = DEFAULT_LABEL_DEFINITION_PREFIXES + .iter() + .map(|(x, y)| (ToString::to_string(x), ToString::to_string(y))) + .collect(); + let label_reference_commands = DEFAULT_LABEL_REFERENCE_COMMANDS .iter() .map(ToString::to_string) .collect(); + let label_reference_prefixes = DEFAULT_LABEL_REFERENCE_PREFIXES + .iter() + .map(|(x, y)| (ToString::to_string(x), ToString::to_string(y))) + .collect(); + Self { follow_package_links: false, + use_file_list: false, math_environments, enum_environments, verbatim_environments, citation_commands, label_definition_commands, + label_definition_prefixes, label_reference_commands, + label_reference_prefixes, } } } @@ -170,7 +186,9 @@ static DEFAULT_CITATION_COMMANDS: &[&str] = &[ "citeA*", ]; -static DEFAULT_LABEL_DEFINITION_COMMANDS: &[&str] = &["label"]; +static DEFAULT_LABEL_DEFINITION_COMMANDS: &[&str] = &["label", "zlabel"]; + +static DEFAULT_LABEL_DEFINITION_PREFIXES: &[(&str, &str)] = &[]; static DEFAULT_LABEL_REFERENCE_COMMANDS: &[&str] = &[ "ref", @@ -182,6 +200,10 @@ static DEFAULT_LABEL_REFERENCE_COMMANDS: &[&str] = &[ "cref*", "Cref", "Cref*", + "zcref", + "zcref*", + "zcpageref", + "zcpageref*", "namecref", "nameCref", "lcnamecref", @@ -192,3 +214,5 @@ static DEFAULT_LABEL_REFERENCE_COMMANDS: &[&str] = &[ "labelcpageref", "eqref", ]; + +static DEFAULT_LABEL_REFERENCE_PREFIXES: &[(&str, &str)] = &[]; diff --git a/support/texlab/crates/parser/src/file_list.rs b/support/texlab/crates/parser/src/file_list.rs new file mode 100644 index 0000000000..c892ebfeef --- /dev/null +++ b/support/texlab/crates/parser/src/file_list.rs @@ -0,0 +1,44 @@ +use syntax::file_list::FileList; + +pub fn parse_file_list(input: &str) -> FileList { + let mut file_list = FileList::default(); + for line in input.lines() { + if let Some(working_dir) = line.strip_prefix("PWD ") { + file_list.working_dir = Some(working_dir.into()); + } else if let Some(input) = line.strip_prefix("INPUT ") { + file_list.inputs.insert(input.into()); + } else if let Some(output) = line.strip_prefix("OUTPUT ") { + file_list.outputs.insert(output.into()); + } + } + + file_list +} + +#[cfg(test)] +mod tests { + use rustc_hash::FxHashSet; + + use super::*; + + #[test] + fn test_parse_file_list() { + let input = r#" +PWD /home/user +INPUT file1.tex +INPUT file1.tex +OUTPUT file2.pdf"#; + + let expected_file_list = FileList { + working_dir: Some("/home/user".into()), + inputs: FxHashSet::from_iter(["file1.tex".into()]), + outputs: FxHashSet::from_iter(["file2.pdf".into()]), + }; + let actual_file_list = parse_file_list(input); + assert_eq!(actual_file_list, expected_file_list); + + assert_eq!(actual_file_list.working_dir, Some("/home/user".into())); + assert_eq!(actual_file_list.inputs.len(), 1); + assert_eq!(actual_file_list.outputs.len(), 1); + } +} diff --git a/support/texlab/crates/parser/src/latex.rs b/support/texlab/crates/parser/src/latex.rs index 669ba82f99..8379d9c5ca 100644 --- a/support/texlab/crates/parser/src/latex.rs +++ b/support/texlab/crates/parser/src/latex.rs @@ -158,6 +158,9 @@ impl<'a> Parser<'a> { CommandName::EndBlockComment => self.generic_command(), CommandName::VerbatimBlock => self.verbatim_block(), CommandName::GraphicsPath => self.graphics_path(), + CommandName::BibItem => self.bibitem(), + CommandName::TocContentsLine => self.toc_contents_line(), + CommandName::TocNumberLine => self.toc_number_line(), }, } } @@ -862,7 +865,20 @@ impl<'a> Parser<'a> { } if self.lexer.peek() == Some(Token::LCurly) { - self.curly_group_word(); + self.builder.start_node(CURLY_GROUP_WORD.into()); + self.eat(); + self.trivia(); + + if self.peek() == Some(Token::Word) || self.peek() == Some(Token::Pipe) { + self.key(); + } + + if let Some(Token::CommandName(_)) = self.peek() { + self.content(ParserContext::default()); + } + + self.expect(Token::RCurly); + self.builder.finish_node(); } self.builder.finish_node(); @@ -1235,6 +1251,50 @@ impl<'a> Parser<'a> { } } } + + fn bibitem(&mut self) { + self.builder.start_node(BIBITEM.into()); + self.eat(); + self.trivia(); + + if self.lexer.peek() == Some(Token::LCurly) { + self.curly_group_word(); + } + + self.builder.finish_node(); + } + + fn toc_contents_line(&mut self) { + self.builder.start_node(TOC_CONTENTS_LINE.into()); + self.eat(); + self.trivia(); + + if self.lexer.peek() == Some(Token::LCurly) { + self.curly_group(); + } + + if self.lexer.peek() == Some(Token::LCurly) { + self.curly_group(); + } + + if self.lexer.peek() == Some(Token::LCurly) { + self.curly_group(); + } + + self.builder.finish_node(); + } + + fn toc_number_line(&mut self) { + self.builder.start_node(TOC_NUMBER_LINE.into()); + self.eat(); + self.trivia(); + + if self.lexer.peek() == Some(Token::LCurly) { + self.curly_group(); + } + + self.builder.finish_node(); + } } pub fn parse_latex(text: &str, config: &SyntaxConfig) -> GreenNode { diff --git a/support/texlab/crates/parser/src/latex/lexer.rs b/support/texlab/crates/parser/src/latex/lexer.rs index 47a6c43b06..19d5ddade5 100644 --- a/support/texlab/crates/parser/src/latex/lexer.rs +++ b/support/texlab/crates/parser/src/latex/lexer.rs @@ -27,7 +27,7 @@ impl<'a> Lexer<'a> { pub fn eat(&mut self) -> Option<(SyntaxKind, &'a str)> { let (kind, text) = self.tokens.pop()?; let kind = match kind { - Token::LineBreak => SyntaxKind::LINE_BREAK, + Token::LineBreak => SyntaxKind::WHITESPACE, Token::Whitespace => SyntaxKind::WHITESPACE, Token::LineComment => SyntaxKind::COMMENT, Token::LCurly => SyntaxKind::L_CURLY, diff --git a/support/texlab/crates/parser/src/latex/lexer/commands.rs b/support/texlab/crates/parser/src/latex/lexer/commands.rs index 4d1a7ca320..3f8b6e36ed 100644 --- a/support/texlab/crates/parser/src/latex/lexer/commands.rs +++ b/support/texlab/crates/parser/src/latex/lexer/commands.rs @@ -96,6 +96,9 @@ pub fn classify(name: &str, config: &SyntaxConfig) -> CommandName { "iffalse" => CommandName::BeginBlockComment, "fi" => CommandName::EndBlockComment, "verb" => CommandName::VerbatimBlock, + "bibitem" => CommandName::BibItem, + "contentsline" => CommandName::TocContentsLine, + "numberline" => CommandName::TocNumberLine, _ if config.citation_commands.contains(name) => CommandName::Citation, _ if config.label_definition_commands.contains(name) => CommandName::LabelDefinition, diff --git a/support/texlab/crates/parser/src/latex/lexer/types.rs b/support/texlab/crates/parser/src/latex/lexer/types.rs index 896ce01c05..796378f671 100644 --- a/support/texlab/crates/parser/src/latex/lexer/types.rs +++ b/support/texlab/crates/parser/src/latex/lexer/types.rs @@ -1,5 +1,7 @@ use logos::Logos; +use crate::util::lex_command_name; + #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, Logos)] pub enum Token { #[regex(r"[\r\n]+", priority = 2)] @@ -44,55 +46,10 @@ pub enum Token { #[regex(r"\$\$?")] Dollar, - #[regex(r"\\", lex_command_name)] + #[regex(r"\\", |lexer| { lex_command_name(lexer); CommandName::Generic } )] CommandName(CommandName), } -fn lex_command_name(lexer: &mut logos::Lexer<Token>) -> CommandName { - let input = &lexer.source()[lexer.span().end..]; - - let mut chars = input.chars().peekable(); - let Some(c) = chars.next() else { - return CommandName::Generic; - }; - - if c.is_whitespace() { - return CommandName::Generic; - } - - lexer.bump(c.len_utf8()); - if !c.is_alphanumeric() && c != '@' { - return CommandName::Generic; - } - - while let Some(c) = chars.next() { - match c { - '*' => { - lexer.bump(c.len_utf8()); - break; - } - c if c.is_alphanumeric() => { - lexer.bump(c.len_utf8()); - } - '_' => { - if !matches!(chars.peek(), Some(c) if c.is_alphanumeric()) { - break; - } - - lexer.bump(c.len_utf8()); - } - '@' | ':' => { - lexer.bump(c.len_utf8()); - } - _ => { - break; - } - } - } - - CommandName::Generic -} - #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)] pub enum CommandName { Generic, @@ -138,6 +95,9 @@ pub enum CommandName { BeginBlockComment, EndBlockComment, VerbatimBlock, + BibItem, + TocContentsLine, + TocNumberLine, } #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)] diff --git a/support/texlab/crates/parser/src/latex/tests.rs b/support/texlab/crates/parser/src/latex/tests.rs index 2c9dc9335f..623c9329fb 100644 --- a/support/texlab/crates/parser/src/latex/tests.rs +++ b/support/texlab/crates/parser/src/latex/tests.rs @@ -24,24 +24,24 @@ Baz"#, PREAMBLE@0..48 TEXT@0..4 WORD@0..3 "Foo" - LINE_BREAK@3..4 "\n" + WHITESPACE@3..4 "\n" BLOCK_COMMENT@4..22 COMMAND_NAME@4..12 "\\iffalse" - LINE_BREAK@12..13 "\n" + WHITESPACE@12..13 "\n" WORD@13..18 "Test1" - LINE_BREAK@18..19 "\n" + WHITESPACE@18..19 "\n" COMMAND_NAME@19..22 "\\fi" - LINE_BREAK@22..23 "\n" + WHITESPACE@22..23 "\n" TEXT@23..27 WORD@23..26 "Bar" - LINE_BREAK@26..27 "\n" + WHITESPACE@26..27 "\n" BLOCK_COMMENT@27..44 COMMAND_NAME@27..35 "\\iffalse" - LINE_BREAK@35..36 "\n" + WHITESPACE@35..36 "\n" COMMAND_NAME@36..40 "\\fii" - LINE_BREAK@40..41 "\n" + WHITESPACE@40..41 "\n" COMMAND_NAME@41..44 "\\fi" - LINE_BREAK@44..45 "\n" + WHITESPACE@44..45 "\n" TEXT@45..48 WORD@45..48 "Baz" @@ -776,7 +776,7 @@ fn test_environment_asymptote() { KEY@7..10 WORD@7..10 "asy" R_CURLY@10..11 "}" - LINE_BREAK@11..12 "\n" + WHITESPACE@11..12 "\n" WHITESPACE@12..16 " " TEXT@16..22 WORD@16..22 "printf" @@ -793,7 +793,7 @@ fn test_environment_asymptote() { R_PAREN@38..39 ")" TEXT@39..41 WORD@39..40 ";" - LINE_BREAK@40..41 "\n" + WHITESPACE@40..41 "\n" END@41..50 COMMAND_NAME@41..45 "\\end" CURLY_GROUP_WORD@45..50 @@ -1289,7 +1289,7 @@ fn test_acronym_package() { WHITESPACE@26..27 " " WORD@27..33 "Second" R_CURLY@33..34 "}" - LINE_BREAK@34..35 "\n" + WHITESPACE@34..35 "\n" "#]], ); @@ -2384,7 +2384,7 @@ fn test_issue_568() { WHITESPACE@15..16 " " WORD@16..34 "scripts/test.ipynb" R_CURLY@34..35 "}" - LINE_BREAK@35..36 "\n" + WHITESPACE@35..36 "\n" LABEL_DEFINITION@36..51 COMMAND_NAME@36..42 "\\label" CURLY_GROUP_WORD@42..51 @@ -2433,7 +2433,7 @@ fn test_issue_745() { KEY@15..22 WORD@15..22 "article" R_CURLY@22..23 "}" - LINE_BREAK@23..24 "\n" + WHITESPACE@23..24 "\n" PACKAGE_INCLUDE@24..50 COMMAND_NAME@24..35 "\\usepackage" CURLY_GROUP_WORD_LIST@35..50 @@ -2442,10 +2442,10 @@ fn test_issue_745() { WORD@36..46 "tabularray" R_CURLY@46..47 "}" WHITESPACE@47..48 " " - LINE_BREAK@48..50 "\n\n" + WHITESPACE@48..50 "\n\n" GENERIC_COMMAND@50..64 COMMAND_NAME@50..63 "\\ExplSyntaxOn" - LINE_BREAK@63..64 "\n" + WHITESPACE@63..64 "\n" ENVIRONMENT_DEFINITION@64..200 COMMAND_NAME@64..87 "\\NewDocumentEnvironment" CURLY_GROUP_WORD@87..96 @@ -2463,19 +2463,19 @@ fn test_issue_745() { TEXT@100..101 WORD@100..101 "m" R_CURLY@101..102 "}" - LINE_BREAK@102..103 "\n" + WHITESPACE@102..103 "\n" WHITESPACE@103..107 " " CURLY_GROUP@107..200 L_CURLY@107..108 "{" - LINE_BREAK@108..109 "\n" + WHITESPACE@108..109 "\n" WHITESPACE@109..113 " " GENERIC_COMMAND@113..194 COMMAND_NAME@113..119 "\\use:x" - LINE_BREAK@119..120 "\n" + WHITESPACE@119..120 "\n" WHITESPACE@120..124 " " CURLY_GROUP@124..194 L_CURLY@124..125 "{" - LINE_BREAK@125..126 "\n" + WHITESPACE@125..126 "\n" WHITESPACE@126..130 " " GENERIC_COMMAND@130..141 COMMAND_NAME@130..140 "\\exp_not:N" @@ -2488,7 +2488,7 @@ fn test_issue_745() { KEY@148..152 WORD@148..152 "tblr" R_CURLY@152..153 "}" - LINE_BREAK@153..154 "\n" + WHITESPACE@153..154 "\n" WHITESPACE@154..158 " " BRACK_GROUP@158..179 L_BRACK@158..159 "[" @@ -2500,24 +2500,24 @@ fn test_issue_745() { WORD@170..172 "#1" R_CURLY@172..173 "}" R_BRACK@173..174 "]" - LINE_BREAK@174..175 "\n" + WHITESPACE@174..175 "\n" WHITESPACE@175..179 " " CURLY_GROUP@179..188 L_CURLY@179..180 "{" TEXT@180..182 WORD@180..182 "#2" R_CURLY@182..183 "}" - LINE_BREAK@183..184 "\n" + WHITESPACE@183..184 "\n" WHITESPACE@184..188 " " R_CURLY@188..189 "}" - LINE_BREAK@189..190 "\n" + WHITESPACE@189..190 "\n" WHITESPACE@190..194 " " R_CURLY@194..195 "}" - LINE_BREAK@195..196 "\n" + WHITESPACE@195..196 "\n" WHITESPACE@196..200 " " CURLY_GROUP@200..223 L_CURLY@200..201 "{" - LINE_BREAK@201..202 "\n" + WHITESPACE@201..202 "\n" WHITESPACE@202..206 " " GENERIC_COMMAND@206..221 COMMAND_NAME@206..210 "\\end" @@ -2526,13 +2526,13 @@ fn test_issue_745() { TEXT@211..215 WORD@211..215 "tblr" R_CURLY@215..216 "}" - LINE_BREAK@216..217 "\n" + WHITESPACE@216..217 "\n" WHITESPACE@217..221 " " R_CURLY@221..222 "}" - LINE_BREAK@222..223 "\n" + WHITESPACE@222..223 "\n" GENERIC_COMMAND@223..239 COMMAND_NAME@223..237 "\\ExplSyntaxOff" - LINE_BREAK@237..239 "\n\n" + WHITESPACE@237..239 "\n\n" ENVIRONMENT@239..271 BEGIN@239..257 COMMAND_NAME@239..245 "\\begin" @@ -2541,7 +2541,7 @@ fn test_issue_745() { KEY@246..254 WORD@246..254 "document" R_CURLY@254..255 "}" - LINE_BREAK@255..257 "\n\n" + WHITESPACE@255..257 "\n\n" END@257..271 COMMAND_NAME@257..261 "\\end" CURLY_GROUP_WORD@261..271 @@ -2620,7 +2620,7 @@ This is an asdf undefined command KEY@15..22 WORD@15..22 "minimal" R_CURLY@22..23 "}" - LINE_BREAK@23..24 "\n" + WHITESPACE@23..24 "\n" ENVIRONMENT@24..135 BEGIN@24..41 COMMAND_NAME@24..30 "\\begin" @@ -2629,7 +2629,7 @@ This is an asdf undefined command KEY@31..39 WORD@31..39 "document" R_CURLY@39..40 "}" - LINE_BREAK@40..41 "\n" + WHITESPACE@40..41 "\n" TEXT@41..75 WORD@41..45 "This" WHITESPACE@45..46 " " @@ -2642,24 +2642,24 @@ This is an asdf undefined command WORD@57..66 "undefined" WHITESPACE@66..67 " " WORD@67..74 "command" - LINE_BREAK@74..75 "\n" + WHITESPACE@74..75 "\n" BLOCK_COMMENT@75..120 COMMAND_NAME@75..83 "\\iffalse" - LINE_BREAK@83..84 "\n" + WHITESPACE@83..84 "\n" WHITESPACE@84..86 " " BLOCK_COMMENT@86..97 COMMAND_NAME@86..94 "\\iffalse" COMMAND_NAME@94..97 "\\fi" - LINE_BREAK@97..98 "\n" + WHITESPACE@97..98 "\n" WHITESPACE@98..100 " " COMMAND_NAME@100..104 "\\end" L_CURLY@104..105 "{" WORD@105..114 "enumerate" R_CURLY@114..115 "}" WHITESPACE@115..116 " " - LINE_BREAK@116..117 "\n" + WHITESPACE@116..117 "\n" COMMAND_NAME@117..120 "\\fi" - LINE_BREAK@120..121 "\n" + WHITESPACE@120..121 "\n" END@121..135 COMMAND_NAME@121..125 "\\end" CURLY_GROUP_WORD@125..135 @@ -2688,7 +2688,7 @@ fn test_issue_857() { CURLY_GROUP@14..17 L_CURLY@14..15 "{" R_CURLY@15..16 "}" - LINE_BREAK@16..17 "\n" + WHITESPACE@16..17 "\n" NEW_COMMAND_DEFINITION@17..38 COMMAND_NAME@17..28 "\\newcommand" CURLY_GROUP_COMMAND@28..35 @@ -2698,7 +2698,7 @@ fn test_issue_857() { CURLY_GROUP@35..38 L_CURLY@35..36 "{" R_CURLY@36..37 "}" - LINE_BREAK@37..38 "\n" + WHITESPACE@37..38 "\n" NEW_COMMAND_DEFINITION@38..49 COMMAND_NAME@38..49 "\\newcommand" GENERIC_COMMAND@49..55 @@ -2768,31 +2768,31 @@ fn test_issue_919() { KEY@15..22 WORD@15..22 "article" R_CURLY@22..23 "}" - LINE_BREAK@23..25 "\n\n" + WHITESPACE@23..25 "\n\n" PACKAGE_INCLUDE@25..173 COMMAND_NAME@25..36 "\\usepackage" CURLY_GROUP_WORD_LIST@36..173 L_CURLY@36..37 "{" - LINE_BREAK@37..38 "\n" + WHITESPACE@37..38 "\n" WHITESPACE@38..42 " " KEY@42..48 WORD@42..48 "lipsum" COMMA@48..49 "," WHITESPACE@49..50 " " COMMENT@50..70 "% provides blindtext" - LINE_BREAK@70..71 "\n" + WHITESPACE@70..71 "\n" WHITESPACE@71..75 " " KEY@75..83 WORD@75..83 "booktabs" COMMA@83..84 "," WHITESPACE@84..85 " " COMMENT@85..110 "% better rules for ta ..." - LINE_BREAK@110..111 "\n" + WHITESPACE@110..111 "\n" WHITESPACE@111..115 " " COMMENT@115..169 "%xcolor % easily defi ..." - LINE_BREAK@169..170 "\n" + WHITESPACE@169..170 "\n" R_CURLY@170..171 "}" - LINE_BREAK@171..173 "\n\n" + WHITESPACE@171..173 "\n\n" ENVIRONMENT@173..217 BEGIN@173..194 COMMAND_NAME@173..179 "\\begin" @@ -2801,11 +2801,11 @@ fn test_issue_919() { KEY@180..188 WORD@180..188 "document" R_CURLY@188..189 "}" - LINE_BREAK@189..190 "\n" + WHITESPACE@189..190 "\n" WHITESPACE@190..194 " " GENERIC_COMMAND@194..202 COMMAND_NAME@194..201 "\\lipsum" - LINE_BREAK@201..202 "\n" + WHITESPACE@201..202 "\n" END@202..217 COMMAND_NAME@202..206 "\\end" CURLY_GROUP_WORD@206..217 @@ -2813,7 +2813,7 @@ fn test_issue_919() { KEY@207..215 WORD@207..215 "document" R_CURLY@215..216 "}" - LINE_BREAK@216..217 "\n" + WHITESPACE@216..217 "\n" "#]], ); @@ -2853,7 +2853,7 @@ world}"#, L_CURLY@6..7 "{" KEY@7..13 WORD@7..12 "hello" - LINE_BREAK@12..13 "\n" + WHITESPACE@12..13 "\n" TEXT@13..18 WORD@13..18 "world" ERROR@18..19 @@ -3150,12 +3150,12 @@ Section 2"#, WHITESPACE@16..17 " " WORD@17..18 "1" R_CURLY@18..19 "}" - LINE_BREAK@19..20 "\n" + WHITESPACE@19..20 "\n" TEXT@20..31 WORD@20..27 "Section" WHITESPACE@27..28 " " WORD@28..29 "1" - LINE_BREAK@29..31 "\n\n" + WHITESPACE@29..31 "\n\n" PARAGRAPH@31..68 COMMAND_NAME@31..41 "\\paragraph" CURLY_GROUP@41..55 @@ -3165,12 +3165,12 @@ Section 2"#, WHITESPACE@51..52 " " WORD@52..53 "1" R_CURLY@53..54 "}" - LINE_BREAK@54..55 "\n" + WHITESPACE@54..55 "\n" TEXT@55..68 WORD@55..64 "Paragraph" WHITESPACE@64..65 " " WORD@65..66 "1" - LINE_BREAK@66..68 "\n\n" + WHITESPACE@66..68 "\n\n" PARAGRAPH@68..105 COMMAND_NAME@68..78 "\\paragraph" CURLY_GROUP@78..92 @@ -3180,12 +3180,12 @@ Section 2"#, WHITESPACE@88..89 " " WORD@89..90 "2" R_CURLY@90..91 "}" - LINE_BREAK@91..92 "\n" + WHITESPACE@91..92 "\n" TEXT@92..105 WORD@92..101 "Paragraph" WHITESPACE@101..102 " " WORD@102..103 "2" - LINE_BREAK@103..105 "\n\n" + WHITESPACE@103..105 "\n\n" SECTION@105..134 COMMAND_NAME@105..113 "\\section" CURLY_GROUP@113..125 @@ -3195,7 +3195,7 @@ Section 2"#, WHITESPACE@121..122 " " WORD@122..123 "2" R_CURLY@123..124 "}" - LINE_BREAK@124..125 "\n" + WHITESPACE@124..125 "\n" TEXT@125..134 WORD@125..132 "Section" WHITESPACE@132..133 " " diff --git a/support/texlab/crates/parser/src/lib.rs b/support/texlab/crates/parser/src/lib.rs index 2a4190b7e2..b3952d6ea5 100644 --- a/support/texlab/crates/parser/src/lib.rs +++ b/support/texlab/crates/parser/src/lib.rs @@ -1,10 +1,12 @@ mod bibtex; mod build_log; mod config; +mod file_list; mod latex; mod latexmkrc; +pub(crate) mod util; pub use self::{ - bibtex::parse_bibtex, build_log::parse_build_log, config::*, latex::parse_latex, - latexmkrc::parse_latexmkrc, + bibtex::parse_bibtex, build_log::parse_build_log, config::*, file_list::parse_file_list, + latex::parse_latex, latexmkrc::parse_latexmkrc, }; diff --git a/support/texlab/crates/parser/src/util.rs b/support/texlab/crates/parser/src/util.rs new file mode 100644 index 0000000000..cb147c493b --- /dev/null +++ b/support/texlab/crates/parser/src/util.rs @@ -0,0 +1,52 @@ +use std::ops::{Index, Range}; + +use logos::{Logos, Source}; + +pub fn lex_command_name<'a, T>(lexer: &mut logos::Lexer<'a, T>) -> &'a str +where + T: Logos<'a>, + T::Source: Index<Range<usize>, Output = str>, +{ + let start = lexer.span().end; + let input = &lexer.source()[start..lexer.source().len()]; + + let mut chars = input.chars().peekable(); + let Some(c) = chars.next() else { + return ""; + }; + + if c.is_whitespace() { + return ""; + } + + lexer.bump(c.len_utf8()); + + if c.is_alphanumeric() || c == '@' { + while let Some(c) = chars.next() { + match c { + '*' => { + lexer.bump(c.len_utf8()); + break; + } + c if c.is_alphanumeric() => { + lexer.bump(c.len_utf8()); + } + '_' => { + if !matches!(chars.peek(), Some(c) if c.is_alphanumeric()) { + break; + } + + lexer.bump(c.len_utf8()); + } + '@' | ':' => { + lexer.bump(c.len_utf8()); + } + _ => { + break; + } + } + } + } + + &lexer.source()[start..lexer.span().end] +} diff --git a/support/texlab/crates/references/Cargo.toml b/support/texlab/crates/references/Cargo.toml index e78e84ea4c..3b3abf0ecc 100644 --- a/support/texlab/crates/references/Cargo.toml +++ b/support/texlab/crates/references/Cargo.toml @@ -8,8 +8,8 @@ rust-version.workspace = true [dependencies] base-db = { path = "../base-db" } -rowan = "0.15.15" -rustc-hash = "1.1.0" +rowan.workspace = true +rustc-hash.workspace = true syntax = { path = "../syntax" } [dev-dependencies] diff --git a/support/texlab/crates/references/src/command.rs b/support/texlab/crates/references/src/command.rs index f4504641f2..cf5849a136 100644 --- a/support/texlab/crates/references/src/command.rs +++ b/support/texlab/crates/references/src/command.rs @@ -32,7 +32,9 @@ pub(super) fn find_all(context: &mut ReferenceContext) -> Option<()> { .collect(); for command in &data.semantics.commands { - if command.text == token.text()[1..] { + let command_text = command.text.trim_end_matches('*'); + let token_text = token.text()[1..].trim_end_matches('*'); + if command_text == token_text { let kind = if defs.contains(command) { ReferenceKind::Definition } else { diff --git a/support/texlab/crates/references/src/tests.rs b/support/texlab/crates/references/src/tests.rs index 845282afb0..7131480699 100644 --- a/support/texlab/crates/references/src/tests.rs +++ b/support/texlab/crates/references/src/tests.rs @@ -260,3 +260,43 @@ fn test_new_command_definition_include_decl() { true, ); } + +#[test] +fn test_new_command_definition_starred() { + check( + r#" +%! main.tex +\foo + ^^^ +\foo* + | + ^^^^ + +\NewDocumentCommand{\foo}{s m}{% + \IfBooleanTF{#1}{\textbf{#2}}{#2}% +} +"#, + false, + ); +} + +#[test] +fn test_new_command_definition_starred_include_decl() { + check( + r#" +%! main.tex +\foo + ^^^ +\foo* + | + ^^^^ + +\NewDocumentCommand{\foo}{s m}{% + ^^^ + \IfBooleanTF{#1}{\textbf{#2}}{#2}% +} + +"#, + true, + ); +} diff --git a/support/texlab/crates/rename/Cargo.toml b/support/texlab/crates/rename/Cargo.toml index 1b2b91ee2a..bbc3487ccf 100644 --- a/support/texlab/crates/rename/Cargo.toml +++ b/support/texlab/crates/rename/Cargo.toml @@ -8,12 +8,13 @@ rust-version.workspace = true [dependencies] base-db = { path = "../base-db" } -rowan = "0.15.15" -rustc-hash = "1.1.0" +rowan.workspace = true +rustc-hash.workspace = true syntax = { path = "../syntax" } [dev-dependencies] test-utils = { path = "../test-utils" } +parser = { path = "../parser" } [lib] doctest = false diff --git a/support/texlab/crates/rename/src/command.rs b/support/texlab/crates/rename/src/command.rs index 7b17d4271c..037e09e4ca 100644 --- a/support/texlab/crates/rename/src/command.rs +++ b/support/texlab/crates/rename/src/command.rs @@ -35,7 +35,10 @@ pub(super) fn rename(builder: &mut RenameBuilder) -> Option<()> { } } - builder.result.changes.insert(*document, edits); + builder + .result + .changes + .insert(*document, edits.iter().map(|&x| x.into()).collect()); } Some(()) diff --git a/support/texlab/crates/rename/src/entry.rs b/support/texlab/crates/rename/src/entry.rs index 5bed4ae613..d35d9e9e84 100644 --- a/support/texlab/crates/rename/src/entry.rs +++ b/support/texlab/crates/rename/src/entry.rs @@ -42,7 +42,7 @@ pub(super) fn rename(builder: &mut RenameBuilder) -> Option<()> { for (document, range) in citations.chain(entries) { let entry = builder.result.changes.entry(document); - entry.or_default().push(range); + entry.or_default().push(range.into()); } Some(()) diff --git a/support/texlab/crates/rename/src/label.rs b/support/texlab/crates/rename/src/label.rs index b21e1b795e..1d82f5e2b4 100644 --- a/support/texlab/crates/rename/src/label.rs +++ b/support/texlab/crates/rename/src/label.rs @@ -3,22 +3,65 @@ use base_db::{ util::queries::{self, Object}, }; -use crate::{RenameBuilder, RenameParams}; +use crate::{RenameBuilder, RenameInformation, RenameParams}; + +struct PrefixInformation<'a> { + def_prefixes: &'a Vec<(String, String)>, + ref_prefixes: &'a Vec<(String, String)>, +} + +fn label_has_prefix(pref_info: &PrefixInformation, label: &tex::Label) -> Option<String> { + match label.kind { + tex::LabelKind::Definition => pref_info.def_prefixes.iter(), + _ => pref_info.ref_prefixes.iter(), + } + .find_map(|(k, v)| { + if k == &label.cmd.clone().unwrap_or(String::new()) { + Some(v) + } else { + None + } + }) + .cloned() +} + +fn find_prefix_in_any( + builder: &mut RenameBuilder, + pref_info: &PrefixInformation, + name: &str, +) -> Option<String> { + let project = &builder.params.feature.project; + queries::objects_with_name::<tex::Label>(project, name) + .find_map(|(_, label)| label_has_prefix(&pref_info, label)) +} pub(super) fn prepare_rename(params: &RenameParams) -> Option<Span> { let data = params.feature.document.data.as_tex()?; let labels = &data.semantics.labels; let label = queries::object_at_cursor(labels, params.offset, queries::SearchMode::Name)?; + Some(Span::new(label.object.name.text.clone(), label.range)) } pub(super) fn rename(builder: &mut RenameBuilder) -> Option<()> { let name = prepare_rename(&builder.params)?; + let syn = &builder.params.feature.workspace.config().syntax; + let pref_info = PrefixInformation { + def_prefixes: &syn.label_definition_prefixes, + ref_prefixes: &syn.label_reference_prefixes, + }; + let prefix = find_prefix_in_any(builder, &pref_info, &name.text); + let project = &builder.params.feature.project; for (document, label) in queries::objects_with_name::<tex::Label>(project, &name.text) { + let prefix = label_has_prefix(&pref_info, label).map_or(prefix.clone(), |_| None); + let entry = builder.result.changes.entry(document); - entry.or_default().push(label.name_range()); + entry.or_default().push(RenameInformation { + range: label.name_range(), + prefix: prefix.clone(), + }); } Some(()) diff --git a/support/texlab/crates/rename/src/lib.rs b/support/texlab/crates/rename/src/lib.rs index bf9023ddef..e6c05ad268 100644 --- a/support/texlab/crates/rename/src/lib.rs +++ b/support/texlab/crates/rename/src/lib.rs @@ -13,8 +13,14 @@ pub struct RenameParams<'a> { } #[derive(Debug, Default)] +pub struct RenameInformation { + pub range: TextRange, + pub prefix: Option<String>, +} + +#[derive(Debug, Default)] pub struct RenameResult<'a> { - pub changes: FxHashMap<&'a Document, Vec<TextRange>>, + pub changes: FxHashMap<&'a Document, Vec<RenameInformation>>, } struct RenameBuilder<'a> { @@ -22,6 +28,20 @@ struct RenameBuilder<'a> { result: RenameResult<'a>, } +impl From<TextRange> for RenameInformation { + fn from(range: TextRange) -> Self { + RenameInformation { + range, + prefix: None, + } + } +} +impl PartialEq for RenameInformation { + fn eq(&self, other: &Self) -> bool { + self.range == other.range + } +} + pub fn prepare_rename(params: &RenameParams) -> Option<TextRange> { command::prepare_rename(params) .or_else(|| entry::prepare_rename(params)) diff --git a/support/texlab/crates/rename/src/tests.rs b/support/texlab/crates/rename/src/tests.rs index 0f4263033e..4da866fb08 100644 --- a/support/texlab/crates/rename/src/tests.rs +++ b/support/texlab/crates/rename/src/tests.rs @@ -1,23 +1,37 @@ use rustc_hash::FxHashMap; -use crate::RenameParams; +use base_db::Config; +use parser::SyntaxConfig; -fn check(input: &str) { - let fixture = test_utils::fixture::Fixture::parse(input); +use crate::{RenameInformation, RenameParams}; + +fn check_with_syntax_config(config: SyntaxConfig, input: &str) { + let mut fixture = test_utils::fixture::Fixture::parse(input); + fixture.workspace.set_config(Config { + syntax: config, + ..Config::default() + }); + let fixture = fixture; - let mut expected = FxHashMap::default(); + let mut expected: FxHashMap<_, Vec<RenameInformation>> = FxHashMap::default(); for spec in &fixture.documents { if !spec.ranges.is_empty() { let document = fixture.workspace.lookup(&spec.uri).unwrap(); - expected.insert(document, spec.ranges.clone()); + expected.insert( + document, + spec.ranges.iter().map(|r| r.clone().into()).collect(), + ); } } - let (feature, offset) = fixture.make_params().unwrap(); let actual = crate::rename(RenameParams { feature, offset }); assert_eq!(actual.changes, expected); } +fn check(input: &str) { + check_with_syntax_config(SyntaxConfig::default(), input) +} + #[test] fn test_command() { check( @@ -78,12 +92,97 @@ fn test_label() { | ^^^ +%! baz.tex +\ref{foo} + %! bar.tex \ref{foo} ^^^ +"#, + ) +} + +#[test] +fn test_custom_label_ref() { + let mut config = SyntaxConfig::default(); + config + .label_definition_commands + .extend(vec!["asm", "goal"].into_iter().map(String::from)); + config.label_definition_prefixes.extend( + vec![("asm", "asm:"), ("goal", "goal:")] + .into_iter() + .map(|(x, y)| (String::from(x), String::from(y))), + ); + config + .label_reference_commands + .extend(vec!["asmref", "goalref"].into_iter().map(String::from)); + config.label_reference_prefixes.extend( + vec![("asmref", "asm:"), ("goalref", "goal:")] + .into_iter() + .map(|(x, y)| (String::from(x), String::from(y))), + ); + check_with_syntax_config( + config, + r#" +%! foo.tex +\goal{foo} + +\asm{foo}\include{bar}\include{baz} + | + ^^^ + +%! bar.tex +\asmref{foo} + ^^^ + +%! baz.tex +\ref{foo} + +\ref{asm:foo} + ^^^^^^^ + +"#, + ) +} + +#[test] +fn test_custom_label_def() { + let mut config = SyntaxConfig::default(); + config + .label_definition_commands + .extend(vec!["asm", "goal"].into_iter().map(String::from)); + config.label_definition_prefixes.extend( + vec![("asm", "asm:"), ("goal", "goal:")] + .into_iter() + .map(|(x, y)| (String::from(x), String::from(y))), + ); + config + .label_reference_commands + .extend(vec!["asmref", "goalref"].into_iter().map(String::from)); + config.label_reference_prefixes.extend( + vec![("asmref", "asm:"), ("goalref", "goal:")] + .into_iter() + .map(|(x, y)| (String::from(x), String::from(y))), + ); + check_with_syntax_config( + config, + r#" +%! foo.tex +\goal{foo} + +\label{asm:foo}\include{bar}\include{baz} + | + ^^^^^^^ + +%! bar.tex +\asmref{foo} + ^^^ %! baz.tex \ref{foo} + +\ref{asm:foo} + ^^^^^^^ "#, ) } diff --git a/support/texlab/crates/symbols/Cargo.toml b/support/texlab/crates/symbols/Cargo.toml index 65db0fc0bd..3ebc88fa50 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.1" +itertools.workspace = true line-index = { path = "../line-index" } -rowan = "0.15.15" +rowan.workspace = true syntax = { path = "../syntax" } -titlecase = "3.0.0" -url = "2.5.0" +titlecase.workspace = true +url.workspace = true [dev-dependencies] -regex = "1.10.4" +regex.workspace = true test-utils = { path = "../test-utils" } -expect-test = "1.5.0" +expect-test.workspace = true diff --git a/support/texlab/crates/symbols/src/document.rs b/support/texlab/crates/symbols/src/document.rs index 791f119919..611a7e0370 100644 --- a/support/texlab/crates/symbols/src/document.rs +++ b/support/texlab/crates/symbols/src/document.rs @@ -23,6 +23,7 @@ pub fn document_symbols(workspace: &Workspace, document: &Document) -> Vec<Symbo | DocumentData::Log(_) | DocumentData::Root | DocumentData::Latexmkrc(_) + | DocumentData::FileList(_) | DocumentData::Tectonic => Vec::new(), }; diff --git a/support/texlab/crates/symbols/src/document/tests.rs b/support/texlab/crates/symbols/src/document/tests.rs index 25450a6cc5..cf599529fb 100644 --- a/support/texlab/crates/symbols/src/document/tests.rs +++ b/support/texlab/crates/symbols/src/document/tests.rs @@ -281,44 +281,44 @@ fn test_section() { check( &fixture, expect![[r#" - [ - Symbol { - name: "Foo", - kind: Section, - label: None, - full_range: 43..56, - selection_range: 43..56, - children: [], - }, - Symbol { - name: "2 Bar", - kind: Section, - label: Some( - Span( - "sec:bar", - 71..86, + [ + Symbol { + name: "1 Foo", + kind: Section, + label: None, + full_range: 43..56, + selection_range: 43..56, + children: [], + }, + Symbol { + name: "2 Bar", + kind: Section, + label: Some( + Span( + "sec:bar", + 71..86, + ), ), - ), - full_range: 58..119, - selection_range: 71..86, - children: [ - Symbol { - name: "Baz", - kind: Section, - label: Some( - Span( - "sec:baz", - 104..119, + full_range: 58..119, + selection_range: 71..86, + children: [ + Symbol { + name: "Baz", + kind: Section, + label: Some( + Span( + "sec:baz", + 104..119, + ), ), - ), - full_range: 88..119, - selection_range: 104..119, - children: [], - }, - ], - }, - ] - "#]], + full_range: 88..119, + selection_range: 104..119, + children: [], + }, + ], + }, + ] + "#]], ); } diff --git a/support/texlab/crates/symbols/src/document/tex.rs b/support/texlab/crates/symbols/src/document/tex.rs index e0df9c85c1..734335ef0e 100644 --- a/support/texlab/crates/symbols/src/document/tex.rs +++ b/support/texlab/crates/symbols/src/document/tex.rs @@ -67,16 +67,14 @@ impl<'a> SymbolBuilder<'a> { let group_text = group.content_text()?; let kind = SymbolKind::Section; - let symbol = match self.find_label(section.syntax()) { - Some(label) => { - let name = match self.find_label_number(&label.text) { - Some(number) => format!("{number} {group_text}"), - None => group_text, - }; + let name = match self.find_section_number(&group_text) { + Some(number) => format!("{number} {group_text}"), + None => group_text, + }; - Symbol::new_label(name, kind, range, label) - } - None => Symbol::new_simple(group_text, kind, range, range), + let symbol = match self.find_label(section.syntax()) { + Some(label) => Symbol::new_label(name, kind, range, label), + None => Symbol::new_simple(name, kind, range, range) }; Some(symbol) @@ -247,6 +245,15 @@ impl<'a> SymbolBuilder<'a> { Some(Span { text, range }) } + fn find_section_number(&self, name: &str) -> Option<&str> { + self.project + .documents + .iter() + .filter_map(|document| document.data.as_aux()) + .find_map(|data| data.semantics.section_numbers.get(name)) + .map(String::as_str) + } + fn find_label_number(&self, name: &str) -> Option<&str> { self.project .documents diff --git a/support/texlab/crates/syntax/Cargo.toml b/support/texlab/crates/syntax/Cargo.toml index a2d3d866b6..5ae82f8bc5 100644 --- a/support/texlab/crates/syntax/Cargo.toml +++ b/support/texlab/crates/syntax/Cargo.toml @@ -7,8 +7,9 @@ edition.workspace = true rust-version.workspace = true [dependencies] -itertools = "0.12.1" -rowan = "0.15.15" +itertools.workspace = true +rowan.workspace = true +rustc-hash.workspace = true [lib] doctest = false diff --git a/support/texlab/crates/syntax/src/file_list.rs b/support/texlab/crates/syntax/src/file_list.rs new file mode 100644 index 0000000000..cd32e787da --- /dev/null +++ b/support/texlab/crates/syntax/src/file_list.rs @@ -0,0 +1,10 @@ +use std::path::PathBuf; + +use rustc_hash::FxHashSet; + +#[derive(Debug, PartialEq, Eq, Clone, Default)] +pub struct FileList { + pub working_dir: Option<PathBuf>, + pub inputs: FxHashSet<PathBuf>, + pub outputs: FxHashSet<PathBuf>, +} diff --git a/support/texlab/crates/syntax/src/latex/cst.rs b/support/texlab/crates/syntax/src/latex/cst.rs index f16d703162..580ef6af12 100644 --- a/support/texlab/crates/syntax/src/latex/cst.rs +++ b/support/texlab/crates/syntax/src/latex/cst.rs @@ -12,7 +12,7 @@ pub fn small_range(node: &dyn AstNode<Language = LatexLanguage>) -> TextRange { let start = full_range.start(); let mut token = node.syntax().last_token(); while let Some(current) = token { - if !matches!(current.kind(), LINE_BREAK | WHITESPACE | COMMENT) { + if !matches!(current.kind(), WHITESPACE | COMMENT) { return TextRange::new(start, current.text_range().end()); } token = current.prev_token(); @@ -233,7 +233,7 @@ impl Key { self.syntax() .children_with_tokens() .filter_map(|node| node.into_token()) - .filter(|node| !matches!(node.kind(), WHITESPACE | LINE_BREAK | COMMENT)) + .filter(|node| !matches!(node.kind(), WHITESPACE | COMMENT)) } } @@ -258,7 +258,7 @@ impl ToString for Key { .children_with_tokens() .filter_map(|node| node.into_token()) { - if matches!(token.kind(), WHITESPACE | LINE_BREAK | COMMENT) { + if matches!(token.kind(), WHITESPACE | COMMENT) { buf.push(' '); } else { buf.push_str(token.text()); @@ -750,3 +750,43 @@ impl GraphicsPath { self.syntax().descendants().filter_map(CurlyGroupWord::cast) } } + +cst_node!(BibItem, BIBITEM); + +impl BibItem { + pub fn command(&self) -> Option<SyntaxToken> { + self.syntax().first_token() + } + + pub fn name(&self) -> Option<CurlyGroupWord> { + self.syntax().children().find_map(CurlyGroupWord::cast) + } +} + +cst_node!(TocContentsLine, TOC_CONTENTS_LINE); + +impl TocContentsLine { + pub fn command(&self) -> Option<SyntaxToken> { + self.syntax().first_token() + } + + pub fn unit(&self) -> Option<CurlyGroup> { + self.syntax().children().find_map(CurlyGroup::cast) + } + + pub fn line(&self) -> Option<CurlyGroup> { + self.syntax().children().filter_map(CurlyGroup::cast).nth(1) + } +} + +cst_node!(TocNumberLine, TOC_NUMBER_LINE); + +impl TocNumberLine { + pub fn command(&self) -> Option<SyntaxToken> { + self.syntax().first_token() + } + + pub fn number(&self) -> Option<CurlyGroup> { + self.syntax().children().find_map(CurlyGroup::cast) + } +} diff --git a/support/texlab/crates/syntax/src/latex/kind.rs b/support/texlab/crates/syntax/src/latex/kind.rs index 8b649b1f31..f67201e003 100644 --- a/support/texlab/crates/syntax/src/latex/kind.rs +++ b/support/texlab/crates/syntax/src/latex/kind.rs @@ -4,7 +4,6 @@ pub enum SyntaxKind { ERROR = 0, - LINE_BREAK, WHITESPACE, COMMENT, VERBATIM, @@ -83,6 +82,9 @@ pub enum SyntaxKind { ENVIRONMENT_DEFINITION, GRAPHICS_PATH, BLOCK_COMMENT, + BIBITEM, + TOC_CONTENTS_LINE, + TOC_NUMBER_LINE, ROOT, } diff --git a/support/texlab/crates/syntax/src/lib.rs b/support/texlab/crates/syntax/src/lib.rs index 35f1bb8c52..d3e2098757 100644 --- a/support/texlab/crates/syntax/src/lib.rs +++ b/support/texlab/crates/syntax/src/lib.rs @@ -1,4 +1,5 @@ pub mod bibtex; +pub mod file_list; pub mod latex; pub mod latexmkrc; diff --git a/support/texlab/crates/test-utils/Cargo.toml b/support/texlab/crates/test-utils/Cargo.toml index b08d9cc09f..581be2e6a2 100644 --- a/support/texlab/crates/test-utils/Cargo.toml +++ b/support/texlab/crates/test-utils/Cargo.toml @@ -10,8 +10,8 @@ rust-version.workspace = true base-db = { path = "../base-db" } distro = { path = "../distro" } line-index = { path = "../line-index" } -rowan = "0.15.15" -url = "2.5.0" +rowan.workspace = true +url.workspace = true [lib] doctest = false diff --git a/support/texlab/crates/texlab/Cargo.toml b/support/texlab/crates/texlab/Cargo.toml index a2fb78e11f..5f73cbf024 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.16.1" +version.workspace = true license.workspace = true readme = "README.md" authors.workspace = true @@ -22,19 +22,19 @@ test = false doctest = false [dependencies] -anyhow = "1.0.86" +anyhow.workspace = true base-db = { path = "../base-db" } bibfmt = { path = "../bibfmt" } citeproc = { path = "../citeproc" } -clap = { version = "4.5.4", features = ["derive"] } +clap = { version = "4.5.20", features = ["derive"] } commands = { path = "../commands" } completion = { path = "../completion" } completion-data = { path = "../completion-data" } -crossbeam-channel = "0.5.12" +crossbeam-channel.workspace = true definition = { path = "../definition" } diagnostics = { path = "../diagnostics" } distro = { path = "../distro" } -fern = "0.6.2" +fern = "0.7.0" folding = { path = "../folding" } highlights = { path = "../highlights" } hover = { path = "../hover" } @@ -42,29 +42,29 @@ 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" +log.workspace = true +lsp-server = "0.7.7" lsp-types = "0.95.1" -notify = "6.1.1" -notify-debouncer-full = "0.3.1" -parking_lot = "0.12.2" +notify.workspace = true +notify-debouncer-full = "0.3.2" +parking_lot = "0.12.3" parser = { path = "../parser" } references = { path = "../references" } -regex = "1.10.4" +regex.workspace = true rename = { path = "../rename" } -rowan = "0.15.15" -rustc-hash = "1.1.0" -serde = "1.0.202" -serde_json = "1.0.117" +rowan.workspace = true +rustc-hash.workspace = true +serde.workspace = true +serde_json.workspace = true serde_regex = "1.1.0" serde_repr = "0.1.19" symbols = { path = "../symbols" } syntax = { path = "../syntax" } -tempfile = "3.10.1" +tempfile.workspace = true threadpool = "1.8.1" [dev-dependencies] -criterion = { version = "0.5.1" } +criterion.workspace = true [[bench]] name = "bench_main" diff --git a/support/texlab/crates/texlab/src/features/formatting.rs b/support/texlab/crates/texlab/src/features/formatting.rs index 3fcd66e39b..9d152f6e8b 100644 --- a/support/texlab/crates/texlab/src/features/formatting.rs +++ b/support/texlab/crates/texlab/src/features/formatting.rs @@ -27,6 +27,7 @@ pub fn format_source_code( | Language::Log | Language::Root | Language::Latexmkrc - | Language::Tectonic => None, + | Language::Tectonic + | Language::FileList => None, } } diff --git a/support/texlab/crates/texlab/src/features/formatting/latexindent.rs b/support/texlab/crates/texlab/src/features/formatting/latexindent.rs index d3fffc2050..b956378ed5 100644 --- a/support/texlab/crates/texlab/src/features/formatting/latexindent.rs +++ b/support/texlab/crates/texlab/src/features/formatting/latexindent.rs @@ -16,7 +16,7 @@ pub fn format_with_latexindent( ) -> Option<Vec<lsp_types::TextEdit>> { let config = workspace.config(); let target_dir = tempdir().ok()?; - let root = ProjectRoot::walk_and_find(workspace, &document.dir); + let root = ProjectRoot::walk_and_find(workspace, document.dir.as_ref()?); let source_dir = root.src_dir.to_file_path().ok()?; let target_file = target_dir @@ -72,6 +72,15 @@ fn build_arguments(config: &LatexIndentConfig, target_file: &Path) -> Vec<String args.push("--modifylinebreaks".to_string()); } + match &config.replacement { + Some(replacement_flag) => { + if ["-r", "-rv", "-rr"].contains(& replacement_flag.as_str()) { + args.push(replacement_flag.clone()); + } + }, + None => {} + } + args.push(target_file.display().to_string()); args } diff --git a/support/texlab/crates/texlab/src/features/inlay_hint.rs b/support/texlab/crates/texlab/src/features/inlay_hint.rs index ddd8ff26d8..0689f07af0 100644 --- a/support/texlab/crates/texlab/src/features/inlay_hint.rs +++ b/support/texlab/crates/texlab/src/features/inlay_hint.rs @@ -7,10 +7,11 @@ pub fn find_all( params: lsp_types::InlayHintParams, ) -> Option<Vec<lsp_types::InlayHint>> { let params = from_proto::inlay_hint_params(workspace, params)?; + let line_index = ¶ms.feature.document.line_index; + let max_length = workspace.config().inlay_hints.max_length; let hints = inlay_hints::find_all(¶ms)? .into_iter() - .filter_map(|hint| to_proto::inlay_hint(hint, ¶ms.feature.document.line_index)) - .collect(); + .filter_map(|hint| to_proto::inlay_hint(hint, line_index, max_length)); - Some(hints) + Some(hints.collect()) } diff --git a/support/texlab/crates/texlab/src/server/options.rs b/support/texlab/crates/texlab/src/server/options.rs index 3f50faefd7..fcb8e1ec60 100644 --- a/support/texlab/crates/texlab/src/server/options.rs +++ b/support/texlab/crates/texlab/src/server/options.rs @@ -1,6 +1,7 @@ -use regex::Regex; use serde::{Deserialize, Serialize}; +use regex::Regex; + #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] #[serde(default)] @@ -55,6 +56,7 @@ impl Default for LatexFormatter { pub struct LatexindentOptions { pub local: Option<String>, pub modify_line_breaks: bool, + pub replacement: Option<String>, } #[derive(Debug, PartialEq, Eq, Clone, Default, Serialize, Deserialize)] @@ -69,6 +71,7 @@ pub struct BuildOptions { pub log_directory: Option<String>, pub pdf_directory: Option<String>, pub filename: Option<String>, + pub use_file_list: bool, } #[derive(Debug, PartialEq, Eq, Clone, Default, Serialize, Deserialize)] @@ -102,6 +105,7 @@ pub struct DiagnosticsOptions { pub struct InlayHintOptions { pub label_definitions: Option<bool>, pub label_references: Option<bool>, + pub max_length: Option<usize>, } #[derive(Debug, Clone, Default, Serialize, Deserialize)] @@ -125,7 +129,9 @@ pub struct ExperimentalOptions { pub verbatim_environments: Vec<String>, pub citation_commands: Vec<String>, pub label_definition_commands: Vec<String>, + pub label_definition_prefixes: Vec<(String, String)>, pub label_reference_commands: Vec<String>, + pub label_reference_prefixes: Vec<(String, String)>, } #[derive(Debug, PartialEq, Eq, Clone, Default, Serialize, Deserialize)] diff --git a/support/texlab/crates/texlab/src/util.rs b/support/texlab/crates/texlab/src/util.rs index c301dc7722..87e41a78ae 100644 --- a/support/texlab/crates/texlab/src/util.rs +++ b/support/texlab/crates/texlab/src/util.rs @@ -18,9 +18,28 @@ pub fn normalize_uri(uri: &mut lsp_types::Url) { } } + uri.set_path(&normalize_percent_encoding(uri.path())); uri.set_fragment(None); } +fn normalize_percent_encoding(input: &str) -> String { + let mut output = String::new(); + let mut i = 0; + for c in input.chars() { + if c == '%' && i == 0 { + output.push(c); + i = 2; + } else if i > 0 { + output.push(c.to_ascii_uppercase()); + i -= 1; + } else { + output.push(c); + } + } + + output +} + fn fix_drive_letter(text: &str) -> Option<String> { if !text.is_ascii() || text.is_empty() { return None; @@ -59,4 +78,44 @@ mod tests { normalize_uri(&mut uri); assert_eq!(uri.as_str(), "foo:///bar/baz.txt"); } + + #[test] + #[cfg(unix)] + fn test_lowercase_percent_encoding_unix() { + let mut uri1 = Url::parse("file:///foo/%c3%a4.tex").unwrap(); + let uri2 = Url::from_file_path("/foo/ä.tex").unwrap(); + + normalize_uri(&mut uri1); + assert_eq!(uri1, uri2); + } + + #[test] + #[cfg(unix)] + fn test_uppercase_percent_encoding_unix() { + let mut uri1 = Url::parse("file:///foo/%C3%A4.tex").unwrap(); + let uri2 = Url::from_file_path("/foo/ä.tex").unwrap(); + + normalize_uri(&mut uri1); + assert_eq!(uri1, uri2); + } + + #[test] + #[cfg(windows)] + fn test_lowercase_percent_encoding_windows() { + let mut uri1 = Url::parse("file:///c%3a/foo/%c3%a4.tex").unwrap(); + let uri2 = Url::from_file_path("C:/foo/ä.tex").unwrap(); + + normalize_uri(&mut uri1); + assert_eq!(uri1, uri2); + } + + #[test] + #[cfg(windows)] + fn test_uppercase_percent_encoding_windows() { + let mut uri1 = Url::parse("file:///c%3A/foo/%C3%A4.tex").unwrap(); + let uri2 = Url::from_file_path("C:/foo/ä.tex").unwrap(); + + normalize_uri(&mut uri1); + assert_eq!(uri1, uri2); + } } diff --git a/support/texlab/crates/texlab/src/util/client_flags.rs b/support/texlab/crates/texlab/src/util/client_flags.rs index 842dbb1eae..2829041832 100644 --- a/support/texlab/crates/texlab/src/util/client_flags.rs +++ b/support/texlab/crates/texlab/src/util/client_flags.rs @@ -20,18 +20,12 @@ pub struct ClientFlags { /// If not set, then fuzzy citation completion will not work. pub completion_always_incomplete: bool, - /// If `true`, the server can include markdown in hover responses. - pub hover_markdown: bool, - /// If `true`, the server can pull the configuration from the client. pub configuration_pull: bool, /// If `true`, the client notifies the server when the configuration changes. pub configuration_push: bool, - /// If `true`, the client can return `LocationLink` instead of `Location`. - pub definition_link: bool, - /// If `true`, the server can return custom kinds like `section`. pub folding_custom_kinds: bool, diff --git a/support/texlab/crates/texlab/src/util/from_proto.rs b/support/texlab/crates/texlab/src/util/from_proto.rs index 789edbf9c3..928b3d6d61 100644 --- a/support/texlab/crates/texlab/src/util/from_proto.rs +++ b/support/texlab/crates/texlab/src/util/from_proto.rs @@ -56,15 +56,6 @@ pub fn client_flags( let completion_always_incomplete = info.map_or(false, |info| info.name == "Visual Studio Code"); - let hover_markdown = capabilities - .text_document - .as_ref() - .and_then(|cap| cap.hover.as_ref()) - .and_then(|cap| cap.content_format.as_ref()) - .map_or(false, |formats| { - formats.contains(&lsp_types::MarkupKind::Markdown) - }); - let configuration_pull = capabilities .workspace .as_ref() @@ -78,13 +69,6 @@ pub fn client_flags( .and_then(|cap| cap.dynamic_registration) .unwrap_or(false); - let definition_link = capabilities - .text_document - .as_ref() - .and_then(|cap| cap.definition) - .and_then(|cap| cap.link_support) - .unwrap_or(false); - let folding_custom_kinds = capabilities .text_document .as_ref() @@ -111,10 +95,8 @@ pub fn client_flags( completion_snippets, completion_kinds, completion_always_incomplete, - hover_markdown, configuration_pull, configuration_push, - definition_link, folding_custom_kinds, progress, show_document, @@ -300,6 +282,7 @@ pub fn config(value: Options) -> Config { config.formatting.latex_indent.local = value.latexindent.local; config.formatting.latex_indent.modify_line_breaks = value.latexindent.modify_line_breaks; + config.formatting.latex_indent.replacement = value.latexindent.replacement; config.synctex = value .forward_search @@ -323,6 +306,7 @@ pub fn config(value: Options) -> Config { 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.inlay_hints.max_length = value.inlay_hints.max_length; config.completion.matcher = match value.completion.matcher { CompletionMatcher::Fuzzy => base_db::MatchingAlgo::Skim, @@ -331,6 +315,8 @@ pub fn config(value: Options) -> Config { CompletionMatcher::PrefixIgnoreCase => base_db::MatchingAlgo::PrefixIgnoreCase, }; + config.syntax.use_file_list = value.build.use_file_list; + config .syntax .math_environments @@ -358,8 +344,18 @@ pub fn config(value: Options) -> Config { config .syntax + .label_definition_prefixes + .extend(value.experimental.label_definition_prefixes); + + config + .syntax .label_reference_commands .extend(value.experimental.label_reference_commands); config + .syntax + .label_reference_prefixes + .extend(value.experimental.label_reference_prefixes); + + config } diff --git a/support/texlab/crates/texlab/src/util/to_proto.rs b/support/texlab/crates/texlab/src/util/to_proto.rs index f5cf2edf8b..82d0132434 100644 --- a/support/texlab/crates/texlab/src/util/to_proto.rs +++ b/support/texlab/crates/texlab/src/util/to_proto.rs @@ -191,13 +191,25 @@ pub fn diagnostic( }) } -pub fn inlay_hint(hint: InlayHint, line_index: &LineIndex) -> Option<lsp_types::InlayHint> { +pub fn inlay_hint( + hint: InlayHint, + line_index: &LineIndex, + max_length: Option<usize>, +) -> Option<lsp_types::InlayHint> { let position = line_index.line_col_lsp(hint.offset)?; + let trim_text = |text: &mut String| match max_length { + Some(max_length) if text.len() > max_length => { + text.truncate(max_length); + text.push('…'); + } + _ => {} + }; + Some(match hint.data { InlayHintData::LabelDefinition(label) => { let number = label.number?; - let text = match &label.object { + let mut text = match &label.object { RenderedObject::Section { prefix, .. } => { format!("{} {}", prefix, number) } @@ -211,9 +223,11 @@ pub fn inlay_hint(hint: InlayHint, line_index: &LineIndex) -> Option<lsp_types:: RenderedObject::EnumItem => format!("Item {}", number), }; + trim_text(&mut text); + lsp_types::InlayHint { position, - label: lsp_types::InlayHintLabel::String(format!(" {text} ")), + label: lsp_types::InlayHintLabel::String(format!(" {text} ",)), kind: None, text_edits: None, tooltip: None, @@ -223,7 +237,8 @@ pub fn inlay_hint(hint: InlayHint, line_index: &LineIndex) -> Option<lsp_types:: } } InlayHintData::LabelReference(label) => { - let text = label.reference(); + let mut text = label.reference(); + trim_text(&mut text); lsp_types::InlayHint { position, @@ -396,8 +411,15 @@ pub fn workspace_edit(result: RenameResult, new_name: &str) -> lsp_types::Worksp let mut edits = Vec::new(); ranges .into_iter() - .filter_map(|range| document.line_index.line_col_lsp_range(range)) - .for_each(|range| edits.push(lsp_types::TextEdit::new(range, new_name.into()))); + .filter_map(|info| { + document.line_index.line_col_lsp_range(info.range).map(|i| { + ( + i, + info.prefix.map_or(new_name.into(), |p| p + new_name.into()), + ) + }) + }) + .for_each(|(range, new_name)| edits.push(lsp_types::TextEdit::new(range, new_name))); changes.insert(document.uri.clone(), edits); } diff --git a/support/texlab/texlab.1 b/support/texlab/texlab.1 index 69f16a0bf9..27fabe2066 100644 --- a/support/texlab/texlab.1 +++ b/support/texlab/texlab.1 @@ -1,7 +1,7 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.1. -.TH TEXLAB "1" "May 2024" "texlab 5.16.1" "User Commands" +.TH TEXLAB "1" "October 2024" "texlab 5.21.0" "User Commands" .SH NAME -texlab \- manual page for texlab 5.16.1 +texlab \- manual page for texlab 5.21.0 .SH SYNOPSIS .B texlab [\fI\,OPTIONS\/\fR] [\fI\,COMMAND\/\fR] diff --git a/support/texlab/texlab.pdf b/support/texlab/texlab.pdf Binary files differindex 8731c5ef78..152ab2bac4 100644 --- a/support/texlab/texlab.pdf +++ b/support/texlab/texlab.pdf |