435 lines
12 KiB
TypeScript
435 lines
12 KiB
TypeScript
import {
|
|
App,
|
|
Plugin,
|
|
PluginSettingTab,
|
|
Setting,
|
|
TFile,
|
|
Platform,
|
|
} from 'obsidian';
|
|
|
|
import { FolderSuggest } from './src/suggestions/folderSuggest';
|
|
import { renderDonateButton } from './src/components/DonateButton';
|
|
import { renameFilesInObsidian } from './src/services/file.service';
|
|
import { createPreviewElement } from './src/components/PreviewElement';
|
|
import {
|
|
getObsidianFilesByFolderName,
|
|
getObsidianFilesByRegExp,
|
|
getObsidianFilesWithTagName,
|
|
} from './src/services/obsidian.service';
|
|
import { renderPreviewFiles } from './src/components/RenderPreviewFiles';
|
|
import { createBackslash } from './src/components/RegExpBackslash';
|
|
import { RegExpFlag } from './src/constants/RegExpFlags';
|
|
import { RegExpFlagsSuggest } from './src/suggestions/RegExpFlagsSuggest';
|
|
import {
|
|
isViewTypeRegExp,
|
|
isViewTypeFolder,
|
|
isViewTypeTags,
|
|
} from './src/services/settings.service';
|
|
|
|
export interface BulkRenamePluginSettings {
|
|
folderName: string;
|
|
fileNames: TFile[];
|
|
existingSymbol: string;
|
|
replacePattern: string;
|
|
tags: string[];
|
|
regExpState: {
|
|
regExp: string;
|
|
withRegExpForReplaceSymbols: boolean;
|
|
flags: RegExpFlag[];
|
|
};
|
|
viewType: 'tags' | 'folder' | 'regexp';
|
|
}
|
|
|
|
const DEFAULT_SETTINGS: BulkRenamePluginSettings = {
|
|
folderName: '',
|
|
fileNames: [],
|
|
existingSymbol: '',
|
|
replacePattern: '',
|
|
regExpState: {
|
|
regExp: '',
|
|
flags: [],
|
|
withRegExpForReplaceSymbols: false,
|
|
},
|
|
tags: [],
|
|
viewType: 'folder',
|
|
};
|
|
|
|
class BulkRenamePlugin extends Plugin {
|
|
settings: BulkRenamePluginSettings;
|
|
|
|
async onload() {
|
|
await this.loadSettings();
|
|
this.addSettingTab(new BulkRenameSettingsTab(this.app, this));
|
|
}
|
|
|
|
onunload() {}
|
|
|
|
async loadSettings() {
|
|
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
|
|
}
|
|
|
|
async saveSettings() {
|
|
await this.saveData(this.settings);
|
|
}
|
|
}
|
|
|
|
export type State = {
|
|
previewScroll: number;
|
|
filesScroll: number;
|
|
};
|
|
|
|
export class BulkRenameSettingsTab extends PluginSettingTab {
|
|
plugin: BulkRenamePlugin;
|
|
state: State;
|
|
filesAndPreview: Setting;
|
|
totalFiles: HTMLSpanElement;
|
|
|
|
constructor(app: App, plugin: BulkRenamePlugin) {
|
|
super(app, plugin);
|
|
this.state = {
|
|
previewScroll: 0,
|
|
filesScroll: 0,
|
|
};
|
|
|
|
this.plugin = plugin;
|
|
}
|
|
|
|
display() {
|
|
const { containerEl } = this;
|
|
containerEl.empty();
|
|
containerEl.createEl('h1', { text: 'Bulk Rename - Settings' });
|
|
containerEl.addEventListener('keyup', (event) => {
|
|
if (event.key !== 'Enter') {
|
|
return;
|
|
}
|
|
|
|
this.reRenderPreview();
|
|
});
|
|
|
|
this.containerEl.addClass('bulk_rename_plugin');
|
|
this.renderTabs();
|
|
this.renderFileLocation();
|
|
this.renderTagNames();
|
|
this.renderRegExpInput();
|
|
this.renderReplaceSymbol();
|
|
this.renderFilesAndPreview();
|
|
this.renderRenameFiles();
|
|
this.renderSupportDevelopment();
|
|
}
|
|
|
|
renderTabs() {
|
|
new Setting(this.containerEl)
|
|
.setName('Search by: ')
|
|
.addButton((button) => {
|
|
button.setButtonText('Folder');
|
|
if (isViewTypeFolder(this.plugin.settings)) {
|
|
button.setCta();
|
|
}
|
|
button.onClick(async () => {
|
|
this.plugin.settings.viewType = 'folder';
|
|
await this.plugin.saveSettings();
|
|
this.display();
|
|
});
|
|
})
|
|
.addButton((button) => {
|
|
button.setButtonText('Tags');
|
|
if (isViewTypeTags(this.plugin.settings)) {
|
|
button.setCta();
|
|
}
|
|
button.onClick(async () => {
|
|
this.plugin.settings.viewType = 'tags';
|
|
await this.plugin.saveSettings();
|
|
this.display();
|
|
});
|
|
})
|
|
.addButton((button) => {
|
|
button.setButtonText('RegExp');
|
|
if (isViewTypeRegExp(this.plugin.settings)) {
|
|
button.setCta();
|
|
}
|
|
button.onClick(async () => {
|
|
this.plugin.settings.viewType = 'regexp';
|
|
await this.plugin.saveSettings();
|
|
this.display();
|
|
});
|
|
});
|
|
}
|
|
|
|
renderFileLocation() {
|
|
if (!isViewTypeFolder(this.plugin.settings)) {
|
|
return;
|
|
}
|
|
new Setting(this.containerEl)
|
|
.setName('Folder location')
|
|
.setDesc('Find files within the folder')
|
|
.addSearch((cb) => {
|
|
new FolderSuggest(this.app, cb.inputEl, this.plugin);
|
|
cb.setPlaceholder('Example: folder1/')
|
|
.setValue(this.plugin.settings.folderName)
|
|
.onChange((newFolder) => {
|
|
this.plugin.settings.folderName = newFolder;
|
|
this.plugin.saveSettings();
|
|
this.getFilesByFolder();
|
|
});
|
|
// @ts-ignore
|
|
cb.containerEl.addClass('bulk_rename');
|
|
cb.inputEl.addClass('bulk_input');
|
|
cb.inputEl.onblur = this.reRenderPreview;
|
|
});
|
|
}
|
|
|
|
renderTagNames() {
|
|
if (!isViewTypeTags(this.plugin.settings)) {
|
|
return;
|
|
}
|
|
|
|
new Setting(this.containerEl)
|
|
.setName('Tag names ')
|
|
.setDesc('all files with the tags will be found')
|
|
.addSearch((cb) => {
|
|
cb.inputEl.addEventListener('keydown', (event) => {
|
|
if (event.key !== 'Enter') {
|
|
return;
|
|
}
|
|
const target = event.target as HTMLInputElement;
|
|
|
|
this.plugin.settings.tags = target.value.replace(/ /g, '').split(',');
|
|
this.plugin.saveSettings();
|
|
});
|
|
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('bulk_rename');
|
|
cb.inputEl.addClass('bulk_input');
|
|
cb.inputEl.onblur = this.reRenderPreview;
|
|
});
|
|
}
|
|
|
|
renderRegExpInput() {
|
|
if (!isViewTypeRegExp(this.plugin.settings)) {
|
|
return;
|
|
}
|
|
|
|
const settings = new Setting(this.containerEl);
|
|
settings.infoEl.addClass('bulk_regexp_search');
|
|
settings.setClass('bulk_regexp_container');
|
|
settings
|
|
.setName('RegExp Search')
|
|
.addText((cb) => {
|
|
const backslash = createBackslash('/');
|
|
cb.inputEl.insertAdjacentElement('beforebegin', backslash);
|
|
// @ts-ignore
|
|
cb.inputEl.addEventListener('keydown', (event) => {
|
|
if (event.key !== 'Enter') {
|
|
return;
|
|
}
|
|
const target = event.target as HTMLInputElement;
|
|
|
|
this.plugin.settings.regExpState.regExp = target.value;
|
|
this.plugin.saveSettings();
|
|
});
|
|
cb.setPlaceholder('Put your RegExp here')
|
|
.setValue(this.plugin.settings.regExpState.regExp)
|
|
.onChange((newFolder) => {
|
|
this.plugin.settings.regExpState.regExp = newFolder;
|
|
this.plugin.saveSettings();
|
|
this.getFilesByRegExp();
|
|
});
|
|
// @ts-ignore
|
|
cb.inputEl.addClass('bulk_regexp');
|
|
cb.inputEl.onblur = this.reRenderPreview;
|
|
})
|
|
.addText((cb) => {
|
|
new RegExpFlagsSuggest(this.app, cb.inputEl, this.plugin);
|
|
const backslash = createBackslash('/');
|
|
cb.inputEl.insertAdjacentElement('beforebegin', backslash);
|
|
cb.inputEl.addEventListener('keydown', (event) => {
|
|
// @ts-ignore
|
|
event.stopPropagation();
|
|
event.stopImmediatePropagation();
|
|
event.preventDefault();
|
|
});
|
|
cb.setPlaceholder('flags here')
|
|
// .setDisabled(true)
|
|
.setValue(this.plugin.settings.regExpState.flags.join(''))
|
|
.onChange((flag: RegExpFlag) => {
|
|
this.plugin.saveSettings();
|
|
this.getFilesByRegExp();
|
|
this.reRenderPreview();
|
|
});
|
|
cb.inputEl.addClass('bulk_regexp_flags');
|
|
})
|
|
.controlEl.addClass('bulk_regexp_control');
|
|
}
|
|
|
|
renderUseRegExpForExistingAndReplacement() {
|
|
if (!isViewTypeRegExp(this.plugin.settings)) {
|
|
return;
|
|
}
|
|
|
|
const newSettings = new Setting(this.containerEl);
|
|
newSettings.setClass('bulk_toggle');
|
|
newSettings
|
|
.setName('Use RegExp For Existing & Replacement?')
|
|
.setDesc(
|
|
"Only RegExp will work now, however it doesn't prevent you to pass string",
|
|
)
|
|
.addToggle((toggle) => {
|
|
toggle
|
|
.setValue(
|
|
this.plugin.settings.regExpState.withRegExpForReplaceSymbols,
|
|
)
|
|
.setTooltip('Use RegExp For Existing & Replacement?')
|
|
.onChange((isRegExpForNames) => {
|
|
this.plugin.settings.regExpState.withRegExpForReplaceSymbols =
|
|
isRegExpForNames;
|
|
this.reRenderPreview();
|
|
this.plugin.saveSettings();
|
|
});
|
|
});
|
|
}
|
|
|
|
renderReplaceSymbol() {
|
|
const { settings } = this.plugin;
|
|
|
|
this.renderUseRegExpForExistingAndReplacement();
|
|
const newSettings = new Setting(this.containerEl);
|
|
if (Platform.isDesktop) {
|
|
const previewLabel = createPreviewElement('Existing');
|
|
const replacementLabel = createPreviewElement('Replacement');
|
|
newSettings.infoEl.replaceChildren(previewLabel, replacementLabel);
|
|
newSettings.setClass('flex');
|
|
newSettings.setClass('flex-col');
|
|
newSettings.infoEl.addClass('bulk_info');
|
|
}
|
|
newSettings.controlEl.addClass('replaceRenderSymbols');
|
|
newSettings.addTextArea((textComponent) => {
|
|
textComponent.setValue(settings.existingSymbol);
|
|
textComponent.setPlaceholder('existing chars');
|
|
textComponent.onChange((newValue) => {
|
|
settings.existingSymbol = newValue;
|
|
this.plugin.saveSettings();
|
|
});
|
|
textComponent.inputEl.addClass('bulk_input');
|
|
textComponent.inputEl.onblur = this.reRenderPreview;
|
|
});
|
|
|
|
newSettings.addTextArea((textComponent) => {
|
|
textComponent.setValue(settings.replacePattern);
|
|
textComponent.setPlaceholder('replace with');
|
|
textComponent.onChange((newValue) => {
|
|
settings.replacePattern = newValue;
|
|
this.plugin.saveSettings();
|
|
this.getFilesByFolder();
|
|
});
|
|
textComponent.inputEl.addClass('bulk_input');
|
|
textComponent.inputEl.onblur = this.reRenderPreview;
|
|
});
|
|
}
|
|
|
|
renderFilesAndPreview = () => {
|
|
this.containerEl.createEl('h2', { text: 'Preview' }, (el) => {
|
|
el.className = 'bulk_preview_header';
|
|
});
|
|
|
|
this.filesAndPreview = new Setting(this.containerEl);
|
|
this.totalFiles = this.containerEl.createEl('span', {
|
|
text: `Total Files: ${this.plugin.settings.fileNames.length}`,
|
|
});
|
|
|
|
this.filesAndPreview.infoEl.detach();
|
|
|
|
this.filesAndPreview.controlEl.addClass('bulk_rename_preview');
|
|
this.reRenderPreview();
|
|
};
|
|
|
|
renderRenameFiles() {
|
|
const desc = document.createDocumentFragment();
|
|
desc.append(
|
|
'You are going to update all files from preview section',
|
|
desc.createEl('br'),
|
|
desc.createEl('b', {
|
|
text: 'Warning: ',
|
|
}),
|
|
'Make sure you verified all files in preview',
|
|
);
|
|
|
|
new Setting(this.containerEl)
|
|
.setDesc(desc)
|
|
.setName('Replace patterns')
|
|
.addButton((button) => {
|
|
button.setClass('bulk_button');
|
|
button.setTooltip("Your files won't be changed");
|
|
button.setButtonText('Preview');
|
|
button.onClick(this.reRenderPreview);
|
|
})
|
|
.addButton((button) => {
|
|
button.setClass('bulk_button');
|
|
button.setTooltip(
|
|
"We don't have undone button yet!\r\n Do we need it?",
|
|
);
|
|
button.setButtonText('Rename');
|
|
button.onClick(async () => {
|
|
button.setDisabled(true);
|
|
await renameFilesInObsidian(this.app, this.plugin);
|
|
this.reRenderPreview();
|
|
button.setDisabled(false);
|
|
});
|
|
});
|
|
}
|
|
|
|
renderSupportDevelopment() {
|
|
renderDonateButton(this.containerEl);
|
|
}
|
|
|
|
reRenderPreview = () => {
|
|
this.calculateFileNames();
|
|
renderPreviewFiles(this.filesAndPreview, this.plugin, this.state);
|
|
this.totalFiles.setText(
|
|
`Total Files: ${this.plugin.settings.fileNames.length}`,
|
|
);
|
|
};
|
|
|
|
calculateFileNames() {
|
|
if (isViewTypeTags(this.plugin.settings)) {
|
|
this.getFilesByTags();
|
|
return;
|
|
}
|
|
|
|
if (isViewTypeRegExp(this.plugin.settings)) {
|
|
this.getFilesByRegExp();
|
|
return;
|
|
}
|
|
|
|
this.getFilesByFolder();
|
|
}
|
|
|
|
getFilesByFolder() {
|
|
this.plugin.settings.fileNames = getObsidianFilesByFolderName(
|
|
this.app,
|
|
this.plugin,
|
|
);
|
|
}
|
|
|
|
getFilesByTags() {
|
|
this.plugin.settings.fileNames = getObsidianFilesWithTagName(
|
|
this.app,
|
|
this.plugin,
|
|
);
|
|
}
|
|
|
|
getFilesByRegExp() {
|
|
this.plugin.settings.fileNames = getObsidianFilesByRegExp(
|
|
this.app,
|
|
this.plugin,
|
|
);
|
|
}
|
|
}
|
|
|
|
export default BulkRenamePlugin;
|