feat: add posibilty to put RegExp to Existing and Replacement + styles improvements

This commit is contained in:
Oleh Lustenko 2023-02-18 19:44:50 +02:00
parent e6ba6e88e3
commit 6ea539162b
5 changed files with 200 additions and 35 deletions

61
main.ts
View File

@ -34,6 +34,7 @@ export interface BulkRenamePluginSettings {
tags: string[]; tags: string[];
regExpState: { regExpState: {
regExp: string; regExp: string;
withRegExpForReplaceSymbols: boolean;
flags: RegExpFlag[]; flags: RegExpFlag[];
}; };
viewType: 'tags' | 'folder' | 'regexp'; viewType: 'tags' | 'folder' | 'regexp';
@ -47,6 +48,7 @@ const DEFAULT_SETTINGS: BulkRenamePluginSettings = {
regExpState: { regExpState: {
regExp: '', regExp: '',
flags: [], flags: [],
withRegExpForReplaceSymbols: false,
}, },
tags: [], tags: [],
viewType: 'folder', viewType: 'folder',
@ -269,20 +271,48 @@ export class BulkRenameSettingsTab extends PluginSettingTab {
.controlEl.addClass('bulk_regexp_control'); .controlEl.addClass('bulk_regexp_control');
} }
renderUseRegExpForExistingAndReplacement() {
if (!isViewTypeRegExp(this.plugin.settings)) {
return;
}
const newSettings2 = new Setting(this.containerEl);
newSettings2
.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() { renderReplaceSymbol() {
const { settings } = this.plugin; const { settings } = this.plugin;
this.renderUseRegExpForExistingAndReplacement();
const newSettings = new Setting(this.containerEl); const newSettings = new Setting(this.containerEl);
newSettings.infoEl.style.display = 'none'; if (Platform.isDesktop) {
const previewLabel = createPreviewElement('Existing');
newSettings.addText((textComponent) => { const replacementLabel = createPreviewElement('Replacement');
if (Platform.isDesktop) { newSettings.infoEl.replaceChildren(previewLabel, replacementLabel);
const previewLabel = createPreviewElement('Existing'); newSettings.setClass('flex');
textComponent.inputEl.insertAdjacentElement( newSettings.setClass('flex-col');
'beforebegin', newSettings.infoEl.addClass('bulk_info');
previewLabel, }
); newSettings.controlEl.addClass('replaceRenderSymbols');
} newSettings.addTextArea((textComponent) => {
textComponent.setValue(settings.existingSymbol); textComponent.setValue(settings.existingSymbol);
textComponent.setPlaceholder('existing chars'); textComponent.setPlaceholder('existing chars');
textComponent.onChange((newValue) => { textComponent.onChange((newValue) => {
@ -293,14 +323,7 @@ export class BulkRenameSettingsTab extends PluginSettingTab {
textComponent.inputEl.onblur = this.reRenderPreview; textComponent.inputEl.onblur = this.reRenderPreview;
}); });
newSettings.addText((textComponent) => { newSettings.addTextArea((textComponent) => {
if (Platform.isDesktop) {
const previewLabel = createPreviewElement('Replacement');
textComponent.inputEl.insertAdjacentElement(
'beforebegin',
previewLabel,
);
}
textComponent.setValue(settings.replacePattern); textComponent.setValue(settings.replacePattern);
textComponent.setPlaceholder('replace with'); textComponent.setPlaceholder('replace with');
textComponent.onChange((newValue) => { textComponent.onChange((newValue) => {
@ -323,7 +346,7 @@ export class BulkRenameSettingsTab extends PluginSettingTab {
text: `Total Files: ${this.plugin.settings.fileNames.length}`, text: `Total Files: ${this.plugin.settings.fileNames.length}`,
}); });
this.filesAndPreview.infoEl.style.display = 'none'; this.filesAndPreview.infoEl.detach();
this.filesAndPreview.controlEl.addClass('bulk_rename_preview'); this.filesAndPreview.controlEl.addClass('bulk_rename_preview');
this.reRenderPreview(); this.reRenderPreview();

View File

@ -52,6 +52,7 @@ export const syncScrolls = (
) => { ) => {
existingFilesArea.addEventListener('scroll', (event) => { existingFilesArea.addEventListener('scroll', (event) => {
const target = event.target as HTMLTextAreaElement; const target = event.target as HTMLTextAreaElement;
if (target.scrollTop !== state.previewScroll) { if (target.scrollTop !== state.previewScroll) {
previewArea.scrollTop = target.scrollTop; previewArea.scrollTop = target.scrollTop;
state.previewScroll = target.scrollTop; state.previewScroll = target.scrollTop;

View File

@ -1,4 +1,5 @@
import { App, Notice, TFile } from 'obsidian'; import { App, Notice, TFile } from 'obsidian';
import XRegExp from 'xregexp';
import BulkRenamePlugin, { BulkRenamePluginSettings } from '../../main'; import BulkRenamePlugin, { BulkRenamePluginSettings } from '../../main';
import { isViewTypeFolder } from './settings.service'; import { isViewTypeFolder } from './settings.service';
import { ROOT_FOLDER_NAME } from '../constants/folders'; import { ROOT_FOLDER_NAME } from '../constants/folders';
@ -49,16 +50,24 @@ export const selectFilenamesWithReplacedPath = (plugin: BulkRenamePlugin) => {
export const replaceFilePath = (plugin: BulkRenamePlugin, file: TFile) => { export const replaceFilePath = (plugin: BulkRenamePlugin, file: TFile) => {
const pathWithoutExtension = file.path.split('.').slice(0, -1).join('.'); const pathWithoutExtension = file.path.split('.').slice(0, -1).join('.');
const { replacePattern, existingSymbol } = plugin.settings; const { replacePattern, existingSymbol, regExpState } = plugin.settings;
if (isRootFilesSelected(plugin)) { if (isRootFilesSelected(plugin)) {
const newPath = replacePattern + pathWithoutExtension; const newPath = replacePattern + pathWithoutExtension;
return `${newPath}.${file.extension}`; return `${newPath}.${file.extension}`;
} }
const convertedToRegExpString = escapeRegExp(existingSymbol); let regExpExistingSymbol: RegExp | string = existingSymbol;
const regExpSymbol = new RegExp(convertedToRegExpString, 'g'); if (regExpState.withRegExpForReplaceSymbols) {
const newPath = pathWithoutExtension?.replace(regExpSymbol, replacePattern); regExpExistingSymbol = XRegExp(existingSymbol, 'x');
}
const newPath = XRegExp.replace(
pathWithoutExtension,
regExpExistingSymbol,
replacePattern,
'all',
);
return `${newPath}.${file.extension}`; return `${newPath}.${file.extension}`;
}; };
@ -102,13 +111,6 @@ export const renameFilesInObsidian = async (
success && new Notice('successfully renamed all files'); success && new Notice('successfully renamed all files');
}; };
let reRegExpChar = /[\\^$.*+?()[\]{}]/g,
reHasRegExpChar = RegExp(reRegExpChar.source);
export function escapeRegExp(s: string) {
return s && reHasRegExpChar.test(s) ? s.replace(reRegExpChar, '\\$&') : s;
}
const isRootFilesSelected = (plugin: BulkRenamePlugin) => { const isRootFilesSelected = (plugin: BulkRenamePlugin) => {
const { existingSymbol, folderName } = plugin.settings; const { existingSymbol, folderName } = plugin.settings;

View File

@ -28,6 +28,9 @@ describe('File Services', () => {
settings: { settings: {
replacePattern: '-', replacePattern: '-',
existingSymbol: '_', existingSymbol: '_',
regExpState: {
withRegExpForReplaceSymbols: false,
},
}, },
} as unknown as BulkRenamePlugin; } as unknown as BulkRenamePlugin;
@ -48,6 +51,9 @@ describe('File Services', () => {
settings: { settings: {
replacePattern: '-', replacePattern: '-',
existingSymbol: '.', existingSymbol: '.',
regExpState: {
withRegExpForReplaceSymbols: false,
},
}, },
} as unknown as BulkRenamePlugin; } as unknown as BulkRenamePlugin;
@ -68,6 +74,9 @@ describe('File Services', () => {
settings: { settings: {
replacePattern: 'days', replacePattern: 'days',
existingSymbol: 'journals', existingSymbol: 'journals',
regExpState: {
withRegExpForReplaceSymbols: false,
},
}, },
} as unknown as BulkRenamePlugin; } as unknown as BulkRenamePlugin;
@ -106,6 +115,9 @@ describe('File Services', () => {
const mockPluginPlugin = { const mockPluginPlugin = {
settings: { settings: {
fileNames: files, fileNames: files,
regExpState: {
withRegExpForReplaceSymbols: false,
},
}, },
} as unknown as BulkRenamePlugin; } as unknown as BulkRenamePlugin;
@ -116,6 +128,9 @@ describe('File Services', () => {
...mockPluginPlugin.settings, ...mockPluginPlugin.settings,
existingSymbol: 'journals|pages|bulkRenameTets|canWe', existingSymbol: 'journals|pages|bulkRenameTets|canWe',
replacePattern: 'qwe', replacePattern: 'qwe',
regExpState: {
withRegExpForReplaceSymbols: true,
},
}, },
} as unknown as BulkRenamePlugin; } as unknown as BulkRenamePlugin;
@ -157,6 +172,99 @@ describe('File Services', () => {
expect(files).toEqual(updatedFiles); expect(files).toEqual(updatedFiles);
}); });
it('should rename many files using capture groups', () => {
const plugin = {
...mockPluginPlugin,
settings: {
...mockPluginPlugin.settings,
existingSymbol: '2022_(.+)',
replacePattern: '$1_2022',
regExpState: {
withRegExpForReplaceSymbols: true,
},
},
} as unknown as BulkRenamePlugin;
const expectedResults = [
{
path: 'journals/10_13_2022.md',
extension: 'md',
},
{
path: 'pages/10_13_2022.md',
extension: 'md',
},
{
path: 'bulkRenameTets/10_13_2022.md',
extension: 'md',
},
{
path: 'YesWecan/canWe/10_13_2022.md',
extension: 'md',
},
];
const updatedFiles = selectFilenamesWithReplacedPath(plugin);
expect(expectedResults).toEqual(updatedFiles);
});
it('should rename many files using capture groups', () => {
const files = [
{
path: '2022_10_13.md',
extension: 'md',
},
{
path: '2022_10_14.md',
extension: 'md',
},
{
path: '2022_10_15.md',
extension: 'md',
},
{
path: '2022_10_16.md',
extension: 'md',
},
] as unknown as TFile[];
const plugin = {
settings: {
fileNames: files,
existingSymbol: `(?<year> [0-9]{4} ) _? # year
(?<month> [0-9]{2} ) _? # month
(?<day> [0-9]{2} ) # day`,
replacePattern: '$<month>-$<day>-$<year>',
regExpState: {
withRegExpForReplaceSymbols: true,
},
},
} as unknown as BulkRenamePlugin;
const expectedResults = [
{
path: '10-13-2022.md',
extension: 'md',
},
{
path: '10-14-2022.md',
extension: 'md',
},
{
path: '10-15-2022.md',
extension: 'md',
},
{
path: '10-16-2022.md',
extension: 'md',
},
];
const updatedFiles = selectFilenamesWithReplacedPath(plugin);
expect(expectedResults).toEqual(updatedFiles);
});
}); });
}); });
}); });

View File

@ -8,10 +8,38 @@
gap: 0; gap: 0;
} }
.flex {
display: flex;
}
.flex-col {
flex-direction: column;
}
.m-auto {
margin: auto;
}
.bulk_info {
display: flex;
justify-content: space-between;
margin: auto;
width: 100%;
}
.bulk_rename_preview > textarea { .bulk_rename_preview > textarea {
height: 360px; height: 360px;
} }
.replaceRenderSymbols {
display: flex;
width: 100%;
padding-top: 1rem;
}
.setting-item-control .bulk_preview_textarea {
min-width: 19em;
}
.bulk_preview_textarea { .bulk_preview_textarea {
margin-left: 5px; margin-left: 5px;
margin-right: 5px; margin-right: 5px;
@ -19,9 +47,6 @@
width: 100%; width: 100%;
height: 400px; height: 400px;
resize: none; resize: none;
width: 100%;
/*white-space: nowrap;*/
/*overflow: auto;*/
} }
.bulk_button { .bulk_button {
@ -33,12 +58,18 @@
margin-bottom: 5px; margin-bottom: 5px;
} }
.bulk_input { .setting-item-control .bulk_input {
width: 100%; width: 100%;
resize: none;
min-width: auto;
min-height: auto;
}
.setting-item-control .bulk_input:first-child {
margin-right: 15px; margin-right: 15px;
} }
.bulk_preview_label { .bulk_preview_label:first-child {
margin-right: 15px; margin-right: 15px;
} }