-
Notifications
You must be signed in to change notification settings - Fork 3
/
LatexCompilerManager.ts
176 lines (146 loc) · 7.52 KB
/
LatexCompilerManager.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
import * as vscode from "vscode";
import * as fs from "fs";
import * as path from "path";
import { InteractiveLatexDocument } from "../InteractiveLatexDocument";
export class LatexCompilerManager {
private ilatexDocument: InteractiveLatexDocument;
private buildTaskIsRunning: boolean;
private lastBuildFailed: boolean;
private hasAlreadrBuiltPdfOnce: boolean;
constructor(ilatexDocument: InteractiveLatexDocument) {
this.ilatexDocument = ilatexDocument;
this.buildTaskIsRunning = false;
this.lastBuildFailed = false;
this.hasAlreadrBuiltPdfOnce = false;
}
get pdfUri(): vscode.Uri {
return vscode.Uri.file(this.ilatexDocument.mainSourceFileUri.path.replace(".tex", ".pdf"));
}
get compilationLogUri(): vscode.Uri {
return vscode.Uri.file(this.ilatexDocument.mainSourceFileUri.path.replace(".tex", ".log"));
}
get pdfExists(): boolean {
return fs.existsSync(this.pdfUri.path);
}
get isBuildingPDF(): boolean {
return this.buildTaskIsRunning;
}
get pdfLastModifiedDate(): Date | null {
const pdfFilePath = this.pdfUri.path;
try {
const pdfFileStats = fs.statSync(pdfFilePath, { throwIfNoEntry: true });
return pdfFileStats.mtime;
}
catch (exception) {
console.error("The stats of the PDF file could not be retrieved: an exception was encountered.", exception);
return null;
}
}
dispose(): void {
}
// Return a promise which is resolved when the compilation of the LaTeX document succeeds
// and rejected when the compilation fails, or a failing promise if a build task is already running
recompilePDF(notifyWebview: boolean = true): Promise<void> {
if (this.isBuildingPDF) {
return Promise.reject("The PDF building task did not start: at most one building task can be ran at once.");
}
return new Promise<void>((resolveCompilation, rejectCompilation) => {
if (notifyWebview) {
this.ilatexDocument.webviewManager.sendNewPDFCompilationStatus(true);
}
// Get the current timestamp
// (to later check whether the PDF was modified at a later date or not).
const timestampAtCompilationStart = Date.now();
// Create a new terminal and use it to run latexmk to build a PDF from the sources
const terminal = vscode.window.createTerminal("iLaTeX");
const observer = vscode.window.onDidCloseTerminal(closedTerminal => {
// Ensure the closed terminal is the one created just above
if (closedTerminal !== terminal) {
return;
}
// The callback will be used only once; therefore it can be removed safely
observer.dispose();
// Depending on the exit code, either resolve or reject the promise
// returned by the buildPDF method
// if (closedTerminal.exitStatus && closedTerminal.exitStatus.code !== 0) {
// this.lastBuildFailed = true;
// this.ilatexDocument.logFileManager.logCoreEvent({ event: "pdf-compilation-failure" });
// rejectCompilation("LaTeX compilation error");
// if (notifyWebview) {
// this.ilatexDocument.webviewManager.sendNewPDFCompilationStatus(false, true);
// }
// }
// Instead of relying on the exit code, which may be non-zero even if the compilation worked,
// as latexmk seems to yield non-zero code, e.g., 12, in some situations that should be treated
// as successful compilations, the following piece of code relies on the fact that the PDF file
// exists and has been modified since the compilation started
// (therefore assuming it is more recent and valid, which may not always hold!).
const pdfLastModifiedDate = this.pdfLastModifiedDate;
const pdfFileExistsAndWasModifiedAfterCompilationStarted =
pdfLastModifiedDate && (timestampAtCompilationStart < pdfLastModifiedDate.getTime());
if (!pdfFileExistsAndWasModifiedAfterCompilationStarted) {
this.lastBuildFailed = true;
this.ilatexDocument.logFileManager.logCoreEvent({ event: "pdf-compilation-failure" });
rejectCompilation("LaTeX compilation error");
if (notifyWebview) {
this.ilatexDocument.webviewManager.sendNewPDFCompilationStatus(false, true);
}
}
else {
this.lastBuildFailed = false;
this.ilatexDocument.logFileManager.logCoreEvent({ event: "pdf-compilation-success" });
resolveCompilation();
if (notifyWebview) {
this.ilatexDocument.webviewManager.sendNewPDFCompilationStatus(false, false);
}
}
this.buildTaskIsRunning = false;
this.hasAlreadrBuiltPdfOnce = true;
});
// Log the start of the compilation
this.ilatexDocument.logFileManager.logCoreEvent({ event: "pdf-compilation-start" });
// List of arguments for latexmk
const extraOptions: string[] = [
// Generate a PDF file (not a DVI)
"-pdf",
// Remove unnecessary output information
"-silent",
// Do not pause on errors
"-interaction=nonstopmode",
// Ensure a .fls file is procuded
// (required to get absolute paths with the currfile LaTeX package)
"-recorder",
// Extra options (may be empty)
` ${this.ilatexDocument.options.extraLatexmkOptions}`
];
// If the last build failed, or if this is the first build of this instance,
// force a full re-compilation
if (this.lastBuildFailed || !this.hasAlreadrBuiltPdfOnce) {
extraOptions.push(`-g`);
}
// Run latexnk to compile the document and close the terminal afterwards
// (if no exit code is specified, the exit command reuses the exit code
// of the last command ran in the terminal, i.e. in this case, latexmk)
const terminalSafeMainFilePath = this.ilatexDocument.mainSourceFileUri.path.replace(/ /g, "\\ ");
terminal.sendText(`cd ${path.dirname(terminalSafeMainFilePath)}`);
terminal.sendText(`latexmk ${extraOptions.join(" ")} ${terminalSafeMainFilePath}`);
terminal.sendText(`exit`);
})
.catch((error) => {
console.error("======= PDF COMPILATION ERROR =======")
console.log(error)
vscode.window.showErrorMessage(
"An error occured during the compilation of the document.",
{ title: "Open log" }
).then(clickedItem => {
// If the "Open log" button was clicked, open the compilation log file
// Otherwise, if the message was dismissed, do not do anything
if (clickedItem) {
vscode.window.showTextDocument(this.compilationLogUri, {
viewColumn: vscode.ViewColumn.Two
});
}
});
});
}
}