From d219bcf5fde65c31d6d2e95ad9d6e9ba1a17dcae Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 6 Apr 2026 13:00:27 +0000 Subject: [PATCH] perf: reuse stable temp file per document to reduce filesystem overhead Instead of generating a random temp file name on every format call and deleting it afterwards, allocate one stable temp file path per document (keyed by document URI) on first use and overwrite it in-place on subsequent saves. Benefits: - Eliminates Math.random() + string manipulation on every save - Replaces create-then-delete with a single in-place overwrite, reducing OS inode churn when the user saves frequently - Predictable, PID-scoped names (phpcbf--.php) make temp files easier to identify and clean up if the process crashes Lifecycle: - Temp file is created on the first format of each document - Deleted in the onDidCloseTextDocument listener when the document closes - All remaining temp files are deleted synchronously in deactivate() The _phpcbf reference on exports is only for deactivation; it is not part of the public API. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- extension.js | 50 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 9 deletions(-) diff --git a/extension.js b/extension.js index 6457e71..ddf1752 100644 --- a/extension.js +++ b/extension.js @@ -17,6 +17,10 @@ const TmpDir = os.tmpdir(); class PHPCBF { constructor() { + // Map from document URI string to stable temp file path. + // Re-using the same path per document avoids creating and deleting a new + // inode on every save — the file is overwritten in-place instead. + this._tmpFiles = new Map(); this.loadSettings(); } @@ -138,14 +142,17 @@ class PHPCBF { let text = document.getText(); let phpcbfError = false; - let fileName = - TmpDir + - "/temp-" + - Math.random() - .toString(36) - .replace(/[^a-z]+/g, "") - .substr(0, 10) + - ".php"; + // Re-use a stable temp file path for this document so every save + // overwrites the same inode instead of creating and deleting a new one. + const docKey = document.uri.toString(); + let fileName = this._tmpFiles.get(docKey); + if (!fileName) { + fileName = path.join( + TmpDir, + "phpcbf-" + process.pid + "-" + this._tmpFiles.size + ".php" + ); + this._tmpFiles.set(docKey, fileName); + } fs.writeFileSync(fileName, text); let exec = cp.spawn(this.executablePath, this.getArgs(document, fileName)); @@ -197,7 +204,8 @@ class PHPCBF { break; } - fs.unlink(fileName, function (err) {}); + // Temp file is reused; deletion is deferred to document-close + // or extension deactivation — do not delete here. }); }); @@ -291,6 +299,17 @@ exports.activate = context => { }) ); + // Clean up each document's stable temp file when the document is closed. + context.subscriptions.push( + workspace.onDidCloseTextDocument(doc => { + const tmpPath = phpcbf._tmpFiles.get(doc.uri.toString()); + if (tmpPath) { + phpcbf._tmpFiles.delete(doc.uri.toString()); + fs.unlink(tmpPath, () => {}); + } + }) + ); + if (phpcbf.documentFormattingProvider) { context.subscriptions.push( languages.registerDocumentFormattingEditProvider("php", { @@ -320,4 +339,17 @@ exports.activate = context => { }) ); } + + // Store reference for deactivation cleanup. + exports._phpcbf = phpcbf; +}; + +exports.deactivate = () => { + const phpcbf = exports._phpcbf; + if (phpcbf) { + for (const tmpPath of phpcbf._tmpFiles.values()) { + try { fs.unlinkSync(tmpPath); } catch (_) {} + } + phpcbf._tmpFiles.clear(); + } };