mirror of
https://github.com/latex-lsp/texlab.git
synced 2025-08-04 18:58:31 +00:00
Handle local TEXINPUTS in latexmkrc
This commit is contained in:
parent
c609974cfc
commit
6dfbb02aae
12 changed files with 131 additions and 36 deletions
3
Cargo.lock
generated
3
Cargo.lock
generated
|
@ -1325,6 +1325,7 @@ dependencies = [
|
|||
name = "parser"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"distro",
|
||||
"expect-test",
|
||||
"log",
|
||||
"logos",
|
||||
|
@ -1333,6 +1334,7 @@ dependencies = [
|
|||
"regex",
|
||||
"rowan",
|
||||
"rustc-hash 2.1.0",
|
||||
"shellexpand",
|
||||
"syntax",
|
||||
"tempfile",
|
||||
"versions",
|
||||
|
@ -1782,6 +1784,7 @@ dependencies = [
|
|||
name = "syntax"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"distro",
|
||||
"itertools 0.13.0",
|
||||
"rowan",
|
||||
"rustc-hash 2.1.0",
|
||||
|
|
|
@ -28,6 +28,7 @@ rowan = "0.15.16"
|
|||
rustc-hash = "2.1.0"
|
||||
serde = { version = "1.0.219", features = ["derive"] }
|
||||
serde_json = "1.0.140"
|
||||
shellexpand = "3.1.0"
|
||||
tempfile = "3.19.1"
|
||||
titlecase = "3.3.0"
|
||||
unicode-normalization = "0.1.24"
|
||||
|
|
|
@ -21,7 +21,7 @@ percent-encoding = "2.3.0"
|
|||
regex.workspace = true
|
||||
rowan.workspace = true
|
||||
rustc-hash.workspace = true
|
||||
shellexpand = "3.1.0"
|
||||
shellexpand.workspace = true
|
||||
syntax = { path = "../syntax" }
|
||||
titlecase.workspace = true
|
||||
url.workspace = true
|
||||
|
|
|
@ -13,14 +13,14 @@ use super::ProjectRoot;
|
|||
|
||||
pub static HOME_DIR: Lazy<Option<PathBuf>> = Lazy::new(dirs::home_dir);
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Edge {
|
||||
pub source: Url,
|
||||
pub target: Url,
|
||||
pub data: EdgeData,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum EdgeData {
|
||||
DirectLink(Box<DirectLinkData>),
|
||||
FileList(Arc<ProjectRoot>),
|
||||
|
@ -28,7 +28,7 @@ pub enum EdgeData {
|
|||
Artifact,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DirectLinkData {
|
||||
pub link: semantics::tex::Link,
|
||||
pub new_root: Option<ProjectRoot>,
|
||||
|
@ -196,7 +196,11 @@ impl Graph {
|
|||
let file_name_db = &workspace.distro().file_name_db;
|
||||
let distro_files = file_names
|
||||
.iter()
|
||||
.filter_map(|name| file_name_db.get(name))
|
||||
.filter_map(|name| {
|
||||
file_name_db
|
||||
.get(name)
|
||||
.or_else(|| start.root.file_name_db.get(name))
|
||||
})
|
||||
.filter(|path| {
|
||||
home_dir.map_or(false, |dir| path.starts_with(dir))
|
||||
|| Language::from_path(path) == Some(Language::Bib)
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use distro::FileNameDB;
|
||||
use url::Url;
|
||||
|
||||
use crate::{util, DocumentData, Workspace};
|
||||
|
||||
use super::graph::HOME_DIR;
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Hash)]
|
||||
#[derive(Clone)]
|
||||
pub struct ProjectRoot {
|
||||
pub compile_dir: Url,
|
||||
pub src_dir: Url,
|
||||
|
@ -12,6 +15,7 @@ pub struct ProjectRoot {
|
|||
pub log_dir: Url,
|
||||
pub pdf_dir: Url,
|
||||
pub additional_files: Vec<Url>,
|
||||
pub file_name_db: Arc<FileNameDB>,
|
||||
}
|
||||
|
||||
impl ProjectRoot {
|
||||
|
@ -70,6 +74,7 @@ impl ProjectRoot {
|
|||
log_dir,
|
||||
pdf_dir,
|
||||
additional_files,
|
||||
file_name_db: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -108,6 +113,8 @@ impl ProjectRoot {
|
|||
|
||||
let additional_files = vec![];
|
||||
|
||||
let file_name_db = Arc::clone(&rcfile.file_name_db);
|
||||
|
||||
Some(Self {
|
||||
compile_dir,
|
||||
src_dir,
|
||||
|
@ -115,6 +122,7 @@ impl ProjectRoot {
|
|||
log_dir,
|
||||
pdf_dir,
|
||||
additional_files,
|
||||
file_name_db,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -150,6 +158,7 @@ impl ProjectRoot {
|
|||
log_dir,
|
||||
pdf_dir,
|
||||
additional_files,
|
||||
file_name_db: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -172,6 +181,7 @@ impl std::fmt::Debug for ProjectRoot {
|
|||
.field("log_dir", &self.log_dir.as_str())
|
||||
.field("pdf_dir", &self.pdf_dir.as_str())
|
||||
.field("additional_files", &self.additional_files)
|
||||
.field("file_name_db", &"...")
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::io::Write;
|
||||
|
||||
use anyhow::Result;
|
||||
use base_db::Workspace;
|
||||
use base_db::{deps::Edge, Workspace};
|
||||
use itertools::Itertools;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
|
@ -40,16 +40,11 @@ pub fn show_dependency_graph(workspace: &Workspace) -> Result<String> {
|
|||
.graphs()
|
||||
.values()
|
||||
.flat_map(|graph| &graph.edges)
|
||||
.unique()
|
||||
.unique_by(|edge| (&edge.source, &edge.target, edge_label(&edge)))
|
||||
{
|
||||
let source = &documents[&edge.source];
|
||||
let target = &documents[&edge.target];
|
||||
let label = match &edge.data {
|
||||
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>",
|
||||
};
|
||||
let label = edge_label(edge);
|
||||
|
||||
writeln!(&mut writer, "\t{source} -> {target} [label=\"{label}\"];")?;
|
||||
}
|
||||
|
@ -57,3 +52,12 @@ pub fn show_dependency_graph(workspace: &Workspace) -> Result<String> {
|
|||
writeln!(&mut writer, "}}")?;
|
||||
Ok(String::from_utf8(writer)?)
|
||||
}
|
||||
|
||||
fn edge_label(edge: &Edge) -> &str {
|
||||
match &edge.data {
|
||||
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>",
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ impl Borrow<str> for DistroFile {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Debug, PartialEq, Eq, Default)]
|
||||
pub struct FileNameDB {
|
||||
files: FxHashSet<DistroFile>,
|
||||
}
|
||||
|
@ -86,4 +86,16 @@ impl FileNameDB {
|
|||
|
||||
Ok(Self { files })
|
||||
}
|
||||
|
||||
pub fn read_dir(&mut self, dir: impl AsRef<Path>) {
|
||||
if let Ok(entries) = std::fs::read_dir(dir) {
|
||||
for file in entries
|
||||
.flatten()
|
||||
.filter(|entry| entry.file_type().map_or(false, |ty| ty.is_file()))
|
||||
.map(|entry| entry.path())
|
||||
{
|
||||
self.insert(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,16 +80,8 @@ impl Distro {
|
|||
|
||||
fn read_env_dir(file_name_db: &mut FileNameDB, env_var: &str) {
|
||||
if let Some(paths) = env::var_os(env_var) {
|
||||
for dir in env::split_paths(&paths) {
|
||||
if let Ok(entries) = std::fs::read_dir(dir) {
|
||||
for file in entries
|
||||
.flatten()
|
||||
.filter(|entry| entry.file_type().map_or(false, |ty| ty.is_file()))
|
||||
.map(|entry| entry.path())
|
||||
{
|
||||
file_name_db.insert(file);
|
||||
}
|
||||
}
|
||||
for dir in std::env::split_paths(&paths) {
|
||||
file_name_db.read_dir(dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ edition.workspace = true
|
|||
rust-version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
distro = { path = "../distro" }
|
||||
log.workspace = true
|
||||
logos = "0.15.0"
|
||||
once_cell.workspace = true
|
||||
|
@ -14,6 +15,7 @@ pathdiff = "0.2.3"
|
|||
regex.workspace = true
|
||||
rowan.workspace = true
|
||||
rustc-hash.workspace = true
|
||||
shellexpand.workspace = true
|
||||
syntax = { path = "../syntax" }
|
||||
tempfile.workspace = true
|
||||
versions = "6.3.2"
|
||||
|
|
|
@ -1,19 +1,25 @@
|
|||
use std::path::{Path, PathBuf};
|
||||
use std::{
|
||||
env,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use distro::FileNameDB;
|
||||
use syntax::latexmkrc::LatexmkrcData;
|
||||
|
||||
mod v483 {
|
||||
use std::path::Path;
|
||||
use std::{path::Path, sync::Arc};
|
||||
|
||||
use syntax::latexmkrc::LatexmkrcData;
|
||||
use tempfile::tempdir;
|
||||
|
||||
use crate::latexmkrc::change_root;
|
||||
|
||||
use super::{append_texinputs, parse_texinputs};
|
||||
|
||||
pub fn parse_latexmkrc(input: &str, src_dir: &Path) -> std::io::Result<LatexmkrcData> {
|
||||
let temp_dir = tempdir()?;
|
||||
let non_existent_tex = temp_dir.path().join("NONEXISTENT.tex");
|
||||
std::fs::write(temp_dir.path().join(".latexmkrc"), input)?;
|
||||
std::fs::write(temp_dir.path().join(".latexmkrc"), &append_texinputs(input))?;
|
||||
|
||||
// Run `latexmk -dir-report $TMPDIR/NONEXISTENT.tex` to obtain out_dir
|
||||
// and aux_dir values. We pass nonexistent file to prevent latexmk from
|
||||
|
@ -30,7 +36,9 @@ mod v483 {
|
|||
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
|
||||
let (aux_dir, out_dir) = stdout.lines().find_map(extract_dirs).ok_or_else(|| {
|
||||
let (file_name_db, mut lines) = parse_texinputs(&stdout, src_dir, temp_dir.path());
|
||||
|
||||
let (aux_dir, out_dir) = 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",
|
||||
|
@ -39,7 +47,12 @@ mod v483 {
|
|||
|
||||
let aux_dir = change_root(src_dir, temp_dir.path(), &aux_dir);
|
||||
let out_dir = change_root(src_dir, temp_dir.path(), &out_dir);
|
||||
Ok(LatexmkrcData { aux_dir, out_dir })
|
||||
|
||||
Ok(LatexmkrcData {
|
||||
aux_dir,
|
||||
out_dir,
|
||||
file_name_db: Arc::new(file_name_db),
|
||||
})
|
||||
}
|
||||
|
||||
/// Extracts $aux_dir and $out_dir from lines of the form
|
||||
|
@ -63,16 +76,16 @@ mod v483 {
|
|||
}
|
||||
|
||||
mod v484 {
|
||||
use std::{path::Path, str::Lines};
|
||||
use std::{path::Path, str::Lines, sync::Arc};
|
||||
|
||||
use syntax::latexmkrc::LatexmkrcData;
|
||||
use tempfile::tempdir;
|
||||
|
||||
use super::change_root;
|
||||
use super::{append_texinputs, change_root, parse_texinputs};
|
||||
|
||||
pub fn parse_latexmkrc(input: &str, src_dir: &Path) -> std::io::Result<LatexmkrcData> {
|
||||
let temp_dir = tempdir()?;
|
||||
std::fs::write(temp_dir.path().join(".latexmkrc"), input)?;
|
||||
std::fs::write(temp_dir.path().join(".latexmkrc"), &append_texinputs(input))?;
|
||||
|
||||
// Create an empty dummy TeX file to let latexmk continue
|
||||
std::fs::write(temp_dir.path().join("dummy.tex"), "")?;
|
||||
|
@ -85,7 +98,9 @@ mod v484 {
|
|||
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
|
||||
let (aux_dir, out_dir) = extract_dirs(stdout.lines()).ok_or_else(|| {
|
||||
let (file_name_db, lines) = parse_texinputs(&stdout, src_dir, temp_dir.path());
|
||||
|
||||
let (aux_dir, out_dir) = extract_dirs(lines).ok_or_else(|| {
|
||||
std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
"Normalized aux and out dir were not found in latexmk output",
|
||||
|
@ -95,7 +110,11 @@ mod v484 {
|
|||
let aux_dir = change_root(src_dir, temp_dir.path(), &aux_dir);
|
||||
let out_dir = change_root(src_dir, temp_dir.path(), &out_dir);
|
||||
|
||||
Ok(LatexmkrcData { aux_dir, out_dir })
|
||||
Ok(LatexmkrcData {
|
||||
aux_dir,
|
||||
out_dir,
|
||||
file_name_db: Arc::new(file_name_db),
|
||||
})
|
||||
}
|
||||
|
||||
/// Extracts $aux_dir and $out_dir from lines of the form
|
||||
|
@ -145,7 +164,7 @@ pub fn parse_latexmkrc(input: &str, src_dir: &Path) -> std::io::Result<Latexmkrc
|
|||
result
|
||||
}
|
||||
|
||||
fn change_root(src_dir: &Path, tmp_dir: &Path, out_dir: &str) -> Option<String> {
|
||||
fn change_root(src_dir: &Path, tmp_dir: &Path, out_dir: impl AsRef<Path>) -> Option<String> {
|
||||
let out_dir = tmp_dir.join(out_dir);
|
||||
let relative_to_tmp = pathdiff::diff_paths(out_dir, tmp_dir)?;
|
||||
let relative_to_src = pathdiff::diff_paths(src_dir.join(relative_to_tmp), src_dir)?;
|
||||
|
@ -155,3 +174,45 @@ fn change_root(src_dir: &Path, tmp_dir: &Path, out_dir: &str) -> Option<String>
|
|||
|
||||
Some(relative_to_src.to_str()?.to_string())
|
||||
}
|
||||
|
||||
fn append_texinputs(latexmkrc: &str) -> String {
|
||||
let mut output = String::new();
|
||||
output.push_str(latexmkrc);
|
||||
output.push('\n');
|
||||
output.push_str(r#"print "TEXINPUTS=" . $ENV{'TEXINPUTS'} . "\n";"#);
|
||||
output.push_str(r#"print "BIBINPUTS=" . $ENV{'BIBINPUTS'} . "\n";"#);
|
||||
output
|
||||
}
|
||||
|
||||
fn parse_texinputs<'a>(
|
||||
stdout: &'a str,
|
||||
src_dir: &Path,
|
||||
tmp_dir: &Path,
|
||||
) -> (FileNameDB, std::str::Lines<'a>) {
|
||||
let mut paths = Vec::new();
|
||||
let mut file_name_db = FileNameDB::default();
|
||||
|
||||
let mut lines = stdout.lines();
|
||||
while let Some(line) = lines.next() {
|
||||
if let Some(inputs) = line.strip_prefix("TEXINPUTS=") {
|
||||
paths.extend(env::split_paths(inputs));
|
||||
} else if let Some(inputs) = line.strip_prefix("BIBINPUTS=") {
|
||||
paths.extend(env::split_paths(inputs));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for path in paths
|
||||
.into_iter()
|
||||
.filter_map(|path| change_root(src_dir, tmp_dir, path))
|
||||
.map(|path| src_dir.join(shellexpand::tilde(&path).as_ref()))
|
||||
{
|
||||
eprintln!("Adding path: {path:?}");
|
||||
|
||||
file_name_db.read_dir(path);
|
||||
}
|
||||
|
||||
eprintln!("File name db: {file_name_db:?}");
|
||||
|
||||
(file_name_db, lines)
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ edition.workspace = true
|
|||
rust-version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
distro = { path = "../distro" }
|
||||
itertools.workspace = true
|
||||
rowan.workspace = true
|
||||
rustc-hash.workspace = true
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use distro::FileNameDB;
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct LatexmkrcData {
|
||||
pub aux_dir: Option<String>,
|
||||
pub out_dir: Option<String>,
|
||||
pub file_name_db: Arc<FileNameDB>,
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue