add find-by tag and to a directory functionality

This commit is contained in:
Oleg 2022-09-05 13:52:48 +03:00
parent 691e8855f5
commit f21f51ecba
8 changed files with 274 additions and 3746 deletions

View File

@ -36,3 +36,7 @@ And rename a **bunch of files** and update their reference in code base respecti
## Installing ## Installing
The plugin in beta. To beta test, you can install the plugin using BRAT (see [BRAT > Adding a beta plugin](https://github.com/TfTHacker/obsidian42-brat#adding-a-beta-plugin) for further instructions). The plugin in beta. To beta test, you can install the plugin using BRAT (see [BRAT > Adding a beta plugin](https://github.com/TfTHacker/obsidian42-brat#adding-a-beta-plugin) for further instructions).
## Support development
[!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/oleglustenko)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

210
main.ts
View File

@ -1,20 +1,27 @@
import { App, Plugin, PluginSettingTab, Setting, TFile } from 'obsidian'; import { App, Plugin, PluginSettingTab, Setting, TFile } from 'obsidian';
import { FolderSuggest } from './src/suggestions/folderSuggest'; import { FolderSuggest } from './src/suggestions/folderSuggest';
import { renderDonateButton } from './src/components/DonateButton'; import { renderDonateButton } from './src/components/DonateButton';
import { import {
getFilesNamesInDirectory, getFilesNamesInDirectory,
getObsidianFiles,
getRenderedFileNamesReplaced, getRenderedFileNamesReplaced,
renameFilesInObsidian, renameFilesInObsidian,
syncScrolls, syncScrolls,
} from './src/services/file.service'; } from './src/services/file.service';
import { createPreviewElement } from './src/components/PreviewElement'; import { createPreviewElement } from './src/components/PreviewElement';
import {
getObsidianFilesByFolderName,
getObsidianFilesWithTagName,
} from './src/services/obsidian.service';
import { renderPreviewFiles } from './src/components/RenderPreviewFiles';
interface BulkRenamePluginSettings { interface BulkRenamePluginSettings {
folderName: string; folderName: string;
fileNames: TFile[]; fileNames: TFile[];
existingSymbol: string; existingSymbol: string;
replacePattern: string; replacePattern: string;
tags: string[];
viewType: 'tags' | 'folder';
} }
const DEFAULT_SETTINGS: BulkRenamePluginSettings = { const DEFAULT_SETTINGS: BulkRenamePluginSettings = {
@ -22,6 +29,15 @@ const DEFAULT_SETTINGS: BulkRenamePluginSettings = {
fileNames: [], fileNames: [],
existingSymbol: '', existingSymbol: '',
replacePattern: '', replacePattern: '',
tags: [],
viewType: 'folder',
};
const isViewTypeFolder = ({ settings }: BulkRenamePlugin) => {
return settings.viewType === 'folder';
};
const isViewTypeTags = ({ settings }: BulkRenamePlugin) => {
return settings.viewType === 'tags';
}; };
class BulkRenamePlugin extends Plugin { class BulkRenamePlugin extends Plugin {
@ -48,9 +64,10 @@ export type State = {
filesScroll: number; filesScroll: number;
}; };
class BulkRenameSettingsTab extends PluginSettingTab { export class BulkRenameSettingsTab extends PluginSettingTab {
plugin: BulkRenamePlugin; plugin: BulkRenamePlugin;
state: State; state: State;
filesAndPreview: Setting;
constructor(app: App, plugin: BulkRenamePlugin) { constructor(app: App, plugin: BulkRenamePlugin) {
super(app, plugin); super(app, plugin);
@ -67,51 +84,46 @@ class BulkRenameSettingsTab extends PluginSettingTab {
containerEl.empty(); containerEl.empty();
containerEl.createEl('h2', { text: 'Bulk Rename - Settings' }); containerEl.createEl('h2', { text: 'Bulk Rename - Settings' });
this.renderTabs();
this.renderFileLocation(); this.renderFileLocation();
this.renderTagNames();
this.renderReplaceSymbol(); this.renderReplaceSymbol();
this.renderFilesAndPreview(); this.renderFilesAndPreview();
this.renderRenameFiles(); this.renderRenameFiles();
this.renderSupportDevelopment(); this.renderSupportDevelopment();
} }
renderReplaceSymbol() { renderTabs() {
const { settings } = this.plugin;
const desc = document.createDocumentFragment();
const button = document.createElement('button');
button.textContent = 'Preview';
button.className = 'mod-cta';
button.onclick = () => {
this.display();
};
desc.appendChild(button);
new Setting(this.containerEl) new Setting(this.containerEl)
.setName('Existing Symbol') .setName('toggle view')
.setDesc(desc) .addButton((button) => {
.addText((textComponent) => { button.setButtonText('Search by folder');
const previewLabel = createPreviewElement('Replacement symbols'); if (isViewTypeFolder(this.plugin)) {
textComponent.inputEl.insertAdjacentElement('afterend', previewLabel); button.setCta();
textComponent.setValue(settings.existingSymbol); }
textComponent.setPlaceholder('existing symbols'); button.onClick(async () => {
textComponent.onChange((newValue) => { this.plugin.settings.viewType = 'folder';
settings.existingSymbol = newValue; await this.plugin.saveSettings();
this.plugin.saveSettings(); this.display();
}); });
}) })
.addText((textComponent) => { .addButton((button) => {
textComponent.setValue(settings.replacePattern); button.setButtonText('Search By Tags');
textComponent.setPlaceholder('replace with'); if (isViewTypeTags(this.plugin)) {
textComponent.onChange((newValue) => { button.setCta();
settings.replacePattern = newValue; }
this.plugin.saveSettings(); button.onClick(async () => {
this.calculateFiles(); this.plugin.settings.viewType = 'tags';
await this.plugin.saveSettings();
this.display();
}); });
}); });
} }
renderFileLocation() { renderFileLocation() {
if (!isViewTypeFolder(this.plugin)) {
return;
}
new Setting(this.containerEl) new Setting(this.containerEl)
.setName('Folder location') .setName('Folder location')
.setDesc('Files in this folder will be available renamed.') .setDesc('Files in this folder will be available renamed.')
@ -131,45 +143,99 @@ class BulkRenameSettingsTab extends PluginSettingTab {
button.setButtonText('Refresh'); button.setButtonText('Refresh');
button.onClick(() => { button.onClick(() => {
this.calculateFiles(); this.calculateFiles();
this.display(); this.reRenderPreview();
}); });
}); });
} }
renderFilesAndPreview() { renderTagNames() {
let existingFilesTextArea: HTMLTextAreaElement; if (!isViewTypeTags(this.plugin)) {
let replacedPreviewTextArea: HTMLTextAreaElement; return;
}
new Setting(this.containerEl) new Setting(this.containerEl)
.setName('Files within the folder') .setName('Tag names ')
.setDesc(`Total Files: ${this.plugin.settings.fileNames.length}`) .setDesc('all files with the tags will be found')
.addTextArea((text) => { .addSearch((cb) => {
text.setPlaceholder('Here you will see files under folder location'); // @ts-ignore
text.setDisabled(true); cb.inputEl.addEventListener('keydown', (event) => {
existingFilesTextArea = text.inputEl; if (event.key !== 'Enter') {
return;
}
const target = event.target as HTMLInputElement;
const value = getFilesNamesInDirectory(this.plugin); this.plugin.settings.tags = target.value.replace(/ /g, '').split(',');
text.setValue(value); this.plugin.saveSettings();
this.getFilesByTags();
const previewLabel = createPreviewElement(); this.reRenderPreview();
text.inputEl.insertAdjacentElement('afterend', previewLabel); });
text.inputEl.addClass('templater_cmd'); cb.setPlaceholder('Example: #tag, #tag2')
.setValue(this.plugin.settings.tags.join(','))
.onChange((newFolder) => {
this.plugin.settings.tags = newFolder.replace(/ /g, '').split(',');
this.plugin.saveSettings();
this.getFilesByTags();
});
// @ts-ignore
cb.containerEl.addClass('templater_search');
}) })
.addTextArea((text) => { .addButton((button) => {
text.setPlaceholder( button.setButtonText('Refresh');
'How filenames will looks like after replacement(click preview first)', button.onClick(() => {
); this.getFilesByTags();
text.setDisabled(true); this.reRenderPreview();
});
replacedPreviewTextArea = text.inputEl;
const value = getRenderedFileNamesReplaced(this.plugin);
text.setValue(value);
text.inputEl.addClass('templater_cmd');
})
.then((setting) => {
syncScrolls(existingFilesTextArea, replacedPreviewTextArea, this.state);
}); });
} }
renderReplaceSymbol() {
// if (isViewTypeTags(this.plugin)) {
// return;
// }
const { settings } = this.plugin;
const desc = document.createDocumentFragment();
const button = document.createElement('button');
button.textContent = 'Preview';
button.className = 'mod-cta';
button.onclick = this.reRenderPreview;
desc.appendChild(button);
const newSettings = new Setting(this.containerEl)
.setName('Existing Symbol')
.setDesc(desc);
// if (!isViewTypeTags(this.plugin)) {
newSettings.addText((textComponent) => {
textComponent.setValue(settings.existingSymbol);
textComponent.setPlaceholder('existing symbols');
textComponent.onChange((newValue) => {
settings.existingSymbol = newValue;
this.plugin.saveSettings();
});
});
// }
newSettings.addText((textComponent) => {
const previewLabel = createPreviewElement('Replacement symbols');
textComponent.inputEl.insertAdjacentElement('beforebegin', previewLabel);
textComponent.setValue(settings.replacePattern);
textComponent.setPlaceholder('replace with');
textComponent.onChange((newValue) => {
settings.replacePattern = newValue;
this.plugin.saveSettings();
this.calculateFiles();
});
});
}
renderFilesAndPreview = () => {
this.filesAndPreview = new Setting(this.containerEl)
.setName('Files within the folder')
.setDesc(`Total Files: ${this.plugin.settings.fileNames.length}`);
renderPreviewFiles(this.filesAndPreview, this.plugin, this.state);
};
renderRenameFiles() { renderRenameFiles() {
const desc = document.createDocumentFragment(); const desc = document.createDocumentFragment();
desc.append( desc.append(
@ -186,12 +252,14 @@ class BulkRenameSettingsTab extends PluginSettingTab {
.setName('Replace patterns') .setName('Replace patterns')
.addButton((button) => { .addButton((button) => {
button.buttonEl.style.width = '100%'; button.buttonEl.style.width = '100%';
button.setTooltip("FYI: We don't have undone button yet!"); button.setTooltip(
"We don't have undone button yet!\r\n Do we need it?",
);
button.setButtonText('Rename'); button.setButtonText('Rename');
button.onClick(async () => { button.onClick(async () => {
button.setDisabled(true); button.setDisabled(true);
await renameFilesInObsidian(this.app, this.plugin); await renameFilesInObsidian(this.app, this.plugin);
this.display(); this.reRenderPreview();
}); });
}); });
} }
@ -201,8 +269,22 @@ class BulkRenameSettingsTab extends PluginSettingTab {
} }
calculateFiles() { calculateFiles() {
this.plugin.settings.fileNames = getObsidianFiles(this.app, this.plugin); this.plugin.settings.fileNames = getObsidianFilesByFolderName(
this.app,
this.plugin,
);
} }
getFilesByTags() {
this.plugin.settings.fileNames = getObsidianFilesWithTagName(
this.app,
this.plugin,
);
}
reRenderPreview = () => {
renderPreviewFiles(this.filesAndPreview, this.plugin, this.state);
};
} }
export default BulkRenamePlugin; export default BulkRenamePlugin;

3666
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -32,6 +32,6 @@
"prettier": "2.7.1", "prettier": "2.7.1",
"ts-jest": "^28.0.8", "ts-jest": "^28.0.8",
"tslib": "2.4.0", "tslib": "2.4.0",
"typescript": "^4.7.4" "typescript": "4.8.2"
} }
} }

