feat: add posibilty to put RegExp to Existing and Replacement + styles improvements
This commit is contained in:
parent
e6ba6e88e3
commit
6ea539162b
61
main.ts
61
main.ts
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
41
styles.css
41
styles.css
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue