diff options
Diffstat (limited to 'support/texlab')
87 files changed, 1429 insertions, 483 deletions
diff --git a/support/texlab/CHANGELOG.md b/support/texlab/CHANGELOG.md index 0a3b5ed683..f2fa8d96cf 100644 --- a/support/texlab/CHANGELOG.md +++ b/support/texlab/CHANGELOG.md @@ -5,6 +5,46 @@ 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.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 + +- Don't treat `\label{}` and `\ref{}` with macro parameter as label commands ([#1119](https://github.com/latex-lsp/texlab/issues/1119)) +- Fix parsing `latexmkrc` for `latexmk` versions older than `v4.8.4` ([#1128](https://github.com/latex-lsp/texlab/issues/1128)) +- Remove unused `texlab.rootDirectory` setting from documentation + ## [5.16.0] - 2024-05-01 ### Added diff --git a/support/texlab/Cargo.lock b/support/texlab/Cargo.lock index 80599217fd..ead2473278 100644 --- a/support/texlab/Cargo.lock +++ b/support/texlab/Cargo.lock @@ -73,9 +73,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.82" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[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", ] @@ -190,9 +190,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.31" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ "num-traits", ] @@ -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.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +checksum = "84b3edb18336f4df585bc9aa31dd99c036dfa5dc5e9a2939a722a188f3a8970d" dependencies = [ "clap_builder", "clap_derive", @@ -254,9 +254,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.2" +version = "4.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +checksum = "c1c09dd5ada6c6c78075d6fd0da3f90d8080651e2d6cc8eb2f1aaa4034ced708" dependencies = [ "anstream", "anstyle", @@ -266,9 +266,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.4" +version = "4.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085" 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]] @@ -594,9 +595,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.29" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4556222738635b7a3417ae6130d8f52201e45a0c4d1a907f0826383adb5f85e7" +checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" dependencies = [ "crc32fast", "miniz_oxide", @@ -741,7 +742,7 @@ dependencies = [ "base-db", "expect-test", "rowan", - "rustc-hash", + "rustc-hash 2.0.0", "test-utils", ] @@ -810,9 +811,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 +861,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.153" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "line-index" version = "0.0.0" dependencies = [ - "rustc-hash", + "rustc-hash 2.0.0", "text-size", ] @@ -899,9 +900,9 @@ 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" @@ -1094,9 +1095,9 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -1126,7 +1127,7 @@ dependencies = [ "pathdiff", "regex", "rowan", - "rustc-hash", + "rustc-hash 2.0.0", "syntax", "tempfile", "versions", @@ -1298,16 +1299,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.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", @@ -1337,8 +1338,9 @@ name = "rename" version = "0.0.0" dependencies = [ "base-db", + "parser", "rowan", - "rustc-hash", + "rustc-hash 2.0.0", "syntax", "test-utils", ] @@ -1352,7 +1354,7 @@ dependencies = [ "countme", "hashbrown", "memoffset", - "rustc-hash", + "rustc-hash 1.1.0", "text-size", ] @@ -1363,6 +1365,12 @@ 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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1404,18 +1412,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.199" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.199" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", @@ -1424,9 +1432,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.116" +version = "1.0.119" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" +checksum = "e8eddb61f0697cc3989c5d64b452f5488e2b8a60fd7d5076a3045076ffef8cb0" dependencies = [ "itoa", "ryu", @@ -1445,9 +1453,9 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", @@ -1494,7 +1502,7 @@ dependencies = [ "base-db", "distro", "expect-test", - "itertools 0.12.1", + "itertools 0.13.0", "line-index", "regex", "rowan", @@ -1519,8 +1527,9 @@ dependencies = [ name = "syntax" version = "0.0.0" dependencies = [ - "itertools 0.12.1", + "itertools 0.13.0", "rowan", + "rustc-hash 2.0.0", ] [[package]] @@ -1548,7 +1557,7 @@ dependencies = [ [[package]] name = "texlab" -version = "5.16.0" +version = "5.19.0" dependencies = [ "anyhow", "base-db", @@ -1582,7 +1591,7 @@ dependencies = [ "regex", "rename", "rowan", - "rustc-hash", + "rustc-hash 2.0.0", "serde", "serde_json", "serde_regex", @@ -1601,18 +1610,18 @@ checksum = "f18aa187839b2bdb1ad2fa35ead8c4c2976b64e4363c386d45ac0f7ee85c9233" [[package]] name = "thiserror" -version = "1.0.59" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.59" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", @@ -1742,11 +1751,11 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "versions" -version = "6.2.0" +version = "6.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38a8931f8d167b6448076020e70b9de46dcf5ea1731212481a092d0071c4ac5b" +checksum = "5fc28d1172a20e32754969ea1a873c2c6e68e36c449c6056aa3e2ee5fe69a794" dependencies = [ - "itertools 0.12.1", + "itertools 0.13.0", "nom", ] diff --git a/support/texlab/crates/base-db/Cargo.toml b/support/texlab/crates/base-db/Cargo.toml index ca477c496c..547ba2ad77 100644 --- a/support/texlab/crates/base-db/Cargo.toml +++ b/support/texlab/crates/base-db/Cargo.toml @@ -10,16 +10,16 @@ rust-version.workspace = true bibtex-utils = { path = "../bibtex-utils" } dirs = "5.0.1" distro = { path = "../distro" } -itertools = "0.12.1" +itertools = "0.13.0" line-index = { path = "../line-index" } -log = "0.4.21" +log = "0.4.22" notify = "6.0.1" once_cell = "1.19.0" parser = { path = "../parser" } percent-encoding = "2.3.0" -regex = "1.10.4" +regex = "1.10.5" rowan = "0.15.15" -rustc-hash = "1.1.0" +rustc-hash = "2.0.0" shellexpand = "3.1.0" syntax = { path = "../syntax" } titlecase = "3.0.0" diff --git a/support/texlab/crates/base-db/src/config.rs b/support/texlab/crates/base-db/src/config.rs index 9814e3e674..e4c827e61c 100644 --- a/support/texlab/crates/base-db/src/config.rs +++ b/support/texlab/crates/base-db/src/config.rs @@ -3,9 +3,8 @@ use std::time::Duration; use parser::SyntaxConfig; use regex::Regex; -#[derive(Debug)] +#[derive(Debug, Default)] pub struct Config { - pub root_dir: Option<String>, pub build: BuildConfig, pub diagnostics: DiagnosticsConfig, pub formatting: FormattingConfig, @@ -36,7 +35,7 @@ pub struct DiagnosticsConfig { pub delay: Duration, } -#[derive(Debug)] +#[derive(Debug, Default)] pub struct ChktexConfig { pub on_open: bool, pub on_save: bool, @@ -65,13 +64,13 @@ pub enum Formatter { LatexIndent, } -#[derive(Debug)] +#[derive(Debug, Default)] pub struct LatexIndentConfig { pub local: Option<String>, pub modify_line_breaks: bool, } -#[derive(Debug)] +#[derive(Debug, Default)] pub struct SymbolConfig { pub allowed_patterns: Vec<Regex>, pub ignored_patterns: Vec<Regex>, @@ -96,22 +95,6 @@ pub enum MatchingAlgo { PrefixIgnoreCase, } -impl Default for Config { - fn default() -> Self { - Self { - root_dir: None, - build: BuildConfig::default(), - diagnostics: DiagnosticsConfig::default(), - formatting: FormattingConfig::default(), - synctex: None, - symbols: SymbolConfig::default(), - syntax: SyntaxConfig::default(), - completion: CompletionConfig::default(), - inlay_hints: InlayHintConfig::default(), - } - } -} - impl Default for BuildConfig { fn default() -> Self { Self { @@ -141,17 +124,6 @@ impl Default for DiagnosticsConfig { } } -impl Default for ChktexConfig { - fn default() -> Self { - Self { - on_open: false, - on_save: false, - on_edit: false, - additional_args: Vec::new(), - } - } -} - impl Default for FormattingConfig { fn default() -> Self { Self { @@ -163,24 +135,6 @@ impl Default for FormattingConfig { } } -impl Default for LatexIndentConfig { - fn default() -> Self { - Self { - local: None, - modify_line_breaks: false, - } - } -} - -impl Default for SymbolConfig { - fn default() -> Self { - Self { - allowed_patterns: Vec::new(), - ignored_patterns: Vec::new(), - } - } -} - impl Default for InlayHintConfig { fn default() -> Self { Self { diff --git a/support/texlab/crates/base-db/src/deps/graph.rs b/support/texlab/crates/base-db/src/deps/graph.rs index e07368d078..0d8252688d 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; @@ -22,7 +22,8 @@ pub struct Edge { #[derive(Debug, PartialEq, Eq, Clone, Hash)] pub enum EdgeData { - DirectLink(DirectLinkData), + DirectLink(Box<DirectLinkData>), + FileList(Arc<ProjectRoot>), AdditionalFiles, Artifact, } @@ -74,6 +75,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 +103,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 +126,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().unwrap_or(&start.source.dir); + 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()?; @@ -159,7 +202,7 @@ impl Graph { for target_uri in file_names .iter() .flat_map(|file_name| { - util::expand_relative_path(&file_name, &start.root.src_dir, workspace.folders()) + util::expand_relative_path(file_name, &start.root.src_dir, workspace.folders()) }) .chain(distro_files) { @@ -169,7 +212,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(), @@ -179,7 +222,7 @@ impl Graph { self.edges.push(Edge { source: start.source.uri.clone(), target: target.uri.clone(), - data: EdgeData::DirectLink(link_data), + data: EdgeData::DirectLink(Box::new(link_data)), }); break; @@ -197,27 +240,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 a8cec6604a..03fd41a9a0 100644 --- a/support/texlab/crates/base-db/src/deps/root.rs +++ b/support/texlab/crates/base-db/src/deps/root.rs @@ -31,11 +31,11 @@ impl ProjectRoot { } let Ok(parent) = current.join("..") else { - break Self::from_config(workspace, &dir); + break Self::from_config(workspace, dir); }; if current == parent || Some(&parent) == home_dir.as_ref() { - break Self::from_config(workspace, &dir); + break Self::from_config(workspace, dir); } current = parent; diff --git a/support/texlab/crates/base-db/src/document.rs b/support/texlab/crates/base-db/src/document.rs index 7a388129fd..6fdcf30c16 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}; @@ -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 489a7d1ac6..8c9fe012e6 100644 --- a/support/texlab/crates/base-db/src/semantics/auxiliary.rs +++ b/support/texlab/crates/base-db/src/semantics/auxiliary.rs @@ -36,7 +36,7 @@ impl Semantics { Some(group) })?; - let text = group.content_text()?.replace('{', "").replace('}', ""); + let text = group.content_text()?.replace(['{', '}'], ""); self.label_numbers.insert(name, text); Some(()) } diff --git a/support/texlab/crates/base-db/src/semantics/tex.rs b/support/texlab/crates/base-db/src/semantics/tex.rs index 1ad8969a80..a1301091d4 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 { @@ -19,11 +40,11 @@ pub struct Semantics { } 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 { @@ -40,17 +61,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()) { @@ -111,11 +132,16 @@ 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; }; + let name = Span::from(&name); + if name.text.contains('#') { + return; + } + let full_range = latex::small_range(&label); let mut objects = Vec::new(); for node in label.syntax().ancestors() { @@ -185,46 +211,75 @@ impl Semantics { self.labels.push(Label { kind: LabelKind::Definition, - name: Span::from(&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; }; let full_range = latex::small_range(&label); for name in name_list.keys() { - self.labels.push(Label { - kind: LabelKind::Reference, - name: Span::from(&name), - targets: Vec::new(), - full_range, - }); + let name = Span::from(&name); + if !name.text.contains('#') { + self.labels.push(Label { + kind: LabelKind::Reference, + 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, + }); + } } } - 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()) { - self.labels.push(Label { - kind: LabelKind::ReferenceRange, - name: Span::from(&from), - targets: Vec::new(), - full_range, - }); + let name = Span::from(&from); + if !name.text.contains('#') { + self.labels.push(Label { + kind: LabelKind::ReferenceRange, + 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, + }); + } } if let Some(to) = label.to().and_then(|group| group.key()) { - self.labels.push(Label { - kind: LabelKind::ReferenceRange, - name: Span::from(&to), - targets: Vec::new(), - full_range, - }); + let name = Span::from(&to); + if !name.text.contains('#') { + self.labels.push(Label { + kind: LabelKind::ReferenceRange, + 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, + }); + } } } @@ -232,10 +287,13 @@ impl Semantics { let full_range = latex::small_range(&citation); if let Some(list) = citation.key_list() { for key in list.keys() { - self.citations.push(Citation { - name: Span::from(&key), - full_range, - }); + let name = Span::from(&key); + if !name.text.contains('#') { + self.citations.push(Citation { + name: Span::from(&key), + full_range, + }); + } } } } @@ -319,6 +377,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/base-db/src/workspace.rs b/support/texlab/crates/base-db/src/workspace.rs index 75674dcf8d..f2564ed415 100644 --- a/support/texlab/crates/base-db/src/workspace.rs +++ b/support/texlab/crates/base-db/src/workspace.rs @@ -99,7 +99,7 @@ impl Workspace { Cow::Owned(text) => text, }; - let owner = if self.distro.file_name_db.contains(&path) { + let owner = if self.distro.file_name_db.contains(path) { Owner::Distro } else { Owner::Server diff --git a/support/texlab/crates/bibfmt/src/lib.rs b/support/texlab/crates/bibfmt/src/lib.rs index cfce131f2c..8840daa1c4 100644 --- a/support/texlab/crates/bibfmt/src/lib.rs +++ b/support/texlab/crates/bibfmt/src/lib.rs @@ -11,7 +11,7 @@ pub struct Options { impl Options { fn indent(&self) -> String { if self.insert_spaces { - std::iter::repeat(' ').take(self.tab_size).collect() + " ".repeat(self.tab_size) } else { String::from("\t") } @@ -126,7 +126,7 @@ impl<'a> Formatter<'a> { } bibtex::FIELD => { let field = bibtex::Field::cast(parent).unwrap(); - self.output.push_str(&self.indent); + self.output.push_str(self.indent); let name = field.name_token().unwrap(); self.output.push_str(name.text()); self.output.push_str(" = "); @@ -160,7 +160,7 @@ impl<'a> Formatter<'a> { if length + current_length + space_length > self.options.line_length { self.output.push('\n'); self.output.push_str(self.indent.as_ref()); - for _ in 0..=align - self.options.tab_size as usize { + for _ in 0..=align - self.options.tab_size { self.output.push(' '); } length = align; diff --git a/support/texlab/crates/bibtex-utils/Cargo.toml b/support/texlab/crates/bibtex-utils/Cargo.toml index 97e13517f5..88ba2d3a37 100644 --- a/support/texlab/crates/bibtex-utils/Cargo.toml +++ b/support/texlab/crates/bibtex-utils/Cargo.toml @@ -7,11 +7,11 @@ edition.workspace = true rust-version.workspace = true [dependencies] -chrono = { version = "0.4.26", default-features = false, features = ["std"] } +chrono = { version = "0.4.38", default-features = false, features = ["std"] } human_name = "2.0.3" -itertools = "0.12.1" +itertools = "0.13.0" rowan = "0.15.15" -rustc-hash = "1.1.0" +rustc-hash = "2.0.0" syntax = { path = "../syntax" } unicode-normalization = "0.1.23" diff --git a/support/texlab/crates/citeproc/Cargo.toml b/support/texlab/crates/citeproc/Cargo.toml index 9e187f88e6..c87ab14b0e 100644 --- a/support/texlab/crates/citeproc/Cargo.toml +++ b/support/texlab/crates/citeproc/Cargo.toml @@ -10,9 +10,9 @@ rust-version.workspace = true bibtex-utils = { path = "../bibtex-utils" } base-db = { path = "../base-db" } isocountry = "0.3.2" -itertools = "0.12.1" +itertools = "0.13.0" rowan = "0.15.15" -rustc-hash = "1.1.0" +rustc-hash = "2.0.0" syntax = { path = "../syntax" } titlecase = "3.0.0" unicode-normalization = "0.1.23" diff --git a/support/texlab/crates/commands/Cargo.toml b/support/texlab/crates/commands/Cargo.toml index c726e5f0d5..6507efb3b8 100644 --- a/support/texlab/crates/commands/Cargo.toml +++ b/support/texlab/crates/commands/Cargo.toml @@ -7,17 +7,17 @@ edition.workspace = true rust-version.workspace = true [dependencies] -anyhow = "1.0.82" +anyhow = "1.0.86" base-db = { path = "../base-db" } bstr = "1.9.1" -crossbeam-channel = "0.5.12" -itertools = "0.12.1" -libc = "0.2.153" -log = "0.4.21" +crossbeam-channel = "0.5.13" +itertools = "0.13.0" +libc = "0.2.155" +log = "0.4.22" rowan = "0.15.15" -rustc-hash = "1.1.0" +rustc-hash = "2.0.0" syntax = { path = "../syntax" } -thiserror = "1.0.59" +thiserror = "1.0.61" url = "2.5.0" [dev-dependencies] 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/find_envs.rs b/support/texlab/crates/commands/src/find_envs.rs index 7a17345d80..1ea88657ab 100644 --- a/support/texlab/crates/commands/src/find_envs.rs +++ b/support/texlab/crates/commands/src/find_envs.rs @@ -53,7 +53,7 @@ mod tests { let workspace = fixture.workspace; let document = workspace.iter().next().unwrap(); let offset = fixture.documents[0].cursor.unwrap(); - let results = find_environments(&document, offset); + let results = find_environments(document, offset); expect.assert_debug_eq(&results); } diff --git a/support/texlab/crates/commands/src/fwd_search.rs b/support/texlab/crates/commands/src/fwd_search.rs index f1991cd48e..f3480ef3e2 100644 --- a/support/texlab/crates/commands/src/fwd_search.rs +++ b/support/texlab/crates/commands/src/fwd_search.rs @@ -103,7 +103,7 @@ impl ForwardSearch { }) .ok_or_else(|| ForwardSearchError::InvalidPath(document.uri.clone()))?; - let pdf_path = pdf_dir.join(&pdf_name); + let pdf_path = pdf_dir.join(pdf_name); let pdf_exists = pdf_path.exists(); log::debug!("[FwdSearch] pdf_path={pdf_path:?}, pdf_exists={pdf_exists}"); diff --git a/support/texlab/crates/commands/src/placeholders.rs b/support/texlab/crates/commands/src/placeholders.rs index 9994420f5d..328a95773a 100644 --- a/support/texlab/crates/commands/src/placeholders.rs +++ b/support/texlab/crates/commands/src/placeholders.rs @@ -45,6 +45,6 @@ mod tests { &[('f', "foo")], ); - assert_eq!(output, vec!["foo".into(), "%f", "%f".into(), "foobar"]); + assert_eq!(output, vec!["foo", "%f", "%f", "foobar"]); } } diff --git a/support/texlab/crates/completion-data/Cargo.toml b/support/texlab/crates/completion-data/Cargo.toml index 59b707e474..6dbb7d4086 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.29" -itertools = "0.12.1" +flate2 = "1.0.30" +itertools = "0.13.0" once_cell = "1.19.0" -rustc-hash = "1.1.0" -serde = { version = "1.0.195", features = ["derive"] } -serde_json = "1.0.115" +rustc-hash = "2.0.0" +serde = { version = "1.0.203", features = ["derive"] } +serde_json = "1.0.119" [lib] doctest = false diff --git a/support/texlab/crates/completion/Cargo.toml b/support/texlab/crates/completion/Cargo.toml index e28e04dfc7..f98f12260b 100644 --- a/support/texlab/crates/completion/Cargo.toml +++ b/support/texlab/crates/completion/Cargo.toml @@ -13,7 +13,7 @@ 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" +rustc-hash = "2.0.0" syntax = { path = "../syntax" } url = "2.5.0" @@ -22,6 +22,7 @@ criterion = "0.5.1" distro = { path = "../distro" } expect-test = "1.5.0" 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 7fc5bba174..5bfd94d944 100644 --- a/support/texlab/crates/completion/src/lib.rs +++ b/support/texlab/crates/completion/src/lib.rs @@ -72,12 +72,12 @@ 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, - Self::Directory(name) => &name, - Self::Argument(data) => &data.0.name, + Self::File(name) => name, + Self::Directory(name) => name, + Self::Argument(data) => data.0.name, Self::Package(name) => name, Self::DocumentClass(name) => name, Self::EntryType(data) => data.0.name, diff --git a/support/texlab/crates/completion/src/providers/argument.rs b/support/texlab/crates/completion/src/providers/argument.rs index 6637cecdd6..a9c8f4304b 100644 --- a/support/texlab/crates/completion/src/providers/argument.rs +++ b/support/texlab/crates/completion/src/providers/argument.rs @@ -37,7 +37,7 @@ pub fn complete_arguments<'a>( .filter(|(i, _)| *i == index) { for arg in ¶m.0 { - if let Some(score) = builder.matcher.score(&arg.name, &cursor.text) { + if let Some(score) = builder.matcher.score(arg.name, &cursor.text) { let data = CompletionItemData::Argument(ArgumentData(arg)); builder .items diff --git a/support/texlab/crates/completion/src/providers/color.rs b/support/texlab/crates/completion/src/providers/color.rs index cbdd38dc32..8be1b50555 100644 --- a/support/texlab/crates/completion/src/providers/color.rs +++ b/support/texlab/crates/completion/src/providers/color.rs @@ -14,7 +14,7 @@ pub fn complete_colors<'a>( latex::ColorReference::cast(group.syntax().parent()?)?; for name in COLORS { - if let Some(score) = builder.matcher.score(&name, &cursor.text) { + if let Some(score) = builder.matcher.score(name, &cursor.text) { let data = CompletionItemData::Color(name); builder .items diff --git a/support/texlab/crates/completion/src/providers/color_model.rs b/support/texlab/crates/completion/src/providers/color_model.rs index 4b991c4807..80970b1913 100644 --- a/support/texlab/crates/completion/src/providers/color_model.rs +++ b/support/texlab/crates/completion/src/providers/color_model.rs @@ -14,7 +14,7 @@ pub fn complete_color_models<'a>( let cursor = check_color_definition(params).or_else(|| check_color_definition_set(params))?; for name in MODEL_NAMES { - if let Some(score) = builder.matcher.score(&name, &cursor.text) { + if let Some(score) = builder.matcher.score(name, &cursor.text) { let data = CompletionItemData::ColorModel(name); builder .items diff --git a/support/texlab/crates/completion/src/providers/command.rs b/support/texlab/crates/completion/src/providers/command.rs index f404d42ab3..34f8876e1f 100644 --- a/support/texlab/crates/completion/src/providers/command.rs +++ b/support/texlab/crates/completion/src/providers/command.rs @@ -44,7 +44,7 @@ impl<'a, 'b> Processor<'a, 'b> { pub fn add_delimiters(&mut self) { for (left, right) in DELIMITERS { - let Some(score) = self.0.builder.matcher.score(&left, &self.0.cursor.text) else { + let Some(score) = self.0.builder.matcher.score(left, &self.0.cursor.text) else { continue; }; @@ -68,7 +68,7 @@ impl<'a, 'b> Processor<'a, 'b> { let data = CompletionItemData::Command(CommandData { name: &command.name, glyph: command.glyph.as_deref(), - image: command.image.as_deref(), + image: command.image, package: Some(package), }); diff --git a/support/texlab/crates/completion/src/providers/environment.rs b/support/texlab/crates/completion/src/providers/environment.rs index 393dd100ba..b56377550e 100644 --- a/support/texlab/crates/completion/src/providers/environment.rs +++ b/support/texlab/crates/completion/src/providers/environment.rs @@ -21,7 +21,7 @@ pub fn complete_environments<'a>( .and_then(|env| env.begin()) .and_then(|begin| begin.name()) .and_then(|name| name.key()) - .map_or_else(|| String::new(), |name| name.to_string()); + .map_or_else(String::new, |name| name.to_string()); let mut proc = Processor { inner: ProviderContext { @@ -48,7 +48,7 @@ impl<'a, 'b> Processor<'a, 'b> { for package in included_packages(&self.inner.params.feature) { let envs_with_score = package.environments.iter().filter_map(|env| { let matcher = &self.inner.builder.matcher; - let score = matcher.score(&env, &self.inner.cursor.text)?; + let score = matcher.score(env, &self.inner.cursor.text)?; Some((*env, score)) }); @@ -76,7 +76,7 @@ impl<'a, 'b> Processor<'a, 'b> { { let matcher = &self.inner.builder.matcher; let name = theorem.name.text.as_str(); - if let Some(score) = matcher.score(&name, &self.inner.cursor.text) { + if let Some(score) = matcher.score(name, &self.inner.cursor.text) { let data = CompletionItemData::Environment(EnvironmentData { name, package: None, diff --git a/support/texlab/crates/completion/src/providers/glossary.rs b/support/texlab/crates/completion/src/providers/glossary.rs index c22f5b5888..d01d62a511 100644 --- a/support/texlab/crates/completion/src/providers/glossary.rs +++ b/support/texlab/crates/completion/src/providers/glossary.rs @@ -58,7 +58,7 @@ impl<'a, 'b> Processor<'a, 'b> { for name in data .root_node() .descendants() - .filter_map(|node| extract(node)) + .filter_map(&extract) .filter_map(|name| name.key()) .map(|name| name.to_string()) { diff --git a/support/texlab/crates/completion/src/providers/import.rs b/support/texlab/crates/completion/src/providers/import.rs index c506026a99..c0f608f9d2 100644 --- a/support/texlab/crates/completion/src/providers/import.rs +++ b/support/texlab/crates/completion/src/providers/import.rs @@ -29,13 +29,13 @@ pub fn complete_imports<'a>( file_names.insert(file_name); let stem = &file_name[0..file_name.len() - 4]; if kind == latex::PACKAGE_INCLUDE { - if let Some(score) = builder.matcher.score(&stem, &cursor.text) { + if let Some(score) = builder.matcher.score(stem, &cursor.text) { let data = CompletionItemData::Package(stem); builder .items .push(CompletionItem::new_simple(score, cursor.range, data)); } - } else if let Some(score) = builder.matcher.score(&stem, &cursor.text) { + } else if let Some(score) = builder.matcher.score(stem, &cursor.text) { let data = CompletionItemData::DocumentClass(stem); builder .items @@ -51,13 +51,13 @@ pub fn complete_imports<'a>( { let stem = &file_name[0..file_name.len() - 4]; if kind == latex::PACKAGE_INCLUDE { - if let Some(score) = builder.matcher.score(&stem, &cursor.text) { + if let Some(score) = builder.matcher.score(stem, &cursor.text) { let data = CompletionItemData::Package(stem); builder .items .push(CompletionItem::new_simple(score, cursor.range, data)); } - } else if let Some(score) = builder.matcher.score(&stem, &cursor.text) { + } else if let Some(score) = builder.matcher.score(stem, &cursor.text) { let data = CompletionItemData::DocumentClass(stem); builder .items diff --git a/support/texlab/crates/completion/src/providers/include.rs b/support/texlab/crates/completion/src/providers/include.rs index 60c432d097..d0a042252f 100644 --- a/support/texlab/crates/completion/src/providers/include.rs +++ b/support/texlab/crates/completion/src/providers/include.rs @@ -19,9 +19,7 @@ pub fn complete_includes<'a>( params: &'a CompletionParams<'a>, builder: &mut CompletionBuilder<'a>, ) -> Option<()> { - if params.feature.document.path.is_none() { - return None; - } + params.feature.document.path.as_ref()?; let (cursor, group) = find_curly_group_word_list(params)?; @@ -115,7 +113,7 @@ fn current_dir( graphics_path: Option<&str>, ) -> Option<PathBuf> { let workspace = ¶ms.workspace; - let parent = deps::parents(&workspace, params.document) + let parent = deps::parents(workspace, params.document) .iter() .next() .map_or(params.document, Clone::clone); 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/providers/tikz_library.rs b/support/texlab/crates/completion/src/providers/tikz_library.rs index 10a0aa44ba..4c3d7e8955 100644 --- a/support/texlab/crates/completion/src/providers/tikz_library.rs +++ b/support/texlab/crates/completion/src/providers/tikz_library.rs @@ -21,7 +21,7 @@ pub fn complete_tikz_libraries<'a>( }; for name in libraries { - if let Some(score) = builder.matcher.score(&name, &cursor.text) { + if let Some(score) = builder.matcher.score(name, &cursor.text) { let data = CompletionItemData::TikzLibrary(name); builder .items 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..254bea0314 100644 --- a/support/texlab/crates/definition/Cargo.toml +++ b/support/texlab/crates/definition/Cargo.toml @@ -9,11 +9,11 @@ rust-version.workspace = true [dependencies] base-db = { path = "../base-db" } rowan = "0.15.15" -rustc-hash = "1.1.0" +rustc-hash = "2.0.0" syntax = { path = "../syntax" } [dev-dependencies] -itertools = "0.12.1" +itertools = "0.13.0" 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 3681cdbaa0..ea7ef8c2c2 100644 --- a/support/texlab/crates/definition/src/command.rs +++ b/support/texlab/crates/definition/src/command.rs @@ -28,13 +28,11 @@ pub(super) fn goto_definition(context: &mut DefinitionContext) -> Option<()> { process_old_definition(node.clone()).or_else(|| process_new_definition(node)) }) .filter(|(_, command)| command.text() == name.text()) - .filter_map(|(target_range, command)| { - Some(DefinitionResult { - origin_selection_range, - target: document, - target_range, - target_selection_range: command.text_range(), - }) + .map(|(target_range, command)| DefinitionResult { + origin_selection_range, + target: document, + target_range, + target_selection_range: command.text_range(), }); context.results.extend(results); diff --git a/support/texlab/crates/definition/src/tests.rs b/support/texlab/crates/definition/src/tests.rs index 26f7c0dc3a..0b3f7fa7cc 100644 --- a/support/texlab/crates/definition/src/tests.rs +++ b/support/texlab/crates/definition/src/tests.rs @@ -11,7 +11,7 @@ fn check(input: &str) { .locations() .filter(|location| location.document == origin_document) .find(|location| location.range.contains_inclusive(offset)) - .map_or_else(|| TextRange::default(), |location| location.range); + .map_or_else(TextRange::default, |location| location.range); let mut expected = FxHashSet::default(); for document in &fixture.documents { diff --git a/support/texlab/crates/diagnostics/Cargo.toml b/support/texlab/crates/diagnostics/Cargo.toml index 1cb09fdec9..37e39280ad 100644 --- a/support/texlab/crates/diagnostics/Cargo.toml +++ b/support/texlab/crates/diagnostics/Cargo.toml @@ -10,14 +10,14 @@ 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 = "0.13.0" line-index = { path = "../line-index" } -log = "0.4.21" +log = "0.4.22" multimap = "0.10.0" once_cell = "1.19.0" -regex = "1.10.4" +regex = "1.10.5" rowan = "0.15.15" -rustc-hash = "1.1.0" +rustc-hash = "2.0.0" syntax = { path = "../syntax" } url = "2.5.0" diff --git a/support/texlab/crates/diagnostics/src/chktex.rs b/support/texlab/crates/diagnostics/src/chktex.rs index 195ea968e5..a3f539440d 100644 --- a/support/texlab/crates/diagnostics/src/chktex.rs +++ b/support/texlab/crates/diagnostics/src/chktex.rs @@ -74,7 +74,7 @@ impl Command { .build(stdout), ); - for line in reader.lines().flatten() { + for line in reader.lines().map_while(Result::ok) { let captures = LINE_REGEX.captures(&line).unwrap(); let line = captures[1].parse::<u32>().unwrap() - 1; let character = captures[2].parse::<u32>().unwrap() - 1; diff --git a/support/texlab/crates/diagnostics/src/citations.rs b/support/texlab/crates/diagnostics/src/citations.rs index 0f09f589be..43d5f495d2 100644 --- a/support/texlab/crates/diagnostics/src/citations.rs +++ b/support/texlab/crates/diagnostics/src/citations.rs @@ -24,7 +24,7 @@ pub fn detect_undefined_citations<'a>( for citation in &data.semantics.citations { let name = citation.name_text(); - if name != "*" && !entries.contains(name) && !name.contains("#") { + if name != "*" && !entries.contains(name) { let diagnostic = Diagnostic::Tex(citation.name.range, TexError::UndefinedCitation); results .entry(document.uri.clone()) @@ -65,8 +65,8 @@ pub fn detect_unused_entries<'a>( Some(()) } -pub fn detect_duplicate_entries<'a>( - workspace: &'a Workspace, +pub fn detect_duplicate_entries( + workspace: &Workspace, results: &mut FxHashMap<Url, Vec<Diagnostic>>, ) { for conflict in queries::Conflict::find_all::<Entry>(workspace) { diff --git a/support/texlab/crates/diagnostics/src/manager.rs b/support/texlab/crates/diagnostics/src/manager.rs index 3573bc40f2..500dc99c13 100644 --- a/support/texlab/crates/diagnostics/src/manager.rs +++ b/support/texlab/crates/diagnostics/src/manager.rs @@ -89,16 +89,12 @@ impl Manager { let config = &workspace.config().diagnostics; - results.retain(|uri, _| { - workspace - .lookup(uri) - .map_or(false, |document| Self::is_relevant(document)) - }); + results.retain(|uri, _| workspace.lookup(uri).map_or(false, Self::is_relevant)); - for (_, diagnostics) in &mut results { + for diagnostics in results.values_mut() { diagnostics.retain(|diagnostic| { filter_regex_patterns( - &diagnostic.message(), + diagnostic.message(), &config.allowed_patterns, &config.ignored_patterns, ) diff --git a/support/texlab/crates/diagnostics/src/tests.rs b/support/texlab/crates/diagnostics/src/tests.rs index 3688abeaf4..05ad67fc8b 100644 --- a/support/texlab/crates/diagnostics/src/tests.rs +++ b/support/texlab/crates/diagnostics/src/tests.rs @@ -6,14 +6,14 @@ fn check(input: &str, expect: Expect) { let mut manager = crate::Manager::default(); for document in fixture.workspace.iter() { - manager.update_syntax(&fixture.workspace, &document); + manager.update_syntax(&fixture.workspace, document); } let results = manager.get(&fixture.workspace); let results = results .iter() .filter(|(_, diags)| !diags.is_empty()) - .sorted_by(|(uri1, _), (uri2, _)| uri1.cmp(&uri2)) + .sorted_by(|(uri1, _), (uri2, _)| uri1.cmp(uri2)) .map(|(uri, diags)| (uri.as_str(), diags)) .collect_vec(); diff --git a/support/texlab/crates/distro/Cargo.toml b/support/texlab/crates/distro/Cargo.toml index eefcea2b49..c81288e86e 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.82" -rustc-hash = "1.1.0" +anyhow = "1.0.86" +rustc-hash = "2.0.0" [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/highlights/src/lib.rs b/support/texlab/crates/highlights/src/lib.rs index 009dd20807..9d7a63b096 100644 --- a/support/texlab/crates/highlights/src/lib.rs +++ b/support/texlab/crates/highlights/src/lib.rs @@ -23,7 +23,7 @@ pub struct HighlightParams<'a> { pub fn find_all(params: &HighlightParams) -> Vec<Highlight> { let mut results = Vec::new(); - label::find_highlights(¶ms, &mut results); + label::find_highlights(params, &mut results); results } diff --git a/support/texlab/crates/hover/src/lib.rs b/support/texlab/crates/hover/src/lib.rs index 2d22d50b0b..dcf4d442a9 100644 --- a/support/texlab/crates/hover/src/lib.rs +++ b/support/texlab/crates/hover/src/lib.rs @@ -35,12 +35,12 @@ pub enum HoverData<'db> { } pub fn find<'a>(params: &HoverParams<'a>) -> Option<Hover<'a>> { - citation::find_hover(¶ms) - .or_else(|| package::find_hover(¶ms)) - .or_else(|| entry_type::find_hover(¶ms)) - .or_else(|| field_type::find_hover(¶ms)) - .or_else(|| label::find_hover(¶ms)) - .or_else(|| string_ref::find_hover(¶ms)) + citation::find_hover(params) + .or_else(|| package::find_hover(params)) + .or_else(|| entry_type::find_hover(params)) + .or_else(|| field_type::find_hover(params)) + .or_else(|| label::find_hover(params)) + .or_else(|| string_ref::find_hover(params)) } #[cfg(test)] diff --git a/support/texlab/crates/inlay-hints/Cargo.toml b/support/texlab/crates/inlay-hints/Cargo.toml index ede4ca6188..94651ef4c7 100644 --- a/support/texlab/crates/inlay-hints/Cargo.toml +++ b/support/texlab/crates/inlay-hints/Cargo.toml @@ -9,7 +9,7 @@ rust-version.workspace = true [dependencies] base-db = { path = "../base-db" } rowan = "0.15.15" -rustc-hash = "1.1.0" +rustc-hash = "2.0.0" [dev-dependencies] test-utils = { path = "../test-utils" } diff --git a/support/texlab/crates/inlay-hints/src/label.rs b/support/texlab/crates/inlay-hints/src/label.rs index 8db852752e..6e29c806b6 100644 --- a/support/texlab/crates/inlay-hints/src/label.rs +++ b/support/texlab/crates/inlay-hints/src/label.rs @@ -9,7 +9,6 @@ use crate::{InlayHint, InlayHintBuilder, InlayHintData}; pub(super) fn find_hints(builder: &mut InlayHintBuilder) -> Option<()> { let definitions = base_db::semantics::tex::Label::find_all(&builder.params.feature.project) - .into_iter() .filter(|(_, label)| label.kind == LabelKind::Definition) .map(|(_, label)| (label.name_text(), label)) .collect::<FxHashMap<_, _>>(); diff --git a/support/texlab/crates/ipc/Cargo.toml b/support/texlab/crates/ipc/Cargo.toml index ae31dcaf55..92afbba876 100644 --- a/support/texlab/crates/ipc/Cargo.toml +++ b/support/texlab/crates/ipc/Cargo.toml @@ -7,10 +7,10 @@ edition.workspace = true rust-version.workspace = true [dependencies] -crossbeam-channel = "0.5.12" -serde = "1.0.199" -serde_json = "1.0.116" -log = "0.4.21" +crossbeam-channel = "0.5.13" +serde = "1.0.203" +serde_json = "1.0.119" +log = "0.4.22" uds_windows = "1.1.0" [lib] diff --git a/support/texlab/crates/line-index/Cargo.toml b/support/texlab/crates/line-index/Cargo.toml index 73d2766d60..58e2fa9e16 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 = "2.0.0" text-size = "1.1.1" [lib] diff --git a/support/texlab/crates/parser/Cargo.toml b/support/texlab/crates/parser/Cargo.toml index 39fa34ef63..2faadcb262 100644 --- a/support/texlab/crates/parser/Cargo.toml +++ b/support/texlab/crates/parser/Cargo.toml @@ -7,16 +7,16 @@ edition.workspace = true rust-version.workspace = true [dependencies] -log = "0.4.21" +log = "0.4.22" logos = "0.14.0" once_cell = "1.19.0" pathdiff = "0.2.1" -regex = "1.10.4" +regex = "1.10.5" rowan = "0.15.15" -rustc-hash = "1.1.0" +rustc-hash = "2.0.0" syntax = { path = "../syntax" } tempfile = "3.10.1" -versions = "6.2.0" +versions = "6.3.0" [dev-dependencies] expect-test = "1.5.0" 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/build_log.rs b/support/texlab/crates/parser/src/build_log.rs index 420d2dda20..c1f59bd086 100644 --- a/support/texlab/crates/parser/src/build_log.rs +++ b/support/texlab/crates/parser/src/build_log.rs @@ -36,7 +36,7 @@ pub fn parse_build_log(log: &str) -> BuildLog { let warnings = extract_matches(&log, &ranges, &WARNING_REGEX, BuildErrorLevel::Warning); let bad_boxes = extract_matches(&log, &ranges, &BAD_BOX_REGEX, BuildErrorLevel::Warning); - let errors = vec![tex_errors, warnings, bad_boxes].concat(); + let errors = [tex_errors, warnings, bad_boxes].concat(); BuildLog { errors } } 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/lexer/types.rs b/support/texlab/crates/parser/src/latex/lexer/types.rs index 896ce01c05..85e542792b 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, diff --git a/support/texlab/crates/parser/src/latexmkrc.rs b/support/texlab/crates/parser/src/latexmkrc.rs index 6013a5f646..58c348d01f 100644 --- a/support/texlab/crates/parser/src/latexmkrc.rs +++ b/support/texlab/crates/parser/src/latexmkrc.rs @@ -28,9 +28,9 @@ mod v483 { .current_dir(temp_dir.path()) .output()?; - let stderr = String::from_utf8_lossy(&output.stderr); + let stdout = String::from_utf8_lossy(&output.stdout); - let (aux_dir, out_dir) = stderr.lines().find_map(extract_dirs).ok_or_else(|| { + let (aux_dir, out_dir) = stdout.lines().find_map(extract_dirs).ok_or_else(|| { std::io::Error::new( std::io::ErrorKind::InvalidData, "Normalized aux and out dir were not found in latexmk output", @@ -106,7 +106,7 @@ mod v484 { let mut it = lines .skip_while(|line| !line.starts_with("Latexmk: Normalized aux dir and out dirs:")) .nth(1)? - .split(","); + .split(','); let aux_dir = it.next()?.trim().strip_prefix('\'')?.strip_suffix('\'')?; @@ -133,7 +133,7 @@ pub fn parse_latexmkrc(input: &str, src_dir: &Path) -> std::io::Result<Latexmkrc .as_ref() .and_then(|line| Some((line.find("Version")?, line))) .and_then(|(i, line)| line[i..].trim_end().strip_prefix("Version ")) - .and_then(|text| versions::Versioning::new(text)); + .and_then(versions::Versioning::new); let result = if version.map_or(false, |v| v >= versions::Versioning::new("4.84").unwrap()) { v484::parse_latexmkrc(input, src_dir) 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..8d4e3a6936 100644 --- a/support/texlab/crates/references/Cargo.toml +++ b/support/texlab/crates/references/Cargo.toml @@ -9,7 +9,7 @@ rust-version.workspace = true [dependencies] base-db = { path = "../base-db" } rowan = "0.15.15" -rustc-hash = "1.1.0" +rustc-hash = "2.0.0" syntax = { path = "../syntax" } [dev-dependencies] diff --git a/support/texlab/crates/references/src/command.rs b/support/texlab/crates/references/src/command.rs index 712da20b73..f4504641f2 100644 --- a/support/texlab/crates/references/src/command.rs +++ b/support/texlab/crates/references/src/command.rs @@ -32,7 +32,7 @@ pub(super) fn find_all(context: &mut ReferenceContext) -> Option<()> { .collect(); for command in &data.semantics.commands { - if command.text == &token.text()[1..] { + if command.text == token.text()[1..] { let kind = if defs.contains(command) { ReferenceKind::Definition } else { diff --git a/support/texlab/crates/references/src/entry.rs b/support/texlab/crates/references/src/entry.rs index c3926058e6..722c96fe4a 100644 --- a/support/texlab/crates/references/src/entry.rs +++ b/support/texlab/crates/references/src/entry.rs @@ -30,14 +30,14 @@ pub(super) fn find_all(context: &mut ReferenceContext) -> Option<()> { }; let project = &context.params.feature.project; - for (document, obj) in queries::objects_with_name::<tex::Citation>(&project, name) { + for (document, obj) in queries::objects_with_name::<tex::Citation>(project, name) { context.results.push(Reference { location: DocumentLocation::new(document, obj.name.range), kind: ReferenceKind::Reference, }); } - for (document, obj) in queries::objects_with_name::<bib::Entry>(&project, name) { + for (document, obj) in queries::objects_with_name::<bib::Entry>(project, name) { context.results.push(Reference { location: DocumentLocation::new(document, obj.name.range), kind: ReferenceKind::Definition, diff --git a/support/texlab/crates/references/src/label.rs b/support/texlab/crates/references/src/label.rs index 872ef2dd14..20fc6e0d25 100644 --- a/support/texlab/crates/references/src/label.rs +++ b/support/texlab/crates/references/src/label.rs @@ -14,7 +14,7 @@ pub(super) fn find_all(context: &mut ReferenceContext) -> Option<()> { .name_text(); let project = &context.params.feature.project; - for (document, label) in queries::objects_with_name::<tex::Label>(&project, name) { + for (document, label) in queries::objects_with_name::<tex::Label>(project, name) { let kind = match label.kind { tex::LabelKind::Definition => ReferenceKind::Definition, tex::LabelKind::Reference | tex::LabelKind::ReferenceRange => ReferenceKind::Reference, diff --git a/support/texlab/crates/rename/Cargo.toml b/support/texlab/crates/rename/Cargo.toml index 1b2b91ee2a..592bafb474 100644 --- a/support/texlab/crates/rename/Cargo.toml +++ b/support/texlab/crates/rename/Cargo.toml @@ -9,11 +9,12 @@ rust-version.workspace = true [dependencies] base-db = { path = "../base-db" } rowan = "0.15.15" -rustc-hash = "1.1.0" +rustc-hash = "2.0.0" 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 edcf7db61e..037e09e4ca 100644 --- a/support/texlab/crates/rename/src/command.rs +++ b/support/texlab/crates/rename/src/command.rs @@ -19,7 +19,7 @@ pub(super) fn prepare_rename(params: &RenameParams) -> Option<Span> { )) } -pub(super) fn rename<'a>(builder: &mut RenameBuilder) -> Option<()> { +pub(super) fn rename(builder: &mut RenameBuilder) -> Option<()> { let name = prepare_rename(&builder.params)?; for document in &builder.params.feature.project.documents { @@ -35,7 +35,10 @@ pub(super) fn rename<'a>(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 c0b508a506..d35d9e9e84 100644 --- a/support/texlab/crates/rename/src/entry.rs +++ b/support/texlab/crates/rename/src/entry.rs @@ -30,7 +30,7 @@ pub(super) fn prepare_rename(params: &RenameParams) -> Option<Span> { } } -pub(super) fn rename<'a>(builder: &mut RenameBuilder) -> Option<()> { +pub(super) fn rename(builder: &mut RenameBuilder) -> Option<()> { let name = prepare_rename(&builder.params)?; let project = &builder.params.feature.project; @@ -42,7 +42,7 @@ pub(super) fn rename<'a>(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 b7dd197ce0..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,14 +28,28 @@ 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(¶ms) - .or_else(|| entry::prepare_rename(¶ms)) - .or_else(|| label::prepare_rename(¶ms)) + command::prepare_rename(params) + .or_else(|| entry::prepare_rename(params)) + .or_else(|| label::prepare_rename(params)) .map(|span| span.range) } -pub fn rename<'a>(params: RenameParams<'a>) -> RenameResult<'a> { +pub fn rename(params: RenameParams) -> RenameResult { let result = RenameResult::default(); let mut builder = RenameBuilder { params, result }; 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..c44f120153 100644 --- a/support/texlab/crates/symbols/Cargo.toml +++ b/support/texlab/crates/symbols/Cargo.toml @@ -12,7 +12,7 @@ doctest = false [dependencies] base-db = { path = "../base-db" } distro = { path = "../distro" } -itertools = "0.12.1" +itertools = "0.13.0" line-index = { path = "../line-index" } rowan = "0.15.15" syntax = { path = "../syntax" } @@ -20,6 +20,6 @@ titlecase = "3.0.0" url = "2.5.0" [dev-dependencies] -regex = "1.10.4" +regex = "1.10.5" test-utils = { path = "../test-utils" } expect-test = "1.5.0" 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/syntax/Cargo.toml b/support/texlab/crates/syntax/Cargo.toml index a2d3d866b6..14dfffe380 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" +itertools = "0.13.0" rowan = "0.15.15" +rustc-hash = "2.0.0" [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/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/texlab/Cargo.toml b/support/texlab/crates/texlab/Cargo.toml index 4c142a2229..f4cb2d3283 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.0" +version = "5.19.0" license.workspace = true readme = "README.md" authors.workspace = true @@ -22,15 +22,15 @@ test = false doctest = false [dependencies] -anyhow = "1.0.82" +anyhow = "1.0.86" base-db = { path = "../base-db" } bibfmt = { path = "../bibfmt" } citeproc = { path = "../citeproc" } -clap = { version = "4.5.4", features = ["derive"] } +clap = { version = "4.5.8", features = ["derive"] } commands = { path = "../commands" } completion = { path = "../completion" } completion-data = { path = "../completion-data" } -crossbeam-channel = "0.5.12" +crossbeam-channel = "0.5.13" definition = { path = "../definition" } diagnostics = { path = "../diagnostics" } distro = { path = "../distro" } @@ -42,22 +42,22 @@ inlay-hints = { path = "../inlay-hints" } ipc = { path = "../ipc" } line-index = { path = "../line-index" } links = { path = "../links" } -log = "0.4.21" +log = "0.4.22" lsp-server = "0.7.6" lsp-types = "0.95.1" notify = "6.1.1" notify-debouncer-full = "0.3.1" -parking_lot = "0.12.1" +parking_lot = "0.12.3" parser = { path = "../parser" } references = { path = "../references" } -regex = "1.10.4" +regex = "1.10.5" rename = { path = "../rename" } rowan = "0.15.15" -rustc-hash = "1.1.0" -serde = "1.0.195" -serde_json = "1.0.115" +rustc-hash = "2.0.0" +serde = "1.0.203" +serde_json = "1.0.119" serde_regex = "1.1.0" -serde_repr = "0.1.18" +serde_repr = "0.1.19" symbols = { path = "../symbols" } syntax = { path = "../syntax" } tempfile = "3.10.1" diff --git a/support/texlab/crates/texlab/src/features/folding.rs b/support/texlab/crates/texlab/src/features/folding.rs index b19fa8a4da..3669021201 100644 --- a/support/texlab/crates/texlab/src/features/folding.rs +++ b/support/texlab/crates/texlab/src/features/folding.rs @@ -9,7 +9,7 @@ pub fn find_all( ) -> Option<Vec<serde_json::Value>> { let params = from_proto::feature_params(workspace, params.text_document)?; - let foldings = folding::find_all(¶ms.document) + let foldings = folding::find_all(params.document) .into_iter() .filter_map(|folding| { to_proto::folding_range(folding, ¶ms.document.line_index, client_flags) 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/rename.rs b/support/texlab/crates/texlab/src/features/rename.rs index b905a7105b..a65f42143b 100644 --- a/support/texlab/crates/texlab/src/features/rename.rs +++ b/support/texlab/crates/texlab/src/features/rename.rs @@ -18,5 +18,5 @@ pub fn rename_all( let new_name = ¶ms.new_name; let params = from_proto::rename_params(workspace, params.text_document_position)?; let result = rename::rename(params); - Some(to_proto::workspace_edit(result, &new_name)) + Some(to_proto::workspace_edit(result, new_name)) } diff --git a/support/texlab/crates/texlab/src/main.rs b/support/texlab/crates/texlab/src/main.rs index ceca02b7f7..d9bf6833d8 100644 --- a/support/texlab/crates/texlab/src/main.rs +++ b/support/texlab/crates/texlab/src/main.rs @@ -110,6 +110,7 @@ fn setup_logger(opts: &Opts) { OpenOptions::new() .write(true) .create(true) + .truncate(true) .open(log_file) .expect("failed to open log file"), ), diff --git a/support/texlab/crates/texlab/src/server.rs b/support/texlab/crates/texlab/src/server.rs index ef70c42c71..ff3c7904d3 100644 --- a/support/texlab/crates/texlab/src/server.rs +++ b/support/texlab/crates/texlab/src/server.rs @@ -46,7 +46,7 @@ use self::{ #[derive(Debug)] enum InternalMessage { SetDistro(Distro), - SetOptions(Options), + SetOptions(Box<Options>), FileEvent(Vec<DebouncedEvent>), Diagnostics, ChktexFinished(Url, Vec<diagnostics::Diagnostic>), @@ -309,7 +309,9 @@ impl Server { .parse_options(json.pop().expect("invalid configuration request")) .unwrap(); - sender.send(InternalMessage::SetOptions(options)).unwrap(); + sender + .send(InternalMessage::SetOptions(Box::new(options))) + .unwrap(); } Err(why) => { log::error!("Retrieving configuration failed: {}", why); @@ -700,19 +702,19 @@ impl Server { let status = if pending_builds.lock().remove(&pid) { if result?.success() { - BuildStatus::SUCCESS + BuildStatus::Success } else { - BuildStatus::ERROR + BuildStatus::Error } } else { - BuildStatus::CANCELLED + BuildStatus::Cancelled }; Ok(status) }) .unwrap_or_else(|why| { log::error!("Failed to compile document \"{uri}\": {why}"); - BuildStatus::FAILURE + BuildStatus::Failure }); drop(progress_reporter); @@ -723,7 +725,7 @@ impl Server { let _ = client.send_response(lsp_server::Response::new_ok(id, result)); } - if fwd_search_after && status != BuildStatus::CANCELLED { + if fwd_search_after && status != BuildStatus::Cancelled { let _ = internal.send(InternalMessage::ForwardSearch(uri, params.position)); } }); @@ -760,7 +762,7 @@ impl Server { self.pool.execute(move || { let status = match command.and_then(ForwardSearch::run) { - Ok(()) => ForwardSearchStatus::SUCCESS, + Ok(()) => ForwardSearchStatus::Success, Err(why) => { log::error!("Failed to execute forward search: {why}"); ForwardSearchStatus::from(why) @@ -1071,7 +1073,7 @@ impl Server { self.workspace.write().set_distro(distro); } InternalMessage::SetOptions(options) => { - self.update_options(options); + self.update_options(*options); } InternalMessage::FileEvent(events) => { for event in events { diff --git a/support/texlab/crates/texlab/src/server/extensions.rs b/support/texlab/crates/texlab/src/server/extensions.rs index ce876c7892..8fa78ef3d2 100644 --- a/support/texlab/crates/texlab/src/server/extensions.rs +++ b/support/texlab/crates/texlab/src/server/extensions.rs @@ -33,10 +33,10 @@ pub struct BuildResult { #[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize_repr, Deserialize_repr)] #[repr(i32)] pub enum BuildStatus { - SUCCESS = 0, - ERROR = 1, - FAILURE = 2, - CANCELLED = 3, + Success = 0, + Error = 1, + Failure = 2, + Cancelled = 3, } pub struct ForwardSearchRequest; @@ -52,21 +52,21 @@ impl lsp_types::request::Request for ForwardSearchRequest { #[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize_repr, Deserialize_repr)] #[repr(i32)] pub enum ForwardSearchStatus { - SUCCESS = 0, - ERROR = 1, - FAILURE = 2, - UNCONFIGURED = 3, + Success = 0, + Error = 1, + Failure = 2, + Unconfigured = 3, } impl From<ForwardSearchError> for ForwardSearchStatus { fn from(why: ForwardSearchError) -> Self { match why { - ForwardSearchError::Unconfigured => ForwardSearchStatus::UNCONFIGURED, - ForwardSearchError::NotLocal(_) => ForwardSearchStatus::FAILURE, - ForwardSearchError::InvalidPath(_) => ForwardSearchStatus::ERROR, - ForwardSearchError::TexNotFound(_) => ForwardSearchStatus::FAILURE, - ForwardSearchError::PdfNotFound(_) => ForwardSearchStatus::ERROR, - ForwardSearchError::LaunchViewer(_) => ForwardSearchStatus::ERROR, + ForwardSearchError::Unconfigured => ForwardSearchStatus::Unconfigured, + ForwardSearchError::NotLocal(_) => ForwardSearchStatus::Failure, + ForwardSearchError::InvalidPath(_) => ForwardSearchStatus::Error, + ForwardSearchError::TexNotFound(_) => ForwardSearchStatus::Failure, + ForwardSearchError::PdfNotFound(_) => ForwardSearchStatus::Error, + ForwardSearchError::LaunchViewer(_) => ForwardSearchStatus::Error, } } } diff --git a/support/texlab/crates/texlab/src/server/options.rs b/support/texlab/crates/texlab/src/server/options.rs index 02200392f6..f4853d7cb9 100644 --- a/support/texlab/crates/texlab/src/server/options.rs +++ b/support/texlab/crates/texlab/src/server/options.rs @@ -1,11 +1,11 @@ -use regex::Regex; use serde::{Deserialize, Serialize}; +use regex::Regex; + #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] #[serde(default)] pub struct Options { - pub root_directory: Option<String>, pub aux_directory: Option<String>, pub bibtex_formatter: BibtexFormatter, pub latex_formatter: LatexFormatter, @@ -70,6 +70,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)] @@ -126,7 +127,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 d9ec5a2fb1..87e41a78ae 100644 --- a/support/texlab/crates/texlab/src/util.rs +++ b/support/texlab/crates/texlab/src/util.rs @@ -18,11 +18,30 @@ 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.len() == 0 { + 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 db4f3a5b39..b55e3cb0e3 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,30 +95,25 @@ 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, } } -pub fn rename_params<'a>( - workspace: &'a Workspace, +pub fn rename_params( + workspace: &Workspace, params: lsp_types::TextDocumentPositionParams, -) -> Option<RenameParams<'a>> { +) -> Option<RenameParams> { let (feature, offset) = feature_params_offset(workspace, params.text_document, params.position)?; Some(RenameParams { feature, offset }) } -pub fn hover_params<'a>( - workspace: &'a Workspace, - params: lsp_types::HoverParams, -) -> Option<HoverParams<'a>> { +pub fn hover_params(workspace: &Workspace, params: lsp_types::HoverParams) -> Option<HoverParams> { let (feature, offset) = feature_params_offset( workspace, params.text_document_position_params.text_document, @@ -144,8 +123,8 @@ pub fn hover_params<'a>( Some(HoverParams { feature, offset }) } -pub fn inlay_hint_params<'a>( - workspace: &'a Workspace, +pub fn inlay_hint_params( + workspace: &Workspace, params: lsp_types::InlayHintParams, ) -> Option<InlayHintParams> { let feature = feature_params(workspace, params.text_document)?; @@ -153,10 +132,10 @@ pub fn inlay_hint_params<'a>( Some(InlayHintParams { feature, range }) } -pub fn highlight_params<'a>( - workspace: &'a Workspace, +pub fn highlight_params( + workspace: &Workspace, params: lsp_types::DocumentHighlightParams, -) -> Option<HighlightParams<'a>> { +) -> Option<HighlightParams<'_>> { let (feature, offset) = feature_params_offset( workspace, params.text_document_position_params.text_document, @@ -166,10 +145,10 @@ pub fn highlight_params<'a>( Some(HighlightParams { feature, offset }) } -pub fn definition_params<'a>( - workspace: &'a Workspace, +pub fn definition_params( + workspace: &Workspace, params: lsp_types::GotoDefinitionParams, -) -> Option<DefinitionParams<'a>> { +) -> Option<DefinitionParams> { let (feature, offset) = feature_params_offset( workspace, params.text_document_position_params.text_document, @@ -179,10 +158,10 @@ pub fn definition_params<'a>( Some(DefinitionParams { feature, offset }) } -pub fn completion_params<'a>( - workspace: &'a Workspace, +pub fn completion_params( + workspace: &Workspace, params: lsp_types::CompletionParams, -) -> Option<CompletionParams<'a>> { +) -> Option<CompletionParams> { let (feature, offset) = feature_params_offset( workspace, params.text_document_position.text_document, @@ -192,10 +171,10 @@ pub fn completion_params<'a>( Some(CompletionParams { feature, offset }) } -pub fn reference_params<'a>( - workspace: &'a Workspace, +pub fn reference_params( + workspace: &Workspace, params: lsp_types::ReferenceParams, -) -> Option<ReferenceParams<'a>> { +) -> Option<ReferenceParams> { let (feature, offset) = feature_params_offset( workspace, params.text_document_position.text_document, @@ -210,19 +189,19 @@ pub fn reference_params<'a>( }) } -pub fn feature_params<'a>( - workspace: &'a Workspace, +pub fn feature_params( + workspace: &Workspace, text_document: lsp_types::TextDocumentIdentifier, -) -> Option<FeatureParams<'a>> { +) -> Option<FeatureParams> { let document = workspace.lookup(&text_document.uri)?; Some(FeatureParams::new(workspace, document)) } -pub fn feature_params_offset<'a>( - workspace: &'a Workspace, +pub fn feature_params_offset( + workspace: &Workspace, text_document: lsp_types::TextDocumentIdentifier, position: lsp_types::Position, -) -> Option<(FeatureParams<'a>, TextSize)> { +) -> Option<(FeatureParams, TextSize)> { let feature = feature_params(workspace, text_document)?; let offset = feature.document.line_index.offset_lsp(position)?; Some((feature, offset)) @@ -236,7 +215,6 @@ pub fn completion_resolve_info(item: &mut lsp_types::CompletionItem) -> Option<R pub fn config(value: Options) -> Config { let mut config = Config::default(); - config.root_dir = value.root_directory; config.build.program = value.build.executable.unwrap_or(config.build.program); config.build.args = value.build.args.unwrap_or(config.build.args); @@ -362,8 +340,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..d2c261c8fb 100644 --- a/support/texlab/crates/texlab/src/util/to_proto.rs +++ b/support/texlab/crates/texlab/src/util/to_proto.rs @@ -396,8 +396,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 2d4e412dc9..852d37051e 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.0" "User Commands" +.TH TEXLAB "1" "July 2024" "texlab 5.19.0" "User Commands" .SH NAME -texlab \- manual page for texlab 5.16.0 +texlab \- manual page for texlab 5.19.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 fe59b1f434..55922ca427 100644 --- a/support/texlab/texlab.pdf +++ b/support/texlab/texlab.pdf |