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
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,12 @@
"scope": "machine",
"markdownDescription": "%configuration.compileOnSave.enabled.markdownDescription%"
},
"overleaf-workshop.localReplica.syncOnFileChange.enabled": {
"type": "boolean",
"default": false,
"scope": "machine",
"markdownDescription": "%configuration.localReplica.syncOnFileChange.enabled.markdownDescription%"
},
"overleaf-workshop.compileOutputFolderName": {
"type": "string",
"default": ".output",
Expand Down
3 changes: 2 additions & 1 deletion package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
"commands.collaboration.jumpToUser.title": "Jump to Collaborator ...",

"configuration.compileOnSave.enabled.markdownDescription": "Always update the compiled PDF when a file is saved.",
"configuration.localReplica.syncOnFileChange.enabled.markdownDescription": "Sync Local Replica changes made by external tools. (Take effect after restarting VSCode)",
"configuration.compileOutputFolderName.markdownDescription": "The name of the folder where the compiled output files (e.g., `output.pdf`) is located. (Take effect after restarting VSCode)",
"configuration.pdfViewer.themes.markdownDescription": "Configure the color themes used by the PDF viewer. (Take effect after restarting VSCode)",
"configuration.pdfViewer.themes.theme.description": "The name of the theme.",
Expand All @@ -69,4 +70,4 @@
"views.explorer.overleaf-workshop.chatWebview.contextualTitle": "Overleaf Chat",

"customEditors.overleaf-workshop.pdfViewer.displayName": "Overleaf Workshop PDF Viewer"
}
}
24 changes: 13 additions & 11 deletions src/scm/localReplicaSCM.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ export class LocalReplicaSCMProvider extends BaseSCM {
private baseCache: {[key:string]: Uint8Array} = {};
private vfsWatcher?: vscode.FileSystemWatcher;
private localWatcher?: vscode.FileSystemWatcher;
private saveListener?: vscode.Disposable;
private ignorePatterns: string[] = [
'**/.*',
'**/.*/**',
Expand Down Expand Up @@ -376,24 +375,27 @@ export class LocalReplicaSCMProvider extends BaseSCM {
);
await this.overwrite();

// Listen for explicit user saves (not file system changes) to push local edits.
// File system watchers would also fire for git operations, compilation outputs,
// and other external modifications, causing unwanted sync (issues #299, #323).
this.saveListener = vscode.workspace.onDidSaveTextDocument(
doc => this.onDocumentSaved(doc)
);
const syncOnFileChange = vscode.workspace
.getConfiguration('overleaf-workshop.localReplica.syncOnFileChange')
.get<boolean>('enabled', false);

// By default, only explicit editor saves push file modifications. Users who
// rely on external tools can opt into file-system change events instead.
// Keep these listeners mutually exclusive so an editor save is not pushed twice.
const localChangeListener = syncOnFileChange
? this.localWatcher.onDidChange(async uri => await this.syncToVFS(uri, 'update'))
: vscode.workspace.onDidSaveTextDocument(doc => this.onDocumentSaved(doc));

return [
// sync from vfs to local
this.vfsWatcher.onDidChange(async uri => await this.syncFromVFS(uri, 'update')),
this.vfsWatcher.onDidCreate(async uri => await this.syncFromVFS(uri, 'update')),
this.vfsWatcher.onDidDelete(async uri => await this.syncFromVFS(uri, 'delete')),
// sync from local to vfs: file updates via editor saves (onDidSaveTextDocument above),
// file creation and deletion still via watcher (these are explicit user actions)
// sync from local to vfs: file updates use the configured listener above;
// file creation and deletion still always use the file-system watcher
localChangeListener,
this.localWatcher.onDidCreate(async uri => await this.syncToVFS(uri, 'update')),
this.localWatcher.onDidDelete(async uri => await this.syncToVFS(uri, 'delete')),
// include save listener for proper disposal
this.saveListener,
];
}

Expand Down