1
- /*
1
+ /*
2
2
* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
3
3
* License: MS-RSL – see LICENSE.md for details
4
4
*/
7
7
LaTeX Editor Actions.
8
8
*/
9
9
10
-
11
10
// cSpell:ignore rtex cmdl ramdisk maketitle documentclass outdirflag latexer rescan
12
11
13
12
const MINIMAL = `\\documentclass{article}
@@ -106,7 +105,7 @@ interface LatexEditorState extends CodeEditorState {
106
105
switch_output_to_pdf_tab ?: boolean ; // used for SyncTeX to switch output panel to PDF tab
107
106
output_panel_id_for_sync ?: string ; // stores the output panel ID for SyncTeX operations
108
107
// job_infos: JobInfos;
109
- sync_in_progress ?: boolean ; // flag to prevent sync loops - true when any sync operation is in progress
108
+ autoSyncInProgress ?: boolean ; // unified flag to prevent sync loops - true when any auto sync operation is in progress
110
109
}
111
110
112
111
export class Actions extends BaseActions < LatexEditorState > {
@@ -136,8 +135,6 @@ export class Actions extends BaseActions<LatexEditorState> {
136
135
private canonical_paths : { [ path : string ] : string } = { } ;
137
136
private parsed_output_log ?: IProcessedLatexLog ;
138
137
139
- // Flag to prevent infinite sync loops - use local variable for immediate sync control
140
- private _sync_in_progress = false ;
141
138
private _last_sync_time = 0 ;
142
139
143
140
// Auto-sync function for cursor position changes (forward sync: source → PDF)
@@ -146,19 +143,26 @@ export class Actions extends BaseActions<LatexEditorState> {
146
143
column : number ,
147
144
filename : string ,
148
145
) : Promise < void > {
149
- if ( this . _sync_in_progress ) {
146
+ if ( this . is_auto_sync_in_progress ( ) ) {
150
147
return ; // Prevent sync loops
151
148
}
152
149
153
- this . _sync_in_progress = true ;
154
- this . set_sync_in_progress ( true ) ; // Also update state for UI
150
+ this . set_auto_sync_in_progress ( true ) ;
155
151
try {
156
152
await this . synctex_tex_to_pdf ( line , column , filename ) ;
153
+
154
+ // Fallback: Clear flag after timeout if viewport change doesn't happen
155
+ setTimeout ( ( ) => {
156
+ if ( this . is_auto_sync_in_progress ( ) ) {
157
+ this . set_auto_sync_in_progress ( false ) ;
158
+ }
159
+ } , 2000 ) ;
160
+
161
+ // Note: The autoSyncInProgress flag will be cleared when PDF viewport actually changes
157
162
} catch ( error ) {
158
163
console . warn ( "Auto-sync forward search failed:" , error ) ;
159
- } finally {
160
- this . _sync_in_progress = false ;
161
- this . set_sync_in_progress ( false ) ; // Also update state for UI
164
+ // Clear flag on error since viewport won't change
165
+ this . set_auto_sync_in_progress ( false ) ;
162
166
}
163
167
}
164
168
@@ -1332,8 +1336,20 @@ export class Actions extends BaseActions<LatexEditorState> {
1332
1336
this . update_gutters ( ) ;
1333
1337
}
1334
1338
1335
- async synctex_pdf_to_tex ( page : number , x : number , y : number ) : Promise < void > {
1336
- // Note: Flag management is handled by the caller
1339
+ async synctex_pdf_to_tex (
1340
+ page : number ,
1341
+ x : number ,
1342
+ y : number ,
1343
+ manual : boolean = false ,
1344
+ ) : Promise < void > {
1345
+ // Only check auto sync flag for automatic sync, not manual double-clicks
1346
+ if ( ! manual && this . is_auto_sync_in_progress ( ) ) {
1347
+ return ; // Prevent sync loops
1348
+ }
1349
+
1350
+ if ( ! manual ) {
1351
+ this . set_auto_sync_in_progress ( true ) ;
1352
+ }
1337
1353
this . set_status ( "Running SyncTex..." ) ;
1338
1354
try {
1339
1355
const info = await synctex . pdf_to_tex ( {
@@ -1365,10 +1381,18 @@ export class Actions extends BaseActions<LatexEditorState> {
1365
1381
this . set_error (
1366
1382
'Synctex failed to run. Try "Force Rebuild" your project (use the Build frame) or retry once the build is complete.' ,
1367
1383
) ;
1384
+ // Clear flag since sync failed (only for automatic sync)
1385
+ if ( ! manual ) {
1386
+ this . set_auto_sync_in_progress ( false ) ;
1387
+ }
1368
1388
return ;
1369
1389
}
1370
1390
console . warn ( "ERROR " , err ) ;
1371
1391
this . set_error ( err ) ;
1392
+ // Clear flag since sync failed (only for automatic sync)
1393
+ if ( ! manual ) {
1394
+ this . set_auto_sync_in_progress ( false ) ;
1395
+ }
1372
1396
} finally {
1373
1397
this . set_status ( "" ) ;
1374
1398
}
@@ -1381,6 +1405,13 @@ export class Actions extends BaseActions<LatexEditorState> {
1381
1405
if ( this . knitr ) {
1382
1406
// #v0 will not support multi-file knitr.
1383
1407
this . programmatically_goto_line ( line , true , true ) ;
1408
+ // Clear auto sync flag after cursor has moved (backward sync completion)
1409
+ // Only for automatic sync - manual sync doesn't set the flag
1410
+ if ( this . is_auto_sync_in_progress ( ) ) {
1411
+ setTimeout ( ( ) => {
1412
+ this . set_auto_sync_in_progress ( false ) ;
1413
+ } , 200 ) ; // Give time for cursor to actually move
1414
+ }
1384
1415
return ;
1385
1416
}
1386
1417
// Focus a cm frame so that we split a code editor below.
@@ -1393,6 +1424,14 @@ export class Actions extends BaseActions<LatexEditorState> {
1393
1424
throw Error ( `actions for "${ path } " must be defined` ) ;
1394
1425
}
1395
1426
( actions as BaseActions ) . programmatically_goto_line ( line , true , true , id ) ;
1427
+
1428
+ // Clear auto sync flag after cursor has moved (backward sync completion)
1429
+ // Only for automatic sync - manual sync doesn't set the flag
1430
+ if ( this . is_auto_sync_in_progress ( ) ) {
1431
+ setTimeout ( ( ) => {
1432
+ this . set_auto_sync_in_progress ( false ) ;
1433
+ } , 200 ) ; // Give time for cursor to actually move
1434
+ }
1396
1435
}
1397
1436
1398
1437
// Check if auto-sync is enabled for any output panel
@@ -1404,9 +1443,10 @@ export class Actions extends BaseActions<LatexEditorState> {
1404
1443
for ( const [ key , value ] of local_view_state . entrySeq ( ) ) {
1405
1444
// Only check output panels
1406
1445
if ( this . _is_output_panel ( key ) && value ) {
1407
- const autoSyncEnabled = typeof value . get === 'function'
1408
- ? value . get ( "autoSyncEnabled" )
1409
- : value . autoSyncEnabled ;
1446
+ const autoSyncEnabled =
1447
+ typeof value . get === "function"
1448
+ ? value . get ( "autoSyncEnabled" )
1449
+ : value . autoSyncEnabled ;
1410
1450
if ( autoSyncEnabled ) {
1411
1451
return true ;
1412
1452
}
@@ -1415,25 +1455,22 @@ export class Actions extends BaseActions<LatexEditorState> {
1415
1455
return false ;
1416
1456
}
1417
1457
1418
- // Set sync in progress flag in state (for UI components)
1419
- private set_sync_in_progress ( inProgress : boolean ) : void {
1420
- // Defer state update to avoid React rendering conflicts
1421
- setTimeout ( ( ) => {
1422
- this . setState ( { sync_in_progress : inProgress } ) ;
1423
- } , 0 ) ;
1458
+ // Set auto sync in progress flag in state
1459
+ private set_auto_sync_in_progress ( inProgress : boolean ) : void {
1460
+ this . setState ( { autoSyncInProgress : inProgress } ) ;
1424
1461
}
1425
1462
1426
- // Check if sync is currently in progress
1427
- private is_sync_in_progress ( ) : boolean {
1428
- return this . _sync_in_progress ;
1463
+ // Check if auto sync is currently in progress
1464
+ private is_auto_sync_in_progress ( ) : boolean {
1465
+ return this . store . get ( "autoSyncInProgress" ) ?? false ;
1429
1466
}
1430
1467
1431
1468
// Handle cursor movement - called by BaseActions.set_cursor_locs
1432
1469
public handle_cursor_move ( locs : any [ ] ) : void {
1433
1470
if ( ! this . is_auto_sync_enabled ( ) || locs . length === 0 ) return ;
1434
1471
1435
1472
// Prevent duplicate sync operations
1436
- if ( this . is_sync_in_progress ( ) ) return ;
1473
+ if ( this . is_auto_sync_in_progress ( ) ) return ;
1437
1474
1438
1475
// Throttle sync operations to prevent excessive calls (max once every 500ms)
1439
1476
const now = Date . now ( ) ;
@@ -1457,7 +1494,7 @@ export class Actions extends BaseActions<LatexEditorState> {
1457
1494
if ( ! this . is_auto_sync_enabled ( ) ) return ;
1458
1495
1459
1496
// Prevent duplicate sync operations
1460
- if ( this . is_sync_in_progress ( ) ) return ;
1497
+ if ( this . is_auto_sync_in_progress ( ) ) return ;
1461
1498
1462
1499
// Use current file if filename not provided
1463
1500
const file = filename || this . path ;
@@ -1474,7 +1511,10 @@ export class Actions extends BaseActions<LatexEditorState> {
1474
1511
1475
1512
_get_most_recent_output_panel ( ) : string | undefined {
1476
1513
let result = this . _get_most_recent_active_frame_id_of_type ( "output" ) ;
1477
- console . log ( "LaTeX: _get_most_recent_output_panel() via active history returning" , result ) ;
1514
+ console . log (
1515
+ "LaTeX: _get_most_recent_output_panel() via active history returning" ,
1516
+ result ,
1517
+ ) ;
1478
1518
1479
1519
// If no recently active output panel found, look for any output panel
1480
1520
if ( ! result ) {
@@ -1546,20 +1586,22 @@ export class Actions extends BaseActions<LatexEditorState> {
1546
1586
this . _get_most_recent_output_panel ( ) ;
1547
1587
let pdfjs_id : string | undefined ;
1548
1588
1549
- console . log ( "LaTeX forward sync: output_panel_id =" , output_panel_id ) ;
1589
+ // console.log("LaTeX forward sync: output_panel_id =", output_panel_id);
1550
1590
1551
1591
if ( output_panel_id ) {
1552
1592
// There's an output panel - switch it to PDF tab and use it
1553
- console . log ( "LaTeX forward sync: Using output panel" , output_panel_id ) ;
1593
+ // console.log("LaTeX forward sync: Using output panel", output_panel_id);
1554
1594
this . _switch_output_panel_to_pdf ( output_panel_id ) ;
1555
1595
pdfjs_id = output_panel_id ;
1556
1596
} else {
1557
1597
// No output panel, look for standalone PDF viewer
1558
- console . log ( "LaTeX forward sync: No output panel found, looking for standalone PDFJS" ) ;
1598
+ // console.log(
1599
+ // "LaTeX forward sync: No output panel found, looking for standalone PDFJS",
1600
+ // );
1559
1601
pdfjs_id = this . _get_most_recent_pdfjs ( ) ;
1560
1602
if ( ! pdfjs_id ) {
1561
1603
// no pdfjs preview, so make one
1562
- console . log ( "LaTeX forward sync: Creating new PDFJS panel" ) ;
1604
+ // console.log("LaTeX forward sync: Creating new PDFJS panel");
1563
1605
this . split_frame ( "col" , this . _get_active_id ( ) , "pdfjs_canvas" ) ;
1564
1606
pdfjs_id = this . _get_most_recent_pdfjs ( ) ;
1565
1607
if ( ! pdfjs_id ) {
0 commit comments