Skip to content
Open
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
1 change: 1 addition & 0 deletions src/packages/frontend/cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"respawns",
"revealjs",
"Rmarkdown",
"rtex",
"rtypes",
"Sagemath",
"sagetex",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import { trimEnd } from "lodash";

import { normalize as path_normalize } from "path";

import { filename_extension } from "@cocalc/util/misc";

// Define some constants
const LOG_WRAP_LIMIT = 79;
const LATEX_WARNING_REGEX = /^LaTeX Warning: (.*)$/;
Expand All @@ -33,6 +35,26 @@ const LINES_REGEX = /lines? ([0-9]+)/;
// This is used to parse the package name from the package warnings
const PACKAGE_REGEX = /^(?:Package|Class|Module) (\b.+\b) Warning/;

// Whitelist of text file extensions that can be used with \input{} or \include{}
const ALLOWED_DEP_EXTENSIONS = [
"bbx",
"bib",
"bst",
"cbx",
"cfg",
"cls",
"def",
"lbx",
"md",
"pgf",
"rnw",
"rtex",
"sty",
"tex",
"tikz",
"txt",
] as const;

class LogText {
private lines: string[];
private row: number;
Expand Down Expand Up @@ -224,14 +246,25 @@ export class LatexParser {

addDeps(line: string): void {
line = line.trim();
// ignore absolute files
// ignore absolute files (starting with /)
if (line[0] === "/") return;
if (line[line.length - 1] === "\\") {
line = line.slice(0, line.length - 1);
}
// we only want to know about tex and bib files
const pl = line.toLowerCase(); // could be name.TEX
if (!pl.endsWith(".tex") && !pl.endsWith(".bib")) return;
// Skip files that contain a colon (like "master.pdf :")
if (line.includes(":")) return;

// Get the file extension (returns empty string if no extension)
const ext = filename_extension(line).toLowerCase();

// If there's an extension, check if it's in the whitelist
if (ext) {
if (!ALLOWED_DEP_EXTENSIONS.includes(ext as any)) {
return;
}
}
// If no extension, include it (files without extensions are allowed)

this.deps.push(line);
}

Expand Down Expand Up @@ -306,7 +339,7 @@ export class LatexParser {
const packageMatch = this.currentLine.match(PACKAGE_REGEX);
if (!packageMatch) return;
const packageName = packageMatch[1];
// Regex to get rid of the unnecesary (packagename) prefix in most multi-line warnings
// Regex to get rid of the unnecessary (packagename) prefix in most multi-line warnings
const prefixRegex = new RegExp(`(?:\\(${packageName}\\))*[\\s]*(.*)`, "i");
// After every warning message there's a blank line, let's use it
while (!!(this.currentLine = this.log.nextLine())) {
Expand Down
25 changes: 23 additions & 2 deletions src/packages/frontend/misc/latex-log-parser.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import fs from "fs";

import { LatexParser } from "@cocalc/frontend/frame-editors/latex-editor/latex-log-parser";

describe("latex-log-parser", () => {
Expand All @@ -14,7 +15,7 @@ describe("latex-log-parser", () => {
expect(parsed1.deps[1]).toEqual("subfile.tex");
});

// This is an abbrivated log of https://github.com/sagemathinc/cocalc/issues/8089
// This is an abbreviated log of https://github.com/sagemathinc/cocalc/issues/8089
test("log2", () => {
const log2 = fs.readFileSync("./misc/latex-logs/log2.txt", "utf-8");
const parsed2 = new LatexParser(log2, {
Expand All @@ -23,7 +24,27 @@ describe("latex-log-parser", () => {
const err0 = parsed2.all[0];
expect(err0.file).toEqual("ch_Euclidean.tex");
expect(err0.message).toEqual("Marginpar on page 1 moved.");
expect(parsed2.deps).toEqual(["01.tex", "02.5.tex"]);
expect(parsed2.deps).toEqual(["./livre1.bst", "01.tex", "02.5.tex"]);
expect(parsed2.files).toEqual(["livre1.tex", "ch_Euclidean.tex"]);
});

// Test for non-.tex/.bib files in dependencies (e.g., .txt files used via \input{})
test("log3", () => {
const log3 = fs.readFileSync("./misc/latex-logs/log3.txt", "utf-8");
const parsed3 = new LatexParser(log3, {
ignoreDuplicates: true,
}).parse();
expect(parsed3.deps).toEqual([
"../subdir0/file2.md",
"long-running.tex",
"long-running.txt",
"subdir/file_9",
]);
// No .tex or .bib files in parentheses, so files array is empty
expect(parsed3.files).toEqual([]);
expect(parsed3.errors).toEqual([]);
expect(parsed3.warnings).toEqual([]);
expect(parsed3.typesetting).toEqual([]);
expect(parsed3.all).toEqual([]);
});
});
27 changes: 27 additions & 0 deletions src/packages/frontend/misc/latex-logs/log3.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
(./long-running.txt) (./subdir/file_9)
(../subdir0/file2.md) [1{/var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map}]
(./long-running.aux) )</usr/share/texlive/texmf-dist/fonts/type1/public/amsfont
s/cm/cmr10.pfb>
Output written on long-running.pdf (1 page, 15440 bytes).
SyncTeX written on long-running.synctex.gz.
Transcript written on long-running.log.
Latexmk: Examining 'long-running.log'
=== TeX engine is 'pdfTeX'
Latexmk: applying rule 'pdflatex'...
Latexmk: All targets (long-running.pdf) are up-to-date
#===Dependents, and related info, for long-running.tex:
long-running.pdf :\
../subdir0/file2.md\
/etc/texmf/web2c/texmf.cnf\
/usr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmr10.pfb\
/usr/share/texlive/texmf-dist/tex/latex/base/article.cls\
/usr/share/texlive/texmf-dist/tex/latex/base/size10.clo\
/usr/share/texlive/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def\
/usr/share/texlive/texmf-dist/web2c/texmf.cnf\
/usr/share/texmf/web2c/texmf.cnf\
/var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map\
/var/lib/texmf/web2c/pdftex/pdflatex.fmt\
long-running.tex\
long-running.txt\
subdir/file_9
#===End dependents for long-running.tex: