Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Infer output paths from latexmkrc if possible #968

Merged
merged 4 commits into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- Infer `texlab.build.auxDirectory`, `texlab.build.logDirectory` and `texlab.build.pdfDirectory` from `latexmkrc` if available ([#907](https://github.com/latex-lsp/texlab/issues/907))

### Fixed

- Do not report `undefined reference` errors when using `\nocite{*}` ([#964](https://github.com/latex-lsp/texlab/issues/964))
Expand Down
16 changes: 13 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 14 additions & 1 deletion crates/base-db/src/document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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,
};

Expand Down Expand Up @@ -131,6 +135,7 @@ pub enum DocumentData {
Aux(AuxDocumentData),
Log(LogDocumentData),
Root,
Latexmkrc(LatexmkrcData),
Tectonic,
}

Expand Down Expand Up @@ -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)]
Expand Down
5 changes: 2 additions & 3 deletions crates/base-db/src/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
69 changes: 47 additions & 22 deletions crates/base-db/src/workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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| {
Expand All @@ -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 {
Expand Down Expand Up @@ -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;
}

Expand Down
11 changes: 4 additions & 7 deletions crates/commands/src/clean.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
];
Expand Down
3 changes: 1 addition & 2 deletions crates/commands/src/fwd_search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
5 changes: 5 additions & 0 deletions crates/distro/src/language.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub enum Language {
Aux,
Log,
Root,
Latexmkrc,
Tectonic,
}

Expand All @@ -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),
Expand Down
1 change: 1 addition & 0 deletions crates/parser/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
51 changes: 51 additions & 0 deletions crates/parser/src/latexmkrc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use syntax::latexmkrc::LatexmkrcData;
use tempfile::tempdir;

pub fn parse_latexmkrc(_input: &str) -> std::io::Result<LatexmkrcData> {
let temp_dir = tempdir()?;
let non_existent_tex = temp_dir.path().join("NONEXISTENT.tex");

// Run `latexmk -dir-report $TMPDIR/NONEXISTENT.tex` to obtain out_dir
// and aux_dir values. We pass nonexistent file to prevent latexmk from
// building anything, since we need this invocation only to extract the
// -dir-report variables.
//
// In the future, latexmk plans to implement -dir-report-only option and we
// won't have to resort to this hack with NONEXISTENT.tex.
let output = std::process::Command::new("latexmk")
.arg("-dir-report")
.arg(non_existent_tex)
.output()?;

let stdout = String::from_utf8_lossy(&output.stdout);

let (aux_dir, out_dir) = stdout
.lines()
.filter_map(extract_dirs)
.next()
.expect("Normalized aux and out dir were not found in latexmk output");

Ok(LatexmkrcData {
aux_dir: Some(aux_dir),
out_dir: Some(out_dir),
})
}

/// Extracts $aux_dir and $out_dir from lines of the form
///
/// Latexmk: Normalized aux dir and out dir: '$aux_dir', '$out_dir'
fn extract_dirs(line: &str) -> Option<(String, String)> {
let mut it = line
.strip_prefix("Latexmk: Normalized aux dir and out dir: ")?
.split(", ");

let aux_dir = it.next()?.strip_prefix('\'')?.strip_suffix('\'')?;
let out_dir = it.next()?.strip_prefix('\'')?.strip_suffix('\'')?;

// Ensure there's no more data
if it.next().is_some() {
return None;
}

Some((String::from(aux_dir), String::from(out_dir)))
}
6 changes: 5 additions & 1 deletion crates/parser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
1 change: 1 addition & 0 deletions crates/symbols/src/document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub fn document_symbols(workspace: &Workspace, document: &Document) -> Vec<Symbo
DocumentData::Aux(_)
| DocumentData::Log(_)
| DocumentData::Root
| DocumentData::Latexmkrc(_)
| DocumentData::Tectonic => Vec::new(),
};

Expand Down
5 changes: 5 additions & 0 deletions crates/syntax/src/latexmkrc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#[derive(Debug, Clone, Default)]
pub struct LatexmkrcData {
pub aux_dir: Option<String>,
pub out_dir: Option<String>,
}
1 change: 1 addition & 0 deletions crates/syntax/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down
6 changes: 5 additions & 1 deletion crates/texlab/src/features/formatting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
}
Loading