View File

@ -0,0 +1,45 @@
import {
getFilesNamesInDirectory,
getRenderedFileNamesReplaced,
syncScrolls,
} from '../services/file.service';
import { createPreviewElement } from './PreviewElement';
import BulkRenamePlugin, { BulkRenameSettingsTab } from '../../main';
export const renderPreviewFiles = (
setting: BulkRenameSettingsTab['filesAndPreview'],
plugin: BulkRenamePlugin,
state: BulkRenameSettingsTab['state'],
) => {
let existingFilesTextArea: HTMLTextAreaElement;
let replacedPreviewTextArea: HTMLTextAreaElement;
return setting
.clear()
.addTextArea((text) => {
text.setPlaceholder('Here you will see files under folder location');
text.setDisabled(true);
existingFilesTextArea = text.inputEl;
const value = getFilesNamesInDirectory(plugin);
text.setValue(value);
const previewLabel = createPreviewElement();
text.inputEl.insertAdjacentElement('afterend', previewLabel);
text.inputEl.addClass('templater_cmd');
})
.addTextArea((text) => {
text.setPlaceholder(
'How filenames will looks like after replacement(click preview first)',
);
text.setDisabled(true);
replacedPreviewTextArea = text.inputEl;
const value = getRenderedFileNamesReplaced(plugin);
text.setValue(value);
text.inputEl.addClass('templater_cmd');
})
.then((setting) => {
syncScrolls(existingFilesTextArea, replacedPreviewTextArea, state);
});
};

