Skip to content

Commit

Permalink
Improved:
Browse files Browse the repository at this point in the history
- Performance improvements on LinkFolder
- Now works smoothly like the previous version if LinkFolder is not shown.

Fixed:
- Now LinkFolder should follow the links correctly.
  - Indirectly referenced notes could be collected correctly.
  - Outgoing links and incoming links also be.
  • Loading branch information
vrtmrz committed Aug 27, 2023
1 parent b3965bc commit f62de8a
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 60 deletions.
12 changes: 8 additions & 4 deletions TagFolderViewBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
VIEW_TYPE_TAGFOLDER,
VIEW_TYPE_TAGFOLDER_LINK,
VIEW_TYPE_TAGFOLDER_LIST,
type TagFolderSettings,
type ViewItem
} from "./types";
import { maxDepth, selectedTags } from "./store";
Expand All @@ -30,9 +31,10 @@ export abstract class TagFolderViewBase extends ItemView {
component: TagFolderViewComponent;
plugin: TagFolderPlugin;
navigation: false;
async saveSettings() {
async saveSettings(settings: TagFolderSettings) {
this.plugin.settings = { ...this.plugin.settings, ...settings };
await this.plugin.saveSettings();
this.plugin.updateFileCaches();
this.plugin.updateFileCaches();
}
showOrder(evt: MouseEvent) {
const menu = new Menu();
Expand Down Expand Up @@ -280,6 +282,7 @@ export abstract class TagFolderViewBase extends ItemView {
menu.addItem((item) =>
item
.setTitle(`Open in new tab`)
.setSection("open")
.setIcon("lucide-file-plus")
.onClick(async () => {
app.workspace.openLinkText(path, path, "tab");
Expand All @@ -288,6 +291,7 @@ export abstract class TagFolderViewBase extends ItemView {
menu.addItem((item) =>
item
.setTitle(`Open to the right`)
.setSection("open")
.setIcon("lucide-separator-vertical")
.onClick(async () => {
app.workspace.openLinkText(path, path, "split");
Expand All @@ -307,7 +311,7 @@ export abstract class TagFolderViewBase extends ItemView {
menu.addItem((item) =>
item
.setTitle(`Open in new tab`)
.setSection("open")
.setSection("open")
.setIcon("lucide-file-plus")
.onClick(async () => {
app.workspace.openLinkText(path, path, "tab");
Expand All @@ -316,7 +320,7 @@ export abstract class TagFolderViewBase extends ItemView {
menu.addItem((item) =>
item
.setTitle(`Open to the right`)
.setSection("open")
.setSection("open")
.setIcon("lucide-separator-vertical")
.onClick(async () => {
app.workspace.openLinkText(path, path, "split");
Expand Down
24 changes: 12 additions & 12 deletions TagFolderViewComponent.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
export let vaultName: string = "";
export let title: string = "";
export let tags: string[] = [];
export let saveSettings: () => Promise<void>;
export let saveSettings: (setting: TagFolderSettings) => Promise<void>;
export let showMenu: (
evt: MouseEvent,
Expand Down Expand Up @@ -78,8 +78,8 @@
let onlyFDREnabled = false;
tagFolderSetting.subscribe((setting) => {
_setting = setting;
outgoingEnabled = _setting?.linkConfig?.outgoing?.enabled ?? false;
incomingEnabled = _setting?.linkConfig?.incoming?.enabled ?? false;
outgoingEnabled = _setting.linkConfig?.outgoing?.enabled ?? false;
incomingEnabled = _setting.linkConfig?.incoming?.enabled ?? false;
onlyFDREnabled = _setting.linkShowOnlyFDR;
});
let showSearch = false;
Expand Down Expand Up @@ -110,21 +110,21 @@
let linkIcon = "";
async function switchIncoming() {
_setting.linkConfig.incoming.enabled =
let newSet = { ..._setting };
newSet.linkConfig.incoming.enabled =
!_setting.linkConfig.incoming.enabled;
if (saveSettings) await saveSettings();
tagFolderSetting.set({ ..._setting });
if (saveSettings) await saveSettings(newSet);
}
async function switchOutgoing() {
_setting.linkConfig.outgoing.enabled =
let newSet = { ..._setting };
newSet.linkConfig.outgoing.enabled =
!_setting.linkConfig.outgoing.enabled;
if (saveSettings) await saveSettings();
tagFolderSetting.set({ ..._setting });
if (saveSettings) await saveSettings(newSet);
}
async function switchOnlyFDR() {
_setting.linkShowOnlyFDR = !_setting.linkShowOnlyFDR;
// if (saveSettings) await saveSettings();
tagFolderSetting.set({ ..._setting });
let newSet = { ..._setting };
newSet.linkShowOnlyFDR = !_setting.linkShowOnlyFDR;
if (saveSettings) await saveSettings(newSet);
}
onMount(() => {
Expand Down
47 changes: 41 additions & 6 deletions V2TreeFolderComponent.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,19 @@
const dLinks = thisInfo.directLinks;
tagsAll = tagsAll.filter((e) => dLinks.contains(e));
}
if (!isRoot && _setting.linkCombineOtherTree && thisInfo) {
if (_setting.linkConfig.incoming.enabled) {
tagsAll = [
...tagsAll,
...$allViewItemsByLink
.filter((e) =>
e.directLinks.contains(thisName)
)
.filter((e) => !trail.contains(e.path))
.map((e) => e.path),
];
}
}
if (!isRoot) {
tagsAll = tagsAll.filter((e) => e != "_unlinked");
}
Expand Down Expand Up @@ -552,11 +565,30 @@
);
}
const dispName = selfInfo?.displayName ?? tag;
const itemCandidates = _setting.linkCombineOtherTree
? $allViewItemsByLink.filter(
(e) => !trail.contains(e.path)
)
: _items;
let itemIncomingCandidates = [] as ViewItem[];
let itemOutgoingCandidates = [] as ViewItem[];
let itemCandidates = [] as ViewItem[];
const onlyFDR = _setting.linkShowOnlyFDR;
if (_setting.linkCombineOtherTree) {
if (_setting.linkConfig.outgoing.enabled) {
itemOutgoingCandidates =
$allViewItemsByLink.filter(
(e) => !trail.contains(e.path)
);
}
if (_setting.linkConfig.incoming.enabled) {
itemIncomingCandidates = $allViewItemsByLink
.filter((e) => e.links.contains(tag))
.filter((e) => !trail.contains(e.path));
}
itemCandidates = [
...itemOutgoingCandidates,
...itemIncomingCandidates,
];
} else {
itemCandidates = _items;
}
return [
tag,
dispName,
Expand All @@ -566,7 +598,10 @@
e.links.contains("_unlinked")
)
: itemCandidates.filter((item) =>
isDirectLinked(selfInfo, item)
(onlyFDR ? isDirectLinked : isLinked)(
selfInfo,
item
)
),
// .filter((item) => !trail.contains(item.path)),
] as V2FolderItem;
Expand Down
98 changes: 62 additions & 36 deletions main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -415,25 +415,53 @@ export default class TagFolderPlugin extends Plugin {

oldFileCache = "";


parsedFileCache = new Map<string, number>();

updateFileCachesAll(): boolean {
const filesAll = [...this.app.vault.getMarkdownFiles(), ...this.app.vault.getAllLoadedFiles().filter(e => "extension" in e && e.extension == "canvas") as TFile[]];
const processFiles = filesAll.filter(file => this.parsedFileCache.get(file.path) ?? 0 != file.stat.mtime);
const cachedLinks = this.app.metadataCache.resolvedLinks;
this.fileCaches = processFiles.map((fileEntry) => {
const [allDirectLinks, allLinks] = this.getLinkView() == null ? [[], []] : parseAllReference(cachedLinks, fileEntry.path, this.settings.linkConfig);
const directLinks = [...allDirectLinks.filter(e => e.endsWith(".md")).map(e => `${e}`)];
const links = [...allLinks.filter(e => e.endsWith(".md")).map(e => `${e}`)];
this.parsedFileCache.set(fileEntry.path, fileEntry.stat.mtime);
return {
file: fileEntry,
metadata: this.app.metadataCache.getFileCache(fileEntry),
links: links,
directLinks: directLinks
};
});
return this.isFileCacheChanged();
}
isFileCacheChanged() {
const fileCacheDump = JSON.stringify(
this.fileCaches.map((e) => ({
path: e.file.path,
links: e.links,
directLinks: e.directLinks,
tags: getAllTags(e.metadata),
}))
);
if (this.oldFileCache == fileCacheDump) {
return false;
} else {
this.oldFileCache = fileCacheDump;
return true;
}
}

updateFileCaches(diff?: TFile): boolean {
let anyUpdated = false;

if (this.fileCaches.length == 0 || !diff) {
const filesAll = [...this.app.vault.getMarkdownFiles(), ...this.app.vault.getAllLoadedFiles().filter(e => "extension" in e && e.extension == "canvas") as TFile[]];
const cachedLinks = this.app.metadataCache.resolvedLinks;
this.fileCaches = filesAll.map((fileEntry) => {
const [allDirectLinks, allLinks] = parseAllReference(cachedLinks, fileEntry.path, this.settings.linkConfig);
const directLinks = [...allDirectLinks.filter(e => e.endsWith(".md")).map(e => `${e}`)];
const links = [...allLinks.filter(e => e.endsWith(".md")).map(e => `${e}`)];
return {
file: fileEntry,
metadata: this.app.metadataCache.getFileCache(fileEntry),
links: links,
directLinks: directLinks
};
});
return this.updateFileCachesAll();
} else {
const cachedLinks = this.app.metadataCache.resolvedLinks;

const [allDirectLinks, allLinks] = parseAllReference(cachedLinks, diff.path, this.settings.linkConfig);
if (this.parsedFileCache.get(diff.path) == diff.stat.mtime) return false;
const [allDirectLinks, allLinks] = this.getLinkView() == null ? [[], []] : parseAllReference(cachedLinks, diff.path, this.settings.linkConfig);
const directLinks = [...allDirectLinks.filter(e => e.endsWith(".md")).map(e => `${e}`)];
const links = [...allLinks.filter(e => e.endsWith(".md")).map(e => `${e}`)];

Expand All @@ -442,8 +470,11 @@ export default class TagFolderPlugin extends Plugin {
);

if (old && JSON.stringify(old?.links) != JSON.stringify(links) && this.getLinkView() != null) {
// Update links
return this.updateFileCaches();
const files = unique([...old.links, ...links]);
for (const file of files) {
const f = app.vault.getAbstractFileByPath(file);
if (f instanceof TFile) anyUpdated = this.updateFileCaches(f) || anyUpdated;
}
}
this.fileCaches = this.fileCaches.filter(
(fileCache) => fileCache.file.path != diff.path
Expand All @@ -455,21 +486,7 @@ export default class TagFolderPlugin extends Plugin {
directLinks: directLinks
});
}

const fileCacheDump = JSON.stringify(
this.fileCaches.map((e) => ({
path: e.file.path,
links: e.links,
directLinks: e.directLinks,
tags: getAllTags(e.metadata),
}))
);
if (this.oldFileCache == fileCacheDump) {
return false;
} else {
this.oldFileCache = fileCacheDump;
return true;
}
return this.isFileCacheChanged() || anyUpdated;
}

async getItemsList(mode: "tag" | "link"): Promise<ViewItem[]> {
Expand Down Expand Up @@ -937,6 +954,18 @@ export default class TagFolderPlugin extends Plugin {
});
}

async refreshAllViewItems() {
this.parsedFileCache.clear();
const items = await this.getItemsList("tag");
const itemsSorted = items.sort(this.compareItems);
this.allViewItems = itemsSorted;
allViewItems.set(this.allViewItems);

const itemsLink = await this.getItemsList("link");
const itemsLinkSorted = itemsLink.sort(this.compareItems);
this.allViewItemsByLink = itemsLinkSorted;
allViewItemsByLink.set(this.allViewItemsByLink);
}
async loadSettings() {
this.settings = Object.assign(
{},
Expand All @@ -954,10 +983,7 @@ export default class TagFolderPlugin extends Plugin {
await this.saveTagInfo();
tagFolderSetting.set(this.settings);
this.compareItems = getCompareMethodItems(this.settings);
if (this.allViewItems) {
this.allViewItems = this.allViewItems.sort(this.compareItems);
allViewItems.set(this.allViewItems);
}
this.refreshAllViewItems();
// this.compareTags = getCompareMethodTags(this.settings);
}

Expand Down
53 changes: 51 additions & 2 deletions util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,27 +390,76 @@ export function parseTagName(thisName: string, _tagInfo: TagInfoDict): [string,
return [tagName, tagNameDisp]
}

const frCache = new Map<string, string[][]>();
const rrCache = new Map<string, string[][]>();


function parseAllForwardReference(metaCache: Record<string, Record<string, number>>, filename: string, passed: string[]) {
const key = `${filename}-${passed.join("-")}`;

const allForwardLinks = Object.keys(metaCache?.[filename] ?? {}).map(e => `${e}`).filter(e => !passed.contains(e));
if (frCache.has(key)) {
const cached = frCache.get(key);
if (cached[1].join("-") != allForwardLinks.join("-")) {
invalidateRefCache(unique([...cached[1], ...allForwardLinks]))
} else {
return cached;
}
}
const nextPassed = [...passed, ...allForwardLinks];
let allNextLinks = [] as string[];
for (const link of allForwardLinks) {
const [, next] = parseAllForwardReference(metaCache, link, nextPassed);
allNextLinks = [...allNextLinks, ...next];
}
return [unique(allForwardLinks), unique([...allForwardLinks, ...allNextLinks])];
const ret = [unique(allForwardLinks), unique([...allForwardLinks, ...allNextLinks])];
frCache.set(key, ret);
return ret;
}
function parseAllReverseReference(metaCache: Record<string, Record<string, number>>, filename: string, passed: string[]) {
const key = `${filename}-${passed.join("-")}`;
const allReverseLinks = Object.entries((metaCache)).filter(([, links]) => filename in links).map(([name,]) => name).map(e => `${e}`).filter(e => !passed.contains(e));

if (rrCache.has(key)) {
const cached = rrCache.get(key);
if (cached[1].join("-") != allReverseLinks.join("-")) {
invalidateRefCache(unique([...cached[1], ...allReverseLinks]))
} else {
return cached;
}
}

const nextPassed = [...passed, ...allReverseLinks];
let allNextLinks = [] as string[];
for (const link of allReverseLinks) {
const [, next] = parseAllReverseReference(metaCache, link, nextPassed);
allNextLinks = [...allNextLinks, ...next];
}
return [unique(allReverseLinks), unique([...allReverseLinks, ...allNextLinks])];
const ret = [unique(allReverseLinks), unique([...allReverseLinks, ...allNextLinks])];
rrCache.set(key, ret);
return ret;
}

export function invalidateRefCache(filenames: string[]) {
const delRCache = [] as string[];
for (const [key, value] of rrCache) {
if (isIntersect(value[1], filenames)) {
delRCache.push(key);
}
}
const delFCache = [] as string[];
for (const [key, value] of frCache) {
if (isIntersect(value[1], filenames)) {
delFCache.push(key);
}
}
for (const key of delRCache) {
rrCache.delete(key);
} for (const key of delFCache) {
frCache.delete(key);
}
}

export function parseAllReference(metaCache: Record<string, Record<string, number>>, filename: string, conf: LinkParseConf): string[][] {
const [allDirectForwardLinks, allForwardLinks] = (!conf?.outgoing?.enabled) ? [[], []] : parseAllForwardReference(metaCache, filename, []);
const [allDirectReverseLinks, allReverseLinks] = (!conf?.incoming?.enabled) ? [[], []] : parseAllReverseReference(metaCache, filename, []);
Expand Down

0 comments on commit f62de8a

Please sign in to comment.