chore(format): no intentional behaviour change - runs pretty

This commit is contained in:
Frank Harrison
2024-11-11 09:39:45 +00:00
parent 6e1eb36f3b
commit 5c97e5b672
71 changed files with 6029 additions and 3740 deletions

View File

@@ -9,7 +9,7 @@ import { markChangesAreSame } from "../../../common/utils.ts";
import { type UXFileInfo } from "../../../lib/src/common/types.ts";
function getFileLockKey(file: TFile | TFolder | string | UXFileInfo) {
return `fl:${typeof (file) == "string" ? file : file.path}`;
return `fl:${typeof file == "string" ? file : file.path}`;
}
function toArrayBuffer(arr: Uint8Array | ArrayBuffer | DataView): ArrayBufferLike {
if (arr instanceof Uint8Array) {
@@ -35,9 +35,9 @@ async function processWriteFile<T>(file: TFile | TFolder | string | UXFileInfo,
}
export class SerializedFileAccess {
app: App
plugin: HasSettings<{ handleFilenameCaseSensitive: boolean }>
constructor(app: App, plugin: typeof this["plugin"]) {
app: App;
plugin: HasSettings<{ handleFilenameCaseSensitive: boolean }>;
constructor(app: App, plugin: (typeof this)["plugin"]) {
this.app = app;
this.plugin = plugin;
}
@@ -72,10 +72,12 @@ export class SerializedFileAccess {
async adapterWrite(file: TFile | string, data: string | ArrayBuffer | Uint8Array, options?: DataWriteOptions) {
const path = file instanceof TFile ? file.path : file;
if (typeof (data) === "string") {
if (typeof data === "string") {
return await processWriteFile(file, () => this.app.vault.adapter.write(path, data, options));
} else {
return await processWriteFile(file, () => this.app.vault.adapter.writeBinary(path, toArrayBuffer(data), options));
return await processWriteFile(file, () =>
this.app.vault.adapter.writeBinary(path, toArrayBuffer(data), options)
);
}
}
@@ -97,19 +99,17 @@ export class SerializedFileAccess {
return await processReadFile(file, () => this.app.vault.readBinary(file));
}
async vaultModify(file: TFile, data: string | ArrayBuffer | Uint8Array, options?: DataWriteOptions) {
if (typeof (data) === "string") {
if (typeof data === "string") {
return await processWriteFile(file, async () => {
const oldData = await this.app.vault.read(file);
if (data === oldData) {
if (options && options.mtime) markChangesAreSame(file.path, file.stat.mtime, options.mtime);
return true;
}
await this.app.vault.modify(file, data, options)
await this.app.vault.modify(file, data, options);
return true;
}
);
});
} else {
return await processWriteFile(file, async () => {
const oldData = await this.app.vault.readBinary(file);
@@ -117,13 +117,17 @@ export class SerializedFileAccess {
if (options && options.mtime) markChangesAreSame(file.path, file.stat.mtime, options.mtime);
return true;
}
await this.app.vault.modifyBinary(file, toArrayBuffer(data), options)
await this.app.vault.modifyBinary(file, toArrayBuffer(data), options);
return true;
});
}
}
async vaultCreate(path: string, data: string | ArrayBuffer | Uint8Array, options?: DataWriteOptions): Promise<TFile> {
if (typeof (data) === "string") {
async vaultCreate(
path: string,
data: string | ArrayBuffer | Uint8Array,
options?: DataWriteOptions
): Promise<TFile> {
if (typeof data === "string") {
return await processWriteFile(path, () => this.app.vault.create(path, data, options));
} else {
return await processWriteFile(path, () => this.app.vault.createBinary(path, toArrayBuffer(data), options));
@@ -135,7 +139,7 @@ export class SerializedFileAccess {
}
async adapterAppend(normalizedPath: string, data: string, options?: DataWriteOptions) {
return await this.app.vault.adapter.append(normalizedPath, data, options)
return await this.app.vault.adapter.append(normalizedPath, data, options);
}
async delete(file: TFile | TFolder, force = false) {
@@ -145,8 +149,6 @@ export class SerializedFileAccess {
return await processWriteFile(file, () => this.app.vault.trash(file, force));
}
isStorageInsensitive(): boolean {
//@ts-ignore
return this.app.vault.adapter.insensitive ?? true;
@@ -188,22 +190,23 @@ export class SerializedFileAccess {
}
}
touchedFiles: string[] = [];
touch(file: TFile | FilePath) {
const f = file instanceof TFile ? file : this.getAbstractFileByPath(file) as TFile;
const f = file instanceof TFile ? file : (this.getAbstractFileByPath(file) as TFile);
const key = `${f.path}-${f.stat.mtime}-${f.stat.size}`;
this.touchedFiles.unshift(key);
this.touchedFiles = this.touchedFiles.slice(0, 100);
}
recentlyTouched(file: TFile | InternalFileInfo | UXFileInfoStub) {
const key = "stat" in file ? `${file.path}-${file.stat.mtime}-${file.stat.size}` : `${file.path}-${file.mtime}-${file.size}`;
const key =
"stat" in file
? `${file.path}-${file.stat.mtime}-${file.stat.size}`
: `${file.path}-${file.mtime}-${file.size}`;
if (this.touchedFiles.indexOf(key) == -1) return false;
return true;
}
clearTouched() {
this.touchedFiles = [];
}
}
}

View File

@@ -1,18 +1,32 @@
import { TAbstractFile, TFile, TFolder } from "../../../deps.ts";
import { Logger } from "../../../lib/src/common/logger.ts";
import { shouldBeIgnored } from "../../../lib/src/string_and_binary/path.ts";
import { DEFAULT_SETTINGS, LOG_LEVEL_DEBUG, LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE, type FilePath, type FilePathWithPrefix, type UXFileInfoStub, type UXInternalFileInfoStub } from "../../../lib/src/common/types.ts";
import {
DEFAULT_SETTINGS,
LOG_LEVEL_DEBUG,
LOG_LEVEL_INFO,
LOG_LEVEL_NOTICE,
LOG_LEVEL_VERBOSE,
type FilePath,
type FilePathWithPrefix,
type UXFileInfoStub,
type UXInternalFileInfoStub,
} from "../../../lib/src/common/types.ts";
import { delay, fireAndForget } from "../../../lib/src/common/utils.ts";
import { type FileEventItem, type FileEventType } from "../../../common/types.ts";
import { serialized, skipIfDuplicated } from "../../../lib/src/concurrency/lock.ts";
import { finishAllWaitingForTimeout, finishWaitingForTimeout, isWaitingForTimeout, waitForTimeout } from "../../../lib/src/concurrency/task.ts";
import {
finishAllWaitingForTimeout,
finishWaitingForTimeout,
isWaitingForTimeout,
waitForTimeout,
} from "../../../lib/src/concurrency/task.ts";
import { Semaphore } from "../../../lib/src/concurrency/semaphore.ts";
import type { LiveSyncCore } from "../../../main.ts";
import { InternalFileToUXFileInfoStub, TFileToUXFileInfoStub } from "./utilObsidian.ts";
import ObsidianLiveSyncPlugin from "../../../main.ts";
// import { InternalFileToUXFileInfo } from "../platforms/obsidian.ts";
export type FileEvent = {
type: FileEventType;
file: UXFileInfoStub | UXInternalFileInfoStub;
@@ -21,19 +35,15 @@ export type FileEvent = {
skipBatchWait?: boolean;
};
export abstract class StorageEventManager {
abstract beginWatch(): void;
abstract flushQueue(): void;
abstract appendQueue(items: FileEvent[], ctx?: any): Promise<void>;
abstract cancelQueue(key: string): void;
abstract isWaiting(filename: FilePath): boolean;
}
export class StorageEventManagerObsidian extends StorageEventManager {
plugin: ObsidianLiveSyncPlugin;
core: LiveSyncCore;
@@ -41,10 +51,10 @@ export class StorageEventManagerObsidian extends StorageEventManager {
return this.core.settings?.batchSave && this.core.settings?.liveSync != true;
}
get batchSaveMinimumDelay(): number {
return this.core.settings?.batchSaveMinimumDelay ?? DEFAULT_SETTINGS.batchSaveMinimumDelay
return this.core.settings?.batchSaveMinimumDelay ?? DEFAULT_SETTINGS.batchSaveMinimumDelay;
}
get batchSaveMaximumDelay(): number {
return this.core.settings?.batchSaveMaximumDelay ?? DEFAULT_SETTINGS.batchSaveMaximumDelay
return this.core.settings?.batchSaveMaximumDelay ?? DEFAULT_SETTINGS.batchSaveMaximumDelay;
}
constructor(plugin: ObsidianLiveSyncPlugin, core: LiveSyncCore) {
super();
@@ -83,10 +93,11 @@ export class StorageEventManagerObsidian extends StorageEventManager {
}
const data = info?.data as string;
const fi: FileEvent = {
type: "CHANGED", file: TFileToUXFileInfoStub(file), cachedData: data,
}
void this.appendQueue([
fi])
type: "CHANGED",
file: TFileToUXFileInfoStub(file),
cachedData: data,
};
void this.appendQueue([fi]);
}
watchVaultCreate(file: TAbstractFile, ctx?: any) {
@@ -109,17 +120,27 @@ export class StorageEventManagerObsidian extends StorageEventManager {
watchVaultRename(file: TAbstractFile, oldFile: string, ctx?: any) {
if (file instanceof TFile) {
const fileInfo = TFileToUXFileInfoStub(file);
void this.appendQueue([
{
type: "DELETE", file: {
path: oldFile as FilePath, name: file.name, stat: {
mtime: file.stat.mtime,
ctime: file.stat.ctime,
size: file.stat.size,
type: "file"
}, deleted: true
}, skipBatchWait: true
}, { type: "CREATE", file: fileInfo, skipBatchWait: true },], ctx);
void this.appendQueue(
[
{
type: "DELETE",
file: {
path: oldFile as FilePath,
name: file.name,
stat: {
mtime: file.stat.mtime,
ctime: file.stat.ctime,
size: file.stat.size,
type: "file",
},
deleted: true,
},
skipBatchWait: true,
},
{ type: "CREATE", file: fileInfo, skipBatchWait: true },
],
ctx
);
}
}
// Watch raw events (Internal API)
@@ -142,16 +163,23 @@ export class StorageEventManagerObsidian extends StorageEventManager {
if (!path.startsWith(this.plugin.app.vault.configDir)) return;
const ignorePatterns = this.plugin.settings.syncInternalFilesIgnorePatterns
.replace(/\n| /g, "")
.split(",").filter(e => e).map(e => new RegExp(e, "i"));
if (ignorePatterns.some(e => path.match(e))) return;
.split(",")
.filter((e) => e)
.map((e) => new RegExp(e, "i"));
if (ignorePatterns.some((e) => path.match(e))) return;
if (path.endsWith("/")) {
// Folder
// Folder
return;
}
void this.appendQueue([
{
type: "INTERNAL", file: InternalFileToUXFileInfoStub(path),
}], null);
void this.appendQueue(
[
{
type: "INTERNAL",
file: InternalFileToUXFileInfoStub(path),
},
],
null
);
}
// Cache file and waiting to can be proceed.
async appendQueue(params: FileEvent[], ctx?: any) {
@@ -164,25 +192,22 @@ export class StorageEventManagerObsidian extends StorageEventManager {
if (shouldBeIgnored(param.file.path)) {
continue;
}
const atomicKey = [
0,
0,
0,
0,
0,
0].map(e => `${Math.floor(Math.random() * 100000)}`).join("-");
const atomicKey = [0, 0, 0, 0, 0, 0].map((e) => `${Math.floor(Math.random() * 100000)}`).join("-");
const type = param.type;
const file = param.file;
const oldPath = param.oldPath;
if (type !== "INTERNAL") {
const size = (file as UXFileInfoStub).stat.size;
if (this.core.$$isFileSizeExceeded(size) && (type == "CREATE" || type == "CHANGED")) {
Logger(`The storage file has been changed but exceeds the maximum size. Skipping: ${param.file.path}`, LOG_LEVEL_NOTICE);
Logger(
`The storage file has been changed but exceeds the maximum size. Skipping: ${param.file.path}`,
LOG_LEVEL_NOTICE
);
continue;
}
}
if (file instanceof TFolder) continue;
if (!await this.core.$$isTargetFile(file.path)) continue;
if (!(await this.core.$$isTargetFile(file.path))) continue;
// Stop cache using to prevent the corruption;
// let cache: null | string | ArrayBuffer;
@@ -197,13 +222,19 @@ export class StorageEventManagerObsidian extends StorageEventManager {
let cache: string | undefined = undefined;
if (param.cachedData) {
cache = param.cachedData
cache = param.cachedData;
}
this.enqueue({
type, args: {
file: file, oldPath, cache, ctx,
}, skipBatchWait: param.skipBatchWait, key: atomicKey
})
type,
args: {
file: file,
oldPath,
cache,
ctx,
},
skipBatchWait: param.skipBatchWait,
key: atomicKey,
});
processFiles.add(file.path as FilePath);
if (oldPath) {
processFiles.add(oldPath as FilePath);
@@ -238,7 +269,7 @@ export class StorageEventManagerObsidian extends StorageEventManager {
Logger(`Processing ${filename}: Started`, LOG_LEVEL_DEBUG);
let noMoreFiles = false;
do {
const target = this.bufferedQueuedItems.find(e => e.args.file.path == filename);
const target = this.bufferedQueuedItems.find((e) => e.args.file.path == filename);
if (target === undefined) {
noMoreFiles = true;
break;
@@ -251,7 +282,7 @@ export class StorageEventManagerObsidian extends StorageEventManager {
// }
const type = target.type;
if (target.cancelled) {
Logger(`Processing ${filename}: Cancelled (scheduled): ${operationType}`, LOG_LEVEL_DEBUG)
Logger(`Processing ${filename}: Cancelled (scheduled): ${operationType}`, LOG_LEVEL_DEBUG);
this.cancelStandingBy(target);
continue;
}
@@ -261,19 +292,31 @@ export class StorageEventManagerObsidian extends StorageEventManager {
let canWait = true;
const now = Date.now();
if (waitedSince !== undefined) {
if (waitedSince + (this.batchSaveMaximumDelay * 1000) < now) {
Logger(`Processing ${filename}: Could not wait no more: ${operationType}`, LOG_LEVEL_INFO)
if (waitedSince + this.batchSaveMaximumDelay * 1000 < now) {
Logger(
`Processing ${filename}: Could not wait no more: ${operationType}`,
LOG_LEVEL_INFO
);
canWait = false;
}
}
if (canWait) {
if (waitedSince === undefined) this.waitedSince.set(filename, now)
target.batched = true
Logger(`Processing ${filename}: Waiting for batch save delay: ${operationType}`, LOG_LEVEL_DEBUG)
if (waitedSince === undefined) this.waitedSince.set(filename, now);
target.batched = true;
Logger(
`Processing ${filename}: Waiting for batch save delay: ${operationType}`,
LOG_LEVEL_DEBUG
);
this.updateStatus();
const result = await waitForTimeout(`storage-event-manager-batchsave-${filename}`, this.batchSaveMinimumDelay * 1000);
const result = await waitForTimeout(
`storage-event-manager-batchsave-${filename}`,
this.batchSaveMinimumDelay * 1000
);
if (!result) {
Logger(`Processing ${filename}: Cancelled by new queue: ${operationType}`, LOG_LEVEL_DEBUG)
Logger(
`Processing ${filename}: Cancelled by new queue: ${operationType}`,
LOG_LEVEL_DEBUG
);
// If could not wait for the timeout, possibly we got a new queue. therefore, currently processing one should be cancelled
this.cancelStandingBy(target);
continue;
@@ -281,16 +324,19 @@ export class StorageEventManagerObsidian extends StorageEventManager {
}
}
} else {
Logger(`Processing ${filename}:Requested to perform immediately ${filename}: ${operationType}`, LOG_LEVEL_DEBUG)
Logger(
`Processing ${filename}:Requested to perform immediately ${filename}: ${operationType}`,
LOG_LEVEL_DEBUG
);
}
Logger(`Processing ${filename}: Request main to process: ${operationType}`, LOG_LEVEL_DEBUG)
Logger(`Processing ${filename}: Request main to process: ${operationType}`, LOG_LEVEL_DEBUG);
await this.requestProcessQueue(target);
} while (!noMoreFiles)
} while (!noMoreFiles);
} finally {
release()
release();
}
Logger(`Processing ${filename}: Finished`, LOG_LEVEL_DEBUG);
})
});
}
cancelStandingBy(fei: FileEventItem) {
@@ -302,30 +348,30 @@ export class StorageEventManagerObsidian extends StorageEventManager {
try {
this.processingCount++;
this.bufferedQueuedItems.remove(fei);
this.updateStatus()
this.updateStatus();
this.waitedSince.delete(fei.args.file.path);
await this.handleFileEvent(fei);
} finally {
this.processingCount--;
this.updateStatus()
this.updateStatus();
}
}
isWaiting(filename: FilePath) {
return isWaitingForTimeout(`storage-event-manager-batchsave-${filename}`);
}
flushQueue() {
this.bufferedQueuedItems.forEach(e => e.skipBatchWait = true)
this.bufferedQueuedItems.forEach((e) => (e.skipBatchWait = true));
finishAllWaitingForTimeout("storage-event-manager-batchsave-", true);
}
cancelQueue(key: string) {
this.bufferedQueuedItems.forEach(e => {
if (e.key === key) e.skipBatchWait = true
})
this.bufferedQueuedItems.forEach((e) => {
if (e.key === key) e.skipBatchWait = true;
});
}
updateStatus() {
const allItems = this.bufferedQueuedItems.filter(e => !e.cancelled)
const batchedCount = allItems.filter(e => e.batched && !e.skipBatchWait).length;
this.core.batched.value = batchedCount
const allItems = this.bufferedQueuedItems.filter((e) => !e.cancelled);
const batchedCount = allItems.filter((e) => e.batched && !e.skipBatchWait).length;
this.core.batched.value = batchedCount;
this.core.processing.value = this.processingCount;
this.core.totalQueued.value = allItems.length - batchedCount;
}
@@ -336,7 +382,7 @@ export class StorageEventManagerObsidian extends StorageEventManager {
return await serialized(lockKey, async () => {
// TODO CHECK
const key = `file-last-proc-${queue.type}-${file.path}`;
const last = Number(await this.core.kvDB.get(key) || 0);
const last = Number((await this.core.kvDB.get(key)) || 0);
if (queue.type == "INTERNAL" || file.isInternal) {
await this.core.$anyProcessOptionalFileEvent(file.path as unknown as FilePath);
} else {
@@ -346,13 +392,16 @@ export class StorageEventManagerObsidian extends StorageEventManager {
} else {
if (file.stat.mtime == last) {
Logger(`File has been already scanned on ${queue.type}, skip: ${file.path}`, LOG_LEVEL_VERBOSE);
// Should Cancel the relative operations? (e.g. rename)
// Should Cancel the relative operations? (e.g. rename)
// this.cancelRelativeEvent(queue);
return;
}
if (!await this.core.$anyHandlerProcessesFileEvent(queue)) {
Logger(`STORAGE -> DB: Handler failed, cancel the relative operations: ${file.path}`, LOG_LEVEL_INFO);
// cancel running queues and remove one of atomic operation (e.g. rename)
if (!(await this.core.$anyHandlerProcessesFileEvent(queue))) {
Logger(
`STORAGE -> DB: Handler failed, cancel the relative operations: ${file.path}`,
LOG_LEVEL_INFO
);
// cancel running queues and remove one of atomic operation (e.g. rename)
this.cancelRelativeEvent(queue);
return;
}
@@ -364,4 +413,4 @@ export class StorageEventManagerObsidian extends StorageEventManager {
cancelRelativeEvent(item: FileEventItem): void {
this.cancelQueue(item.key);
}
}
}

View File

@@ -6,10 +6,22 @@ import type { SerializedFileAccess } from "./SerializedFileAccess.ts";
import { addPrefix, isPlainText } from "../../../lib/src/string_and_binary/path.ts";
import { LOG_LEVEL_VERBOSE, Logger } from "octagonal-wheels/common/logger";
import { createBlob } from "../../../lib/src/common/utils.ts";
import type { FilePath, FilePathWithPrefix, UXFileInfo, UXFileInfoStub, UXFolderInfo, UXInternalFileInfoStub } from "../../../lib/src/common/types.ts";
import type {
FilePath,
FilePathWithPrefix,
UXFileInfo,
UXFileInfoStub,
UXFolderInfo,
UXInternalFileInfoStub,
} from "../../../lib/src/common/types.ts";
import type { LiveSyncCore } from "../../../main.ts";
export async function TFileToUXFileInfo(core: LiveSyncCore, file: TFile, prefix?: string, deleted?: boolean): Promise<UXFileInfo> {
export async function TFileToUXFileInfo(
core: LiveSyncCore,
file: TFile,
prefix?: string,
deleted?: boolean
): Promise<UXFileInfo> {
const isPlain = isPlainText(file.name);
const possiblyLarge = !isPlain;
let content: Blob;
@@ -34,11 +46,14 @@ export async function TFileToUXFileInfo(core: LiveSyncCore, file: TFile, prefix?
type: "file",
},
body: content,
}
};
}
export async function InternalFileToUXFileInfo(fullPath: string, vaultAccess: SerializedFileAccess, prefix: string = ICHeader): Promise<UXFileInfo> {
export async function InternalFileToUXFileInfo(
fullPath: string,
vaultAccess: SerializedFileAccess,
prefix: string = ICHeader
): Promise<UXFileInfo> {
const name = fullPath.split("/").pop() as string;
const stat = await vaultAccess.adapterStat(fullPath);
if (stat == null) throw new Error(`File not found: ${fullPath}`);
@@ -64,7 +79,7 @@ export async function InternalFileToUXFileInfo(fullPath: string, vaultAccess: Se
type: "file",
},
body: content,
}
};
}
export function TFileToUXFileInfoStub(file: TFile | TAbstractFile, deleted?: boolean): UXFileInfoStub {
@@ -81,8 +96,8 @@ export function TFileToUXFileInfoStub(file: TFile | TAbstractFile, deleted?: boo
ctime: file.stat.ctime,
type: "file",
},
deleted: deleted
}
deleted: deleted,
};
return ret;
}
export function InternalFileToUXFileInfoStub(filename: FilePathWithPrefix, deleted?: boolean): UXInternalFileInfoStub {
@@ -93,8 +108,8 @@ export function InternalFileToUXFileInfoStub(filename: FilePathWithPrefix, delet
isFolder: false,
stat: undefined,
isInternal: true,
deleted
}
deleted,
};
return ret;
}
export function TFolderToUXFileInfoStub(file: TFolder): UXFolderInfo {
@@ -103,7 +118,7 @@ export function TFolderToUXFileInfoStub(file: TFolder): UXFolderInfo {
path: file.path as FilePathWithPrefix,
parent: file.parent?.path as FilePath | undefined,
isFolder: true,
children: file.children.map(e => TFileToUXFileInfoStub(e)),
}
children: file.children.map((e) => TFileToUXFileInfoStub(e)),
};
return ret;
}
}