Skip to content

Commit

Permalink
feat: Include canvas files when determining orphans (#53)
Browse files Browse the repository at this point in the history
  • Loading branch information
MostlyArmless authored Mar 13, 2024
1 parent bb1b21b commit 56900fe
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 40 deletions.
124 changes: 89 additions & 35 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import {
getAllTags,
getLinkpath,
iterateRefs,
Notice,
Plugin,
TFile,
TFolder,
} from "obsidian";
import { CanvasData } from "obsidian/canvas";
import { DeleteFilesModal } from "./deleteFilesModal";
import { SettingsTab } from "./settingsTab";
import { Utils } from "./utils";
Expand Down Expand Up @@ -176,15 +176,15 @@ export default class FindOrphanedFilesPlugin extends Plugin {
const emptyFiles: TFile[] = [];
for (const file of files) {
if (
!new Utils(
new Utils(
this.app,
file.path,
[],
[],
this.settings.emptyFilesDirectories,
this.settings.emptyFilesFilesToIgnore,
this.settings.emptyFilesIgnoreDirectories
).isValid()
).shouldIgnoreFile()
) {
continue;
}
Expand Down Expand Up @@ -220,33 +220,81 @@ export default class FindOrphanedFilesPlugin extends Plugin {
);
}

findOrphanedFiles(dir?: string) {
async findOrphanedFiles(dir?: string) {
const startTime = Date.now();
const outFileName = this.settings.outputFileName + ".md";
let outFile: TFile;
const files = this.app.vault.getFiles();
let outFile: TFile | null = null;
const allFiles = this.app.vault.getFiles();
const markdownFiles = this.app.vault.getMarkdownFiles();
const links: string[] = [];
const canvasFiles = allFiles.filter(
(file) => file.extension === "canvas"
);
const links: Set<string> = new Set();
const findLinkInTextRegex = /\[\[(.*?)\]\]|\[.*?\]\((.*?)\)/g;

// get a list of all links within canvas files
const canvasParsingPromises = canvasFiles.map(
async (canvasFile: TFile) => {
// Read the canvas file as JSON
const canvasFileContent: CanvasData = JSON.parse(
await this.app.vault.cachedRead(canvasFile)
);
// Get a list of all links within the canvas file
canvasFileContent.nodes.forEach((node) => {
let linkTexts: string[] = [];

if (node.type === "file") {
linkTexts.push(node.file);
} else if (node.type === "text") {
// There could be zero or more links in the text. Use a regex to extract all the text between "[[" and "]]"
let match;
while (
(match = findLinkInTextRegex.exec(node.text)) !==
null
) {
linkTexts.push(match[1] ?? match[2]);
}
} else {
return; // Skip other types (e.g. "group")
}

linkTexts.forEach((linkText: string) => {
const targetFile =
this.app.metadataCache.getFirstLinkpathDest(
linkText.split("|")[0].split("#")[0],
canvasFile.path
);
if (targetFile != null) links.add(targetFile.path);
});
});
}
);

markdownFiles.forEach((markFile: TFile) => {
if (markFile.path == outFileName) {
outFile = markFile;
// Get a list of all links within markdown files
markdownFiles.forEach((mdFile: TFile) => {
if (outFile === null && mdFile.path == outFileName) {
outFile = mdFile;
return;
}
const cache = this.app.metadataCache.getFileCache(markFile);
const cache = this.app.metadataCache.getFileCache(mdFile);
for (const ref of [
...(cache.embeds ?? []),
...(cache.links ?? []),
...(cache.frontmatterLinks ?? []),
]) {
const txt = this.app.metadataCache.getFirstLinkpathDest(
getLinkpath(ref.link),
markFile.path
mdFile.path
);
if (txt != null) links.push(txt.path);
if (txt != null) links.add(txt.path);
}
});
const notLinkedFiles = files.filter((file) =>
this.isValid(file, links, dir)

// Ensure the canvas files have all been parsed before continuing.
await Promise.all(canvasParsingPromises);

const notLinkedFiles = allFiles.filter((file) =>
this.isFileAnOrphan(file, links, dir)
);
notLinkedFiles.remove(outFile);

Expand All @@ -270,6 +318,13 @@ export default class FindOrphanedFilesPlugin extends Plugin {
text,
this.settings.openOutputFile
);
const endTime = Date.now();
const diff = endTime - startTime;
if (diff > 1000) {
new Notice(
`Found ${notLinkedFiles.length} orphaned files in ${diff}ms`
);
}
}
async deleteOrphanedFiles() {
if (
Expand Down Expand Up @@ -359,7 +414,7 @@ export default class FindOrphanedFilesPlugin extends Plugin {
this.settings.unresolvedLinksFilesToIgnore,
this.settings.unresolvedLinksIgnoreDirectories
);
if (!utils.isValid()) continue;
if (utils.shouldIgnoreFile()) continue;

for (const link in brokenLinks[sourceFilepath]) {
const linkFileType = link.substring(link.lastIndexOf(".") + 1);
Expand Down Expand Up @@ -409,24 +464,23 @@ export default class FindOrphanedFilesPlugin extends Plugin {
let outFile: TFile;
const files = this.app.vault.getMarkdownFiles();
let withoutFiles = files.filter((file) => {
if (
new Utils(
this.app,
file.path,
[],
[],
this.settings.withoutTagsDirectoriesToIgnore,
this.settings.withoutTagsFilesToIgnore,
true
).isValid()
) {
return (
(getAllTags(this.app.metadataCache.getFileCache(file))
.length ?? 0) <= 0
);
} else {
const utils = new Utils(
this.app,
file.path,
[],
[],
this.settings.withoutTagsDirectoriesToIgnore,
this.settings.withoutTagsFilesToIgnore,
true
);

if (utils.shouldIgnoreFile()) {
return false;
}
return (
(getAllTags(this.app.metadataCache.getFileCache(file)).length ??
0) <= 0
);
});
withoutFiles.remove(outFile);

Expand All @@ -450,8 +504,8 @@ export default class FindOrphanedFilesPlugin extends Plugin {
* @param file file to check
* @param links all links in the vault
*/
isValid(file: TFile, links: string[], dir: string): boolean {
if (links.contains(file.path)) return false;
isFileAnOrphan(file: TFile, links: Set<string>, dir: string): boolean {
if (links.has(file.path)) return false;

//filetypes to ignore by default
if (file.extension == "css") return false;
Expand All @@ -477,7 +531,7 @@ export default class FindOrphanedFilesPlugin extends Plugin {
this.settings.ignoreDirectories,
dir
);
if (!utils.isValid()) return false;
if (utils.shouldIgnoreFile()) return false;

return true;
}
Expand Down
10 changes: 5 additions & 5 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,12 @@ export class Utils {
return this.filesToIgnore.contains(this.filePath);
}

public isValid() {
public shouldIgnoreFile() {
return (
!this.hasTagsToIgnore() &&
!this.hasLinksToIgnore() &&
!this.checkDirectory() &&
!this.isFileToIgnore()
this.hasTagsToIgnore() ||
this.hasLinksToIgnore() ||
this.checkDirectory() ||
this.isFileToIgnore()
);
}

Expand Down

0 comments on commit 56900fe

Please sign in to comment.