From a14efe03a84d2cddf35fbe0a88bc54cc9821cc27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20F=C3=B6rster?= Date: Sun, 26 Nov 2023 13:43:11 +0100 Subject: [PATCH] Use paths from latexmkrc if set --- Cargo.lock | 16 ++++- crates/base-db/src/document.rs | 15 +++- crates/base-db/src/graph.rs | 5 +- crates/base-db/src/workspace.rs | 69 +++++++++++++------ crates/commands/src/clean.rs | 11 ++- crates/commands/src/fwd_search.rs | 3 +- crates/distro/src/language.rs | 5 ++ crates/parser/Cargo.toml | 1 + crates/parser/src/latexmkrc.rs | 45 ++++++++++++ crates/parser/src/lib.rs | 6 +- crates/symbols/src/document.rs | 1 + crates/syntax/src/latexmkrc.rs | 5 ++ crates/syntax/src/lib.rs | 1 + crates/texlab/src/features/formatting.rs | 6 +- .../texlab/src/features/inlay_hint/label.rs | 41 ----------- 15 files changed, 149 insertions(+), 81 deletions(-) create mode 100644 crates/parser/src/latexmkrc.rs create mode 100644 crates/syntax/src/latexmkrc.rs delete mode 100644 crates/texlab/src/features/inlay_hint/label.rs diff --git a/Cargo.lock b/Cargo.lock index e8de4e359..22a409652 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1084,6 +1084,7 @@ dependencies = [ "rowan", "rustc-hash", "syntax", + "tempfile", ] [[package]] @@ -1229,6 +1230,15 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "redox_users" version = "0.4.3" @@ -1466,13 +1476,13 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.8.0" +version = "3.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" +checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" dependencies = [ "cfg-if", "fastrand", - "redox_syscall 0.3.5", + "redox_syscall 0.4.1", "rustix", "windows-sys", ] diff --git a/crates/base-db/src/document.rs b/crates/base-db/src/document.rs index c88c634d4..9f45876d7 100644 --- a/crates/base-db/src/document.rs +++ b/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, BuildError}; +use syntax::{bibtex, latex, latexmkrc::LatexmkrcData, BuildError}; use url::Url; use crate::{semantics, Config}; @@ -75,6 +75,10 @@ impl Document { DocumentData::Log(LogDocumentData { errors }) } Language::Root => DocumentData::Root, + Language::Latexmkrc => { + let data = parser::parse_latexmkrc(&text).unwrap_or_default(); + DocumentData::Latexmkrc(data) + } Language::Tectonic => DocumentData::Tectonic, }; @@ -131,6 +135,7 @@ pub enum DocumentData { Aux(AuxDocumentData), Log(LogDocumentData), Root, + Latexmkrc(LatexmkrcData), Tectonic, } @@ -166,6 +171,14 @@ impl DocumentData { None } } + + pub fn as_latexmkrc(&self) -> Option<&LatexmkrcData> { + if let DocumentData::Latexmkrc(data) = self { + Some(data) + } else { + None + } + } } #[derive(Debug, Clone)] diff --git a/crates/base-db/src/graph.rs b/crates/base-db/src/graph.rs index 46b9038de..088e0828b 100644 --- a/crates/base-db/src/graph.rs +++ b/crates/base-db/src/graph.rs @@ -138,9 +138,8 @@ impl<'a> Graph<'a> { fn add_implicit_edges(&mut self, source: &'a Document, base_dir: &Url) { if source.language == Language::Tex { - let config = &self.workspace.config().build; - let aux_dir = self.workspace.output_dir(base_dir, config.aux_dir.clone()); - let log_dir = self.workspace.output_dir(base_dir, config.log_dir.clone()); + let aux_dir = self.workspace.aux_dir(base_dir); + let log_dir = self.workspace.log_dir(base_dir); let relative_path = base_dir.make_relative(&source.uri).unwrap(); diff --git a/crates/base-db/src/workspace.rs b/crates/base-db/src/workspace.rs index ce88b7f5d..330ed5a49 100644 --- a/crates/base-db/src/workspace.rs +++ b/crates/base-db/src/workspace.rs @@ -8,6 +8,7 @@ use itertools::Itertools; use line_index::LineCol; use rowan::{TextLen, TextRange}; use rustc_hash::FxHashSet; +use syntax::latexmkrc::LatexmkrcData; use url::Url; use crate::{graph, Config, Document, DocumentData, DocumentParams, Owner}; @@ -115,22 +116,11 @@ impl Workspace { self.iter() .filter(|document| document.uri.scheme() == "file") .flat_map(|document| { - let dir1 = self.output_dir( - &self.current_dir(&document.dir), - self.config.build.aux_dir.clone(), - ); - - let dir2 = self.output_dir( - &self.current_dir(&document.dir), - self.config.build.log_dir.clone(), - ); - - let dir3 = &document.dir; - [ - dir1.to_file_path(), - dir2.to_file_path(), - dir3.to_file_path(), - ] + let current_dir = &self.current_dir(&document.dir); + let doc_dir = document.dir.to_file_path(); + let aux_dir = self.aux_dir(current_dir).to_file_path(); + let log_dir = self.log_dir(current_dir).to_file_path(); + [aux_dir, log_dir, doc_dir] }) .flatten() .for_each(|path| { @@ -154,13 +144,45 @@ impl Workspace { .unwrap_or_else(|| base_dir.clone()) } - pub fn output_dir(&self, base_dir: &Url, relative_path: String) -> Url { - let mut path = relative_path; - if !path.ends_with('/') { - path.push('/'); + pub fn aux_dir(&self, base_dir: &Url) -> Url { + self.output_dir(base_dir, &self.config.build.aux_dir, |data| { + data.aux_dir.as_deref() + }) + } + + pub fn log_dir(&self, base_dir: &Url) -> Url { + self.output_dir(base_dir, &self.config.build.log_dir, |_| None) + } + + pub fn pdf_dir(&self, base_dir: &Url) -> Url { + self.output_dir(base_dir, &self.config.build.pdf_dir, |_| None) + } + + fn current_latexmkrc(&self, base_dir: &Url) -> Option<&LatexmkrcData> { + self.documents + .iter() + .filter(|document| document.language == Language::Latexmkrc) + .find(|document| document.uri.join(".").as_ref() == Ok(base_dir)) + .and_then(|document| document.data.as_latexmkrc()) + } + + fn output_dir( + &self, + base_dir: &Url, + config: &str, + extract_latexmkrc: impl FnOnce(&LatexmkrcData) -> Option<&str>, + ) -> Url { + let mut dir: String = self + .current_latexmkrc(base_dir) + .and_then(|data| extract_latexmkrc(data).or_else(|| data.out_dir.as_deref())) + .unwrap_or(config) + .into(); + + if !dir.ends_with('/') { + dir.push('/'); } - base_dir.join(&path).unwrap_or_else(|_| base_dir.clone()) + base_dir.join(&dir).unwrap_or_else(|_| base_dir.clone()) } pub fn contains(&self, path: &Path) -> bool { @@ -299,7 +321,10 @@ impl Workspace { continue; }; - if !matches!(lang, Language::Tex | Language::Root | Language::Tectonic) { + if !matches!( + lang, + Language::Tex | Language::Root | Language::Tectonic | Language::Latexmkrc + ) { continue; } diff --git a/crates/commands/src/clean.rs b/crates/commands/src/clean.rs index 562d9c49b..706e7dfaf 100644 --- a/crates/commands/src/clean.rs +++ b/crates/commands/src/clean.rs @@ -29,18 +29,15 @@ impl CleanCommand { }; let out_dir = match target { - CleanTarget::Auxiliary => &workspace.config().build.aux_dir, - CleanTarget::Artifacts => &workspace.config().build.pdf_dir, + CleanTarget::Auxiliary => workspace.aux_dir(&base_dir), + CleanTarget::Artifacts => workspace.pdf_dir(&base_dir), }; - let out_dir = workspace - .output_dir(&base_dir, out_dir.clone()) - .to_file_path() - .unwrap(); + let out_dir_path = out_dir.to_file_path().unwrap(); let executable = String::from("latexmk"); let args = vec![ - format!("-outdir={}", out_dir.display()), + format!("-outdir={}", out_dir_path.display()), String::from(flag), path.display().to_string(), ]; diff --git a/crates/commands/src/fwd_search.rs b/crates/commands/src/fwd_search.rs index 9004ef9b0..7e82f3848 100644 --- a/crates/commands/src/fwd_search.rs +++ b/crates/commands/src/fwd_search.rs @@ -58,9 +58,8 @@ impl ForwardSearch { return Err(ForwardSearchError::NotLocal(parent.uri.clone())); } - let dir = workspace.current_dir(&parent.dir); let dir = workspace - .output_dir(&dir, workspace.config().build.pdf_dir.clone()) + .pdf_dir(&workspace.current_dir(&parent.dir)) .to_file_path() .unwrap(); diff --git a/crates/distro/src/language.rs b/crates/distro/src/language.rs index 207b70058..201ed0702 100644 --- a/crates/distro/src/language.rs +++ b/crates/distro/src/language.rs @@ -7,6 +7,7 @@ pub enum Language { Aux, Log, Root, + Latexmkrc, Tectonic, } @@ -21,6 +22,10 @@ impl Language { return Some(Self::Tectonic); } + if name.eq_ignore_ascii_case(".latexmkrc") || name.eq_ignore_ascii_case("latexmkrc") { + return Some(Self::Latexmkrc); + } + let extname = path.extension()?.to_str()?; match extname.to_lowercase().as_str() { "tex" | "sty" | "cls" | "def" | "lco" | "rnw" => Some(Self::Tex), diff --git a/crates/parser/Cargo.toml b/crates/parser/Cargo.toml index 4652d0944..d1e571401 100644 --- a/crates/parser/Cargo.toml +++ b/crates/parser/Cargo.toml @@ -13,6 +13,7 @@ regex = "1.9.1" rowan = "0.15.13" rustc-hash = "1.1.0" syntax = { path = "../syntax" } +tempfile = "3.8.1" [dev-dependencies] expect-test = "1.4.1" diff --git a/crates/parser/src/latexmkrc.rs b/crates/parser/src/latexmkrc.rs new file mode 100644 index 000000000..e849c96c4 --- /dev/null +++ b/crates/parser/src/latexmkrc.rs @@ -0,0 +1,45 @@ +use std::io::Write; + +use syntax::latexmkrc::LatexmkrcData; +use tempfile::tempdir; + +/// Extra section at the bottom of the latexmkrc file to print the values of $aux_dir and $log_dir. +const EXTRA: &str = r#" +print "texlab:aux_dir=" . $aux_dir . "\n"; +print "texlab:out_dir=" . $out_dir . "\n"; +exit 0; # Don't build the document"#; + +pub fn parse_latexmkrc(input: &str) -> std::io::Result { + let temp_dir = tempdir()?; + let rc_path = temp_dir.path().join("latexmkrc"); + let mut rc_file = std::fs::File::create(&rc_path)?; + rc_file.write_all(input.as_bytes())?; + rc_file.write_all(EXTRA.as_bytes())?; + drop(rc_file); + + let output = std::process::Command::new("latexmk") + .arg("-r") + .arg(rc_path) + .output()?; + + let mut result = LatexmkrcData::default(); + let stdout = String::from_utf8_lossy(&output.stdout); + + for line in stdout.lines() { + result.aux_dir = result + .aux_dir + .or_else(|| extract_dir(line, "texlab:aux_dir=")); + + result.out_dir = result + .out_dir + .or_else(|| extract_dir(line, "texlab:out_dir=")); + } + + Ok(result) +} + +fn extract_dir(line: &str, key: &str) -> Option { + line.strip_prefix(key) + .filter(|path| !path.is_empty()) + .map(String::from) +} diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index fab576a04..2a4190b7e 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -2,5 +2,9 @@ mod bibtex; mod build_log; mod config; mod latex; +mod latexmkrc; -pub use self::{bibtex::parse_bibtex, build_log::parse_build_log, config::*, latex::parse_latex}; +pub use self::{ + bibtex::parse_bibtex, build_log::parse_build_log, config::*, latex::parse_latex, + latexmkrc::parse_latexmkrc, +}; diff --git a/crates/symbols/src/document.rs b/crates/symbols/src/document.rs index b34974e01..e8f108705 100644 --- a/crates/symbols/src/document.rs +++ b/crates/symbols/src/document.rs @@ -22,6 +22,7 @@ pub fn document_symbols(workspace: &Workspace, document: &Document) -> Vec Vec::new(), }; diff --git a/crates/syntax/src/latexmkrc.rs b/crates/syntax/src/latexmkrc.rs new file mode 100644 index 000000000..41cc5b9bb --- /dev/null +++ b/crates/syntax/src/latexmkrc.rs @@ -0,0 +1,5 @@ +#[derive(Debug, Clone, Default)] +pub struct LatexmkrcData { + pub aux_dir: Option, + pub out_dir: Option, +} diff --git a/crates/syntax/src/lib.rs b/crates/syntax/src/lib.rs index dc8761b73..35f1bb8c5 100644 --- a/crates/syntax/src/lib.rs +++ b/crates/syntax/src/lib.rs @@ -1,5 +1,6 @@ pub mod bibtex; pub mod latex; +pub mod latexmkrc; #[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Hash)] pub enum BuildErrorLevel { diff --git a/crates/texlab/src/features/formatting.rs b/crates/texlab/src/features/formatting.rs index f16ee8e24..9073b04aa 100644 --- a/crates/texlab/src/features/formatting.rs +++ b/crates/texlab/src/features/formatting.rs @@ -24,6 +24,10 @@ pub fn format_source_code( Formatter::Server => format_bibtex_internal(workspace, document, options), Formatter::LatexIndent => format_with_latexindent(workspace, document), }, - Language::Aux | Language::Log | Language::Root | Language::Tectonic => None, + Language::Aux + | Language::Log + | Language::Root + | Language::Latexmkrc + | Language::Tectonic => None, } } diff --git a/crates/texlab/src/features/inlay_hint/label.rs b/crates/texlab/src/features/inlay_hint/label.rs deleted file mode 100644 index f73d3fd9a..000000000 --- a/crates/texlab/src/features/inlay_hint/label.rs +++ /dev/null @@ -1,41 +0,0 @@ -use base_db::{ - semantics::tex::LabelKind, - util::{render_label, RenderedObject}, - DocumentData, -}; - -use super::InlayHintBuilder; - -pub(super) fn find_hints(builder: &mut InlayHintBuilder) -> Option<()> { - let DocumentData::Tex(data) = &builder.document.data else { return None }; - - let range = builder.range; - for label in data - .semantics - .labels - .iter() - .filter(|label| label.kind == LabelKind::Definition) - .filter(|label| label.name.range.intersect(range).is_some()) - { - let Some(rendered) = render_label(builder.workspace, &builder.project, label) else { continue }; - let Some(number) = &rendered.number else { continue }; - - let text = match &rendered.object { - RenderedObject::Section { prefix, .. } => { - format!("{} {}", prefix, number) - } - RenderedObject::Float { kind, .. } => { - format!("{} {}", kind.as_str(), number) - } - RenderedObject::Theorem { kind, .. } => { - format!("{} {}", kind, number) - } - RenderedObject::Equation => format!("Equation ({})", number), - RenderedObject::EnumItem => format!("Item {}", number), - }; - - builder.push(label.full_range.end(), text); - } - - Some(()) -}