Skip to content

Commit 018fede

Browse files
committed
frontend/latex: reinstate explicit update_pdf calls, with debouncing, and manual PDF reload in output controls
1 parent 61c4059 commit 018fede

File tree

3 files changed

+56
-12
lines changed

3 files changed

+56
-12
lines changed

src/packages/frontend/frame-editors/latex-editor/actions.ts

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,9 @@ export class Actions extends BaseActions<LatexEditorState> {
144144
// PDF file watcher - watches directory for PDF file changes
145145
private pdf_watcher?: PDFWatcher;
146146

147+
// Debounced version - initialized in _init2()
148+
update_pdf: (time: number, force: boolean) => void;
149+
147150
// Auto-sync function for cursor position changes (forward sync: source → PDF)
148151
private async handle_cursor_sync_to_pdf(
149152
line: number,
@@ -179,6 +182,11 @@ export class Actions extends BaseActions<LatexEditorState> {
179182

180183
_init2(): void {
181184
this.set_gutter = this.set_gutter.bind(this);
185+
// Debounce update_pdf with 500ms delay, trailing only, has to work when PDF watcher fires during the build
186+
this.update_pdf = debounce(this._update_pdf.bind(this), 500, {
187+
leading: false,
188+
trailing: true,
189+
});
182190
if (!this.is_public) {
183191
this.init_bad_filename();
184192
this.init_ext_filename(); // safe to set before syncstring init
@@ -210,7 +218,10 @@ export class Actions extends BaseActions<LatexEditorState> {
210218
this.pdf_watcher = new PDFWatcher(
211219
this.project_id,
212220
pdfPath,
213-
this.update_pdf.bind(this),
221+
// We ignore the PDFs timestamp (mtime) and use last_save_time for consistency with build-triggered updates
222+
(_mtime: number, force: boolean) => {
223+
this.update_pdf(this.last_save_time(), force);
224+
},
214225
);
215226
await this.pdf_watcher.init();
216227
}
@@ -882,22 +893,25 @@ export class Actions extends BaseActions<LatexEditorState> {
882893
if (this._has_frame_of_type("word_count")) {
883894
run_word_count = this.word_count(time, force);
884895
}
885-
await this.run_latex(time, force);
896+
// update_pdf=false, because it is deferred until the end
897+
await this.run_latex(time, force, false);
886898
// ... and then patch the synctex file to align the source line numberings
887899
if (this.knitr) {
888900
await this.run_patch_synctex(time, force);
889901
}
890902

891903
const s = this.store.unsafe_getIn(["build_logs", "latex", "stdout"]);
904+
let update_pdf = true;
892905
if (typeof s == "string") {
893906
const is_sagetex = s.indexOf("sagetex.sty") != -1;
894907
const is_pythontex =
895908
s.indexOf("pythontex.sty") != -1 || s.indexOf("PythonTeX") != -1;
896909
if (is_sagetex || is_pythontex) {
897910
if (this.ensure_output_directory_disabled()) {
898911
// rebuild if build command changed
899-
await this.run_latex(time, true);
912+
await this.run_latex(time, true, false);
900913
}
914+
update_pdf = false;
901915
if (is_sagetex) {
902916
await this.run_sagetex(time, force);
903917
}
@@ -908,6 +922,12 @@ export class Actions extends BaseActions<LatexEditorState> {
908922
}
909923
}
910924

925+
// we suppress a cycle of loading the PDF if sagetex or pythontex runs above
926+
// because these two trigger a rebuild and update_pdf on their own at the end
927+
if (update_pdf) {
928+
this.update_pdf(time, force);
929+
}
930+
911931
if (run_word_count != null) {
912932
// and finally, wait for word count to finish -- to make clear the whole operation is done
913933
await run_word_count;
@@ -994,7 +1014,11 @@ export class Actions extends BaseActions<LatexEditorState> {
9941014
}
9951015
}
9961016

997-
private async run_latex(time: number, force: boolean): Promise<void> {
1017+
private async run_latex(
1018+
time: number,
1019+
force: boolean,
1020+
update_pdf: boolean = true,
1021+
): Promise<void> {
9981022
if (this.is_stopping) return;
9991023
let output: BuildLog;
10001024
let build_command: string | string[];
@@ -1051,6 +1075,10 @@ export class Actions extends BaseActions<LatexEditorState> {
10511075
this.check_for_fatal_error();
10521076
this.update_gutters();
10531077
this.update_gutters_soon();
1078+
// Explicit PDF reload after latex compilation
1079+
if (update_pdf) {
1080+
this.update_pdf(time, force);
1081+
}
10541082
}
10551083

10561084
// this *merges* errors from log into an eventually already existing this.parsed_output_log
@@ -1186,7 +1214,7 @@ export class Actions extends BaseActions<LatexEditorState> {
11861214
});
11871215
}
11881216

1189-
update_pdf(time: number, force: boolean): void {
1217+
private _update_pdf(time: number, force: boolean): void {
11901218
const timestamp = this.make_timestamp(time, force);
11911219
// forget currently cached pdf
11921220
this._forget_pdf_document();
@@ -1230,11 +1258,13 @@ export class Actions extends BaseActions<LatexEditorState> {
12301258
this.get_output_directory(),
12311259
);
12321260
if (hash === this._last_sagetex_hash) {
1233-
// no change - nothing to do
1261+
// no change - nothing to do except updating the pdf preview
1262+
this.update_pdf(time, force);
12341263
return;
12351264
}
12361265
} catch (err) {
12371266
this.set_error(err);
1267+
this.update_pdf(time, force);
12381268
return;
12391269
} finally {
12401270
this.set_status("");
@@ -1264,6 +1294,7 @@ export class Actions extends BaseActions<LatexEditorState> {
12641294
await this.run_latex(time + 1, force);
12651295
} catch (err) {
12661296
this.set_error(err);
1297+
this.update_pdf(time, force);
12671298
} finally {
12681299
this._last_sagetex_hash = hash;
12691300
this.set_status("");
@@ -1303,6 +1334,7 @@ export class Actions extends BaseActions<LatexEditorState> {
13031334
} catch (err) {
13041335
this.set_error(err);
13051336
// this.setState({ pythontex_error: true });
1337+
this.update_pdf(time, force);
13061338
return;
13071339
} finally {
13081340
this.set_status("");
@@ -1469,15 +1501,15 @@ export class Actions extends BaseActions<LatexEditorState> {
14691501

14701502
_get_most_recent_output_panel(): string | undefined {
14711503
let result = this._get_most_recent_active_frame_id_of_type("output");
1472-
console.log(
1473-
"LaTeX: _get_most_recent_output_panel() via active history returning",
1474-
result,
1475-
);
1504+
// console.log(
1505+
// "LaTeX: _get_most_recent_output_panel() via active history returning",
1506+
// result,
1507+
// );
14761508

14771509
// If no recently active output panel found, look for any output panel
14781510
if (!result) {
14791511
result = this._get_any_frame_id_of_type("output");
1480-
console.log("LaTeX: _get_any_frame_id_of_type() returning", result);
1512+
//console.log("LaTeX: _get_any_frame_id_of_type() returning", result);
14811513
}
14821514

14831515
return result;

src/packages/frontend/frame-editors/latex-editor/output-control-build.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
BUILD_ON_SAVE_ICON_ENABLED,
2323
BUILD_ON_SAVE_LABEL,
2424
} from "@cocalc/frontend/frame-editors/frame-tree/commands/generic-commands";
25+
import { server_time } from "@cocalc/frontend/frame-editors/generic/client";
2526
import { editor, IntlMessage } from "@cocalc/frontend/i18n";
2627
import { DARK_MODE_ICON } from "@cocalc/util/consts/ui";
2728

@@ -67,6 +68,10 @@ export function BuildControls({ actions, id, narrow }: BuildControlsProps) {
6768
set_account_table({ editor_settings: { build_on_save: !buildOnSave } });
6869
};
6970

71+
const handleReloadPdf = () => {
72+
actions.update_pdf(server_time().valueOf(), true);
73+
};
74+
7075
const buildMenuItems: MenuProps["items"] = [
7176
{
7277
key: "force-build",
@@ -83,6 +88,12 @@ export function BuildControls({ actions, id, narrow }: BuildControlsProps) {
8388
{
8489
type: "divider",
8590
},
91+
{
92+
key: "reload-pdf",
93+
label: "Reload PDF",
94+
icon: <Icon name="refresh" />,
95+
onClick: handleReloadPdf,
96+
},
8697
{
8798
key: "download-pdf",
8899
label: intl.formatMessage(COMMANDS.download_pdf.label as IntlMessage),

src/packages/frontend/frame-editors/latex-editor/pdf-watcher.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,11 @@ export class PDFWatcher {
5050

5151
// Listen for directory changes
5252
this.directory_listings.on("change", async (paths: string[]) => {
53+
if (this.directory_listings == null) return;
5354
if (paths.includes(this.watch_dir)) {
5455
try {
5556
const updatedFiles: DirectoryListingEntry[] | undefined =
56-
await this.directory_listings!.get(this.watch_dir);
57+
await this.directory_listings.get(this.watch_dir);
5758
const updatedPdfFile = updatedFiles?.find(
5859
(f) => f.name === this.pdf_filename,
5960
);

0 commit comments

Comments
 (0)