View File

@ -1,21 +1,6 @@
import { App, Notice, TFile } from 'obsidian'; import { App, Notice, TFile } from 'obsidian';
import BulkRenamePlugin, { State } from '../../main'; import BulkRenamePlugin, { State } from '../../main';
export const getObsidianFiles = (app: App, plugin: BulkRenamePlugin) => {
const { folderName } = plugin.settings;
const abstractFiles = app.vault.getAllLoadedFiles();
const files = [] as TFile[];
abstractFiles.forEach((file) => {
if (file instanceof TFile && file.parent.name.includes(folderName)) {
files.push(file);
}
});
const filesSortedByName = files.sort((a, b) => a.name.localeCompare(b.name));
return filesSortedByName;
};
export const getFilesNamesInDirectory = (plugin: BulkRenamePlugin) => { export const getFilesNamesInDirectory = (plugin: BulkRenamePlugin) => {
const { fileNames } = plugin.settings; const { fileNames } = plugin.settings;
@ -65,27 +50,33 @@ export const replaceFilePath = (plugin: BulkRenamePlugin, file: TFile) => {
return `${newPath}.${file.extension}`; return `${newPath}.${file.extension}`;
}; };
export const renameFilesInObsidian = async (app: App, plugin: BulkRenamePlugin) => { export const renameFilesInObsidian = async (
const { replacePattern, existingSymbol } = plugin.settings; app: App,
plugin: BulkRenamePlugin,
) => {
const { existingSymbol, fileNames } = plugin.settings;
if (!existingSymbol) { if (!existingSymbol) {
new Notice('please fill Existing Symbol'); new Notice('please fill Existing Symbol');
return; return;
} }
if (replacePattern === existingSymbol) { // if (replacePattern === existingSymbol) {
new Notice("Replace Pattern shouldn't much Existing Symbol"); // new Notice("Replace Pattern shouldn't much Existing Symbol");
return; // return;
} // }
if (!plugin.settings.fileNames.length) { if (!fileNames.length) {
new Notice('Please check your results before rename!'); new Notice('Please check your results before rename!');
return; return;
} }
new Notice('renaming has been started'); new Notice('renaming has been started');
for (const fileName of plugin.settings.fileNames) { for (const fileName of fileNames) {
await app.fileManager.renameFile(fileName, replaceFilePath(plugin, fileName)); await app.fileManager.renameFile(
fileName,
replaceFilePath(plugin, fileName),
);
} }
new Notice('successfully renamed all files'); new Notice('successfully renamed all files');
}; };

View File

@ -0,0 +1,54 @@
import { App, TFile } from 'obsidian';
import BulkRenamePlugin from '../../main';
export const getObsidianFilesByFolderName = (
app: App,
plugin: BulkRenamePlugin,
) => {
const { folderName } = plugin.settings;
const abstractFiles = app.vault.getAllLoadedFiles();
const files = abstractFiles.filter((file) => {
return file instanceof TFile && file.parent.name.includes(folderName);
});
const filesSortedByName = files.sort((a, b) => a.name.localeCompare(b.name));
return filesSortedByName;
};
export const getObsidianFilesWithTagName = (
app: App,
plugin: BulkRenamePlugin,
) => {
const { tags } = plugin.settings;
const abstractFiles = app.vault.getAllLoadedFiles();
const files = abstractFiles.filter((file) => {
if (!(file instanceof TFile)) {
return;
}
const fileMetadata = app.metadataCache.getFileCache(file);
if (!fileMetadata) {
return;
}
if (!fileMetadata.tags) {
return;
}
const hasTagsInTheFile = fileMetadata.tags.find((fileTags) => {
return tags.includes(fileTags.tag);
});
if (!hasTagsInTheFile) {
return;
}
return file;
});
const filesSortedByName = files.sort((a, b) => a.name.localeCompare(b.name));
return filesSortedByName;
};