perf: use debounce for vault event

- now the returned path of all vault event is kept in waitingList and only get update when all
consecutive events are finished
- add filters to reduce duplicate count update
This commit is contained in:
AidenLx 2021-06-08 08:40:09 +08:00
parent a6a73950bb
commit a4498fdec9
5 changed files with 99 additions and 62 deletions

View file

@ -23,6 +23,7 @@
"@types/node": "^14.14.37",
"@typescript-eslint/eslint-plugin": "^4.23.0",
"@typescript-eslint/parser": "^4.23.0",
"assert-never": "^1.2.1",
"cz-conventional-changelog": "^3.3.0",
"eslint": "^7.26.0",
"eslint-config-prettier": "^8.3.0",

View file

@ -5,10 +5,11 @@ import {
AbstractFileFilter,
getParentPath,
isFolder,
isParent,
iterateItems,
withSubfolderClass,
} from 'misc';
import { AFItem, FolderItem, TAbstractFile, TFolder } from 'obsidian';
import { AFItem, FolderItem, TFolder } from 'obsidian';
const countFolderChildren = (folder: TFolder, filter: AbstractFileFilter) => {
let count = 0;
@ -19,38 +20,53 @@ const countFolderChildren = (folder: TFolder, filter: AbstractFileFilter) => {
return count;
};
/** filter out all path that is the parent of existing path */
const filterParent = (pathList: string[]): Set<string> => {
const list = Array.from(pathList);
list.sort();
for (let i = 0; i < list.length; i++) {
if (
i < list.length - 1 &&
(list[i] === list[i + 1] || isParent(list[i], list[i + 1]))
) {
list.shift();
i--;
}
}
return new Set(list);
};
/** get all parents and add to set if not exist */
const getAllParents = (path: string, set: Set<string>): void => {
let parent = getParentPath(path);
while (parent && !set.has(parent)) {
set.add(parent);
parent = getParentPath(parent);
}
};
/**
* Update folder count of target's parent
*/
export const updateCount = (
target: string | TAbstractFile,
targetList: string[],
plugin: FileExplorerNoteCount,
): void => {
if (!plugin.fileExplorer) throw new Error('fileExplorer not found');
const explorer = plugin.fileExplorer;
const iterate = (folder: TFolder) => {
if (!folder.isRoot()) {
setCount(
explorer.fileItems[folder.path] as FolderItem,
plugin.fileFilter,
);
iterate(folder.parent);
}
};
let parent: TFolder;
if (typeof target === 'string' || !target.parent) {
const filePath = typeof target === 'string' ? target : target.path;
const parentPath = getParentPath(filePath);
parent = plugin.app.vault.getAbstractFileByPath(parentPath) as TFolder;
if (!parent) {
console.error('cannot find parent: ' + parentPath);
return;
}
} else parent = target.parent;
iterate(parent);
const set = filterParent(targetList);
for (const path of targetList) {
getAllParents(path, set);
}
// set count of path
const { fileExplorer, fileFilter } = plugin;
if (!fileExplorer) {
console.error('fileExplorer missing');
return;
}
for (const path of set) {
// check if path available
if (!fileExplorer.fileItems[path]) continue;
setCount(fileExplorer.fileItems[path] as FolderItem, fileFilter);
}
// empty waitingList
targetList.length = 0;
};
export const setupCount = (plugin: FileExplorerNoteCount, revert = false) => {
@ -63,7 +79,7 @@ export const setupCount = (plugin: FileExplorerNoteCount, revert = false) => {
});
};
const setCount = (item: FolderItem, filter: AbstractFileFilter) => {
export const setCount = (item: FolderItem, filter: AbstractFileFilter) => {
if (item.file.isRoot()) return;
const count = countFolderChildren(item.file, filter);
item.titleEl.dataset['count'] = count.toString();

View file

@ -1,10 +1,10 @@
import './styles/patch.css';
import { AbstractFileFilter, getParentPath, withSubfolderClass } from 'misc';
import { FileExplorer, Plugin, TAbstractFile, TFile, TFolder } from 'obsidian';
import { dirname } from 'path-browserify';
import { AbstractFileFilter, withSubfolderClass } from 'misc';
import { FileExplorer, Plugin, TFile } from 'obsidian';
import { VaultHandler } from 'vault-handler';
import { setupCount, updateCount } from './folder-count';
import { setupCount } from './folder-count';
import { DEFAULT_SETTINGS, FENoteCountSettingTab } from './settings';
export default class FileExplorerNoteCount extends Plugin {
@ -12,34 +12,7 @@ export default class FileExplorerNoteCount extends Plugin {
fileExplorer?: FileExplorer;
onRename = (af: TAbstractFile, oldPath: string) => {
// only update when file is moved to other location
// if af is TFolder, its count will be updated when its children are renamed
if (af instanceof TFolder && dirname(af.path) === dirname(oldPath))
return;
updateCount(af, this);
const oldParent = getParentPath(oldPath);
// when file is moved alone (not with folder)
if (this.app.vault.getAbstractFileByPath(oldParent))
updateCount(oldPath, this);
};
registerVaultEvent() {
// attach events on new folder
this.registerEvent(
this.app.vault.on('create', (af) => {
updateCount(af, this);
}),
);
// include mv and rename
this.registerEvent(this.app.vault.on('rename', this.onRename));
this.registerEvent(
this.app.vault.on('delete', (af) => {
updateCount(af, this);
}),
);
}
vaultHandler = new VaultHandler(this);
initialize = (revert = false) => {
const leaves = this.app.workspace.getLeavesOfType('file-explorer');
@ -49,7 +22,7 @@ export default class FileExplorerNoteCount extends Plugin {
if (!this.fileExplorer)
this.fileExplorer = leaves[0].view as FileExplorer;
setupCount(this, revert);
if (!revert) this.registerVaultEvent();
if (!revert) this.vaultHandler.registerVaultEvent();
if (revert) {
for (const el of document.getElementsByClassName(
withSubfolderClass,

View file

@ -22,7 +22,8 @@ export const iterateItems = (
}
};
export const getParentPath = (src: string) => {
export const getParentPath = (src: string): string | null => {
if (src === '/') return null;
const path = dirname(src);
if (path === '.') return '/';
else return path;
@ -39,3 +40,11 @@ export const equals = (arr1: any, arr2: any) => {
return arr1.every((v, i) => v === arr2[i]);
};
export const isParent = (parent: string, child: string): boolean => {
if (child === parent) return false;
if (parent === '/') parent = '';
if (child === '/') child = '';
const parentTokens = parent.split('/').filter((i) => i.length);
return parentTokens.every((t, i) => child.split('/')[i] === t);
};

38
src/vault-handler.ts Normal file
View file

@ -0,0 +1,38 @@
import { updateCount } from 'folder-count';
import FileExplorerNoteCount from 'main';
import { getParentPath } from 'misc';
import { App, debounce, TAbstractFile, Vault } from 'obsidian';
export class VaultHandler {
waitingList: string[] = [];
get app(): App {
return this.plugin.app;
}
get vault(): Vault {
return this.plugin.app.vault;
}
plugin: FileExplorerNoteCount;
constructor(plugin: FileExplorerNoteCount) {
this.plugin = plugin;
}
update = debounce(
() => updateCount(this.waitingList, this.plugin),
500,
true,
);
handler = (...args: (string | TAbstractFile)[]) => {
for (const arg of args) {
const path = arg instanceof TAbstractFile ? arg.path : arg;
this.waitingList.push(getParentPath(path) ?? '/');
}
this.update();
};
registerVaultEvent = () => {
this.plugin.registerEvent(this.vault.on('create', this.handler));
this.plugin.registerEvent(this.vault.on('rename', this.handler));
this.plugin.registerEvent(this.vault.on('delete', this.handler));
};
}