Skip to content
Merged
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ All notable changes to the "python-ply-preview" extension will be documented in
Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.


### Unreleased

* Preview files left behind when VS Code was closed during a debug session are now removed after a day.

### 0.1.0

* The PLY file is now written only when you click **View PLY**, not every time the cursor moves. This makes the debugger much more responsive with large point clouds.
Expand Down
38 changes: 38 additions & 0 deletions src/ViewPLYService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,40 @@ and o.ndim in (2, 3) and (o.ndim == 2 or o.shape[0] == 1) and o.shape[-1] * o.sh
and (o.shape[-1] in (3, 4, 6) or o.shape[-2] in (3, 4, 6))))\
(type(o).__module__ or '', type(o).__name__))((${expression}))`;

// --- Stale session cleanup ---

/** Session folders untouched for this long are considered abandoned. */
const STALE_SESSION_AGE_MS = 24 * 60 * 60 * 1000;

/**
* Removes session folders in `baseDir` that were last modified more than `maxAgeMs` ago.
* Folders are normally removed when their debug session terminates, but they are left behind when
* VS Code exits while a session is still running. Returns the removed folders.
*/
export async function removeStaleSessionDirs(baseDir: string, maxAgeMs: number, now = Date.now()): Promise<string[]> {
let entries;
try {
entries = await fs.readdir(baseDir, { withFileTypes: true });
} catch {
// Nothing has been previewed in this workspace yet
return [];
}

const removed: string[] = [];
for (const entry of entries) {
if (!entry.isDirectory()) {
continue;
}
const dir = join(baseDir, entry.name);
const { mtimeMs } = await fs.stat(dir);
if (now - mtimeMs > maxAgeMs) {
await fs.rm(dir, { recursive: true, force: true });
removed.push(dir);
}
}
return removed;
}

// --- Service Class ---

export default class ViewPLYService {
Expand All @@ -38,6 +72,10 @@ export default class ViewPLYService {

this.logInfo(`Service initialized. Storage directory: ${this.baseStorageDir}`);
vscode.debug.onDidTerminateDebugSession(this.cleanupSessionFiles.bind(this));

removeStaleSessionDirs(this.baseStorageDir, STALE_SESSION_AGE_MS)
.then(removed => removed.forEach(dir => this.logInfo(`Removed stale session directory: ${dir}`)))
.catch(error => this.logError('Error while removing stale session directories.', error));
}

/**
Expand Down
38 changes: 38 additions & 0 deletions src/test/cleanup.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import * as assert from 'assert';
import { existsSync, mkdirSync, mkdtempSync, utimesSync, writeFileSync } from 'fs';
import { tmpdir } from 'os';
import { join } from 'path';
import { removeStaleSessionDirs } from '../ViewPLYService';

const DAY_MS = 24 * 60 * 60 * 1000;

function makeSessionDir(baseDir: string, name: string, ageMs: number): string {
const dir = join(baseDir, name);
mkdirSync(dir, { recursive: true });
writeFileSync(join(dir, 'cloud.ply'), 'ply\n');
const time = new Date(Date.now() - ageMs);
utimesSync(dir, time, time);
return dir;
}

suite('Stale session cleanup', () => {
test('removes only session folders older than the max age', async () => {
const baseDir = mkdtempSync(join(tmpdir(), 'viewply-'));
const stale = makeSessionDir(baseDir, 'stale-session', 2 * DAY_MS);
const fresh = makeSessionDir(baseDir, 'fresh-session', 60 * 1000);
writeFileSync(join(baseDir, 'not-a-session.txt'), '');

const removed = await removeStaleSessionDirs(baseDir, DAY_MS);

assert.deepStrictEqual(removed, [stale]);
assert.ok(!existsSync(stale));
assert.ok(existsSync(join(fresh, 'cloud.ply')));
assert.ok(existsSync(join(baseDir, 'not-a-session.txt')));
});

test('does nothing when the preview folder does not exist', async () => {
const removed = await removeStaleSessionDirs(join(tmpdir(), 'viewply-does-not-exist'), DAY_MS);

assert.deepStrictEqual(removed, []);
});
});
Loading