Fix main & improve cursor sync (#101)

This commit is contained in:
Andras Schmelczer 2025-08-25 17:15:52 +01:00 committed by GitHub
parent 81b81e30ff
commit a36a24effc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
36 changed files with 926 additions and 686 deletions

View file

@ -31,16 +31,17 @@ export class SafeFileSystemOperations implements FileSystemOperations {
this.logger.debug(`Reading file '${path}'`);
return this.safeOperation(
path,
this.decorateToHoldLock(path, async () => this.fs.read(path)),
async () =>
this.locks.withLock(path, async () => this.fs.read(path)),
"read"
);
}
public async write(path: RelativePath, content: Uint8Array): Promise<void> {
this.logger.debug(`Writing to file '${path}'`);
return this.decorateToHoldLock(path, async () =>
return this.locks.withLock(path, async () =>
this.fs.write(path, content)
)();
);
}
public async atomicUpdateText(
@ -50,9 +51,10 @@ export class SafeFileSystemOperations implements FileSystemOperations {
this.logger.debug(`Atomically updating file '${path}'`);
return this.safeOperation(
path,
this.decorateToHoldLock(path, async () =>
this.fs.atomicUpdateText(path, updater)
),
async () =>
this.locks.withLock(path, async () =>
this.fs.atomicUpdateText(path, updater)
),
"atomicUpdateText"
);
}
@ -61,32 +63,29 @@ export class SafeFileSystemOperations implements FileSystemOperations {
// Logging this would be too noisy
return this.safeOperation(
path,
this.decorateToHoldLock(path, async () =>
this.fs.getFileSize(path)
),
async () =>
this.locks.withLock(path, async () =>
this.fs.getFileSize(path)
),
"getFileSize"
);
}
public async exists(path: RelativePath): Promise<boolean> {
this.logger.debug(`Checking if file '${path}' exists`);
return this.decorateToHoldLock(path, async () =>
this.fs.exists(path)
)();
return this.locks.withLock(path, async () => this.fs.exists(path));
}
public async createDirectory(path: RelativePath): Promise<void> {
this.logger.debug(`Creating directory '${path}'`);
return this.decorateToHoldLock(path, async () =>
return this.locks.withLock(path, async () =>
this.fs.createDirectory(path)
)();
);
}
public async delete(path: RelativePath): Promise<void> {
this.logger.debug(`Deleting file '${path}'`);
return this.decorateToHoldLock(path, async () =>
this.fs.delete(path)
)();
return this.locks.withLock(path, async () => this.fs.delete(path));
}
public async rename(
@ -96,43 +95,14 @@ export class SafeFileSystemOperations implements FileSystemOperations {
this.logger.debug(`Renaming file '${oldPath}' to '${newPath}'`);
return this.safeOperation(
oldPath,
this.decorateToHoldLock([oldPath, newPath], async () =>
this.fs.rename(oldPath, newPath)
),
async () =>
this.locks.withLock([oldPath, newPath], async () =>
this.fs.rename(oldPath, newPath)
),
"rename"
);
}
/**
* Decorate an operation to ensure that the file is locked before running it
* and that the lock is released afterwards. This results in at-most one
* concurrent operation running per file.
*/
private decorateToHoldLock<T>(
pathOrPaths: RelativePath | RelativePath[],
operation: () => Promise<T>
): () => Promise<T> {
return async () => {
const paths = Array.isArray(pathOrPaths)
? pathOrPaths
: [pathOrPaths];
await Promise.all(
paths.map(async (path) => this.locks.waitForLock(path))
);
try {
return await operation();
} finally {
await Promise.all(
paths.map((path) => {
this.locks.unlock(path);
})
);
}
};
}
/**
* Decorate an operation to ensure that the file exists before running it.
* If the operation fails, it will check if the file still exists and throw