Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: renaming the existing index file when renaming its folder #90

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 126 additions & 50 deletions main.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { App, Modal, debounce, Plugin, PluginSettingTab, Setting, TFile, TAbstractFile, } from 'obsidian';
import { App, Modal, debounce, Plugin, PluginSettingTab, Setting, TFile, TAbstractFile } from 'obsidian';
import { IndexItemStyle } from './interfaces/IndexItemStyle';
import { GeneralContentOptions, ZoottelkeeperPluginSettings } from './interfaces'
import { isInAllowedFolder, isInDisAllowedFolder, updateFrontmatter, updateIndexContent, removeFrontmatter, hasFrontmatter } from './utils'
Expand All @@ -11,7 +11,9 @@ export default class ZoottelkeeperPlugin extends Plugin {
lastVault: Set<string>;

triggerUpdateIndexFile = debounce(
this.keepTheZooClean.bind(this, false),
(file: TAbstractFile, oldPath?: string) => {
this.keepTheZooClean(false, file, oldPath)
},
3000,
true
);
Expand All @@ -38,66 +40,144 @@ export default class ZoottelkeeperPlugin extends Plugin {

this.addSettingTab(new ZoottelkeeperPluginSettingTab(this.app, this));
}

loadVault() {
this.lastVault = new Set(
this.lastVault = this.getVaultSet();
}

getVaultSet() {
return new Set(
this.app.vault.getMarkdownFiles().map((file) => file.path)
);
}
async keepTheZooClean(triggeredManually?: boolean) {

async keepTheZooClean(triggeredManually?: boolean, file?: TAbstractFile, oldPath?: string) {
console.debug('keeping the zoo clean...');
if (this.lastVault || triggeredManually) {
const vaultFilePathsSet = new Set(
this.app.vault.getMarkdownFiles().map((file) => file.path)
);
const vaultFilePathsSet = this.getVaultSet();
try {
// getting the changed files using symmetric diff

let changedFiles = new Set([
...Array.from(vaultFilePathsSet).filter(
(currentFile) => !this.lastVault.has(currentFile)
),
...Array.from(this.lastVault).filter(
(currentVaultFile) => !vaultFilePathsSet.has(currentVaultFile)
),
]);
const changedFiles = this.getCreatedAndDeletedFiles(vaultFilePathsSet)

console.debug(
`changedFiles: ${JSON.stringify(Array.from(changedFiles))}`
`changedFiles: ${JSON.stringify(changedFiles)}`
);
// getting index files to be updated
const indexFiles2BUpdated = new Set<string>();

for (const changedFile of Array.from(changedFiles)) {
const indexFilePath = this.getIndexFilePath(changedFile);
if (indexFilePath
&& isInAllowedFolder(this.settings, indexFilePath)
&& !isInDisAllowedFolder(this.settings, indexFilePath)) {
indexFiles2BUpdated.add(indexFilePath);
}

// getting the parents' index notes of each changed file in order to update their links as well (hierarhical backlinks)
const parentIndexFilePath = this.getIndexFilePath(
this.getParentFolder(changedFile)
);
if (parentIndexFilePath) indexFiles2BUpdated.add(parentIndexFilePath);
}

const indexFileAndNewPath = this.getIndexFile2BRenamed(file, oldPath)

const indexFiles2BUpdated = this.getIndexFiles2BUpdated(changedFiles)

console.debug(
`Index files to be updated: ${JSON.stringify(
Array.from(indexFiles2BUpdated)
)}`
);

await this.removeDisallowedFoldersIndexes(indexFiles2BUpdated);
// update index files
for (const indexFile of Array.from(indexFiles2BUpdated)) {
await this.generateIndexContents(indexFile);
}
await this.cleanDisallowedFolders();

await this.renameIndexFile(indexFileAndNewPath)
await this.updateIndexFiles(indexFiles2BUpdated)

} catch (e) {}
}
this.lastVault = new Set(
this.app.vault.getMarkdownFiles().map((file) => file.path)
);
this.lastVault = this.getVaultSet();
}

getCreatedAndDeletedFiles(vaultFilePathsSet: Set<string>) {
// getting the changed files using symmetric diff
const createdFiles = Array.from(vaultFilePathsSet).filter(
(currentFile) => !this.lastVault.has(currentFile)
)
const deletedFiles = Array.from(this.lastVault).filter(
(currentVaultFile) => !vaultFilePathsSet.has(currentVaultFile)
)

let changedFiles = Array.from(new Set([
...createdFiles,
...deletedFiles,
]));

return changedFiles
}

getIndexFile2BRenamed(file?: TAbstractFile, oldPath?: string): { file: TFile, newPath: string } | undefined {
if (!file || !oldPath) return undefined;

const createdFileSplit = file.path.split('/')
const deletedFileSplit = oldPath.split('/')
const createdFileName = file.name
const deletedFileName = deletedFileSplit.last()

// the file itself was renamed, not the folder
if (createdFileName !== deletedFileName) return undefined

// The file was moved to a shallower or deeper nested directory
if (createdFileSplit.length !== deletedFileSplit.length) return undefined

// Find the folder that was renamed
for (let i = 0; i < createdFileSplit.length; i++) {
const createdParentFolder = createdFileSplit[i];
const deletedParentFolder = deletedFileSplit[i];

// This folder has not changed
if (createdParentFolder === deletedParentFolder) continue

// Is the index file of the old folder still present in the new folder?
const indexFilePath = `${createdFileSplit.slice(0, i + 1).join('/')}/${this.settings.indexPrefix}${deletedParentFolder}.md`
const folderOrIndexFile = this.app.vault.getAbstractFileByPath(indexFilePath)

// The old index file is still there => folder has been renamed and the file can be deleted
if (folderOrIndexFile instanceof TFile) {
const newPath = this.getIndexFilePath(`${createdFileSplit.slice(0, i + 1).join('/')}/`)
return { file: folderOrIndexFile, newPath }
}

// If there is no such file, either that folder is excluded or the file was moved there.
// In both cases there is no action necessary
return undefined
}
}

getIndexFiles2BUpdated(changedFiles: string[]) {
const indexFiles2BUpdated = new Set<string>();

for (const changedFile of changedFiles) {
const indexFilePath = this.getIndexFilePath(changedFile);
if (indexFilePath
&& isInAllowedFolder(this.settings, indexFilePath)
&& !isInDisAllowedFolder(this.settings, indexFilePath)) {
indexFiles2BUpdated.add(indexFilePath);
}

// getting the parents' index notes of each changed file in order to update their links as well (hierarhical backlinks)
const parentIndexFilePath = this.getIndexFilePath(
this.getParentFolder(changedFile)
);
if (parentIndexFilePath) indexFiles2BUpdated.add(parentIndexFilePath);
}

return indexFiles2BUpdated
}

async renameIndexFile(indexFileAndNewPath: { file: TFile, newPath: string } | undefined) {
if (!indexFileAndNewPath) return

const { file, newPath } = indexFileAndNewPath

const newIndexFile = this.app.vault.getAbstractFileByPath(newPath)
const newIndexFileExists = newIndexFile instanceof TFile

if (newIndexFileExists) {
await this.app.vault.delete(newIndexFile)
}

await this.app.vault.rename(file, newPath)
}

async updateIndexFiles(indexFiles2BUpdated: Set<string>) {
await this.removeDisallowedFoldersIndexes(indexFiles2BUpdated);
// update index files
for (const indexFile of Array.from(indexFiles2BUpdated)) {
await this.generateIndexContents(indexFile);
}
await this.cleanDisallowedFolders();
}

onunload() {
Expand Down Expand Up @@ -292,17 +372,13 @@ export default class ZoottelkeeperPlugin extends Plugin {
}

isIndexFile = (item: TAbstractFile): boolean => {

return this.isFile(item)
&& (this.settings.indexPrefix === ''
? item.name === item.parent.name
: item.name.startsWith(this.settings.indexPrefix));
&& item.name === `${this.settings.indexPrefix}${item.parent.name}`
}

isFile = (item: TAbstractFile): boolean => {
return item instanceof TFile;
}

}

class ZoottelkeeperPluginModal extends Modal {
Expand Down