This commit is contained in:
Oleg 2022-08-28 12:39:54 +03:00
parent 6e45036c19
commit 697c78f6cc
10 changed files with 18206 additions and 410 deletions

View File

@ -1,10 +1,12 @@
{
"root": true,
"parser": "@typescript-eslint/parser",
"env": { "node": true },
"plugins": [
"@typescript-eslint"
],
"env": {
"browser": true,
"node": true,
"jest/globals": true
},
"plugins": ["@typescript-eslint", "jest"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
@ -20,4 +22,4 @@
"no-prototype-builtins": "off",
"@typescript-eslint/no-empty-function": "off"
}
}
}

View File

@ -1,17 +1,17 @@
import esbuild from "esbuild";
import process from "process";
import builtins from 'builtin-modules'
import esbuild from 'esbuild';
import process from 'process';
import builtins from 'builtin-modules';
const banner =
`/*
const banner = `/*
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
if you want to view the source, please visit the github repository of this plugin
*/
`;
const prod = (process.argv[2] === 'production');
const prod = process.argv[2] === 'production';
esbuild.build({
esbuild
.build({
banner: {
js: banner,
},
@ -31,12 +31,14 @@ esbuild.build({
'@lezer/common',
'@lezer/highlight',
'@lezer/lr',
...builtins],
...builtins,
],
format: 'cjs',
watch: !prod,
target: 'es2018',
logLevel: "info",
logLevel: 'info',
sourcemap: prod ? false : 'inline',
treeShaking: true,
outfile: 'main.js',
}).catch(() => process.exit(1));
})
.catch(() => process.exit(1));

145
main.ts
View File

@ -1,6 +1,21 @@
import { App, Plugin, PluginSettingTab, Setting, TFile } from 'obsidian';
import {
App,
Notice,
Plugin,
PluginSettingTab,
Setting,
TFile,
} from 'obsidian';
import { FolderSuggest } from './src/suggestions/folderSuggest';
import { renderDonateButton } from './src/components/DonateButton';
import {
getFilesNamesInDirectory,
getObsidianFiles,
getRenderedFileNamesReplaced,
renameFilesInObsidian,
syncScrolls,
} from './src/services/file.service';
import { createPreviewElement } from './src/components/PreviewElement';
interface MyPluginSettings {
folderName: string;
@ -16,31 +31,38 @@ const DEFAULT_SETTINGS: MyPluginSettings = {
replacePattern: '',
};
export default class MyPlugin extends Plugin {
class BulkRenamePlugin extends Plugin {
settings: MyPluginSettings;
async onload() {
await this.loadSettings();
this.addSettingTab(new BulkRenameSettingsTab(this.app, this));
// this.app.vault.on('rename', (file, oldPath) => {
// });
}
onunload() {}
async loadSettings() {
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
}
async saveSettings() {
await this.saveData(this.settings);
}
}
type State = {
export type State = {
previewScroll: number;
filesScroll: number;
};
class BulkRenameSettingsTab extends PluginSettingTab {
plugin: MyPlugin;
plugin: BulkRenamePlugin;
state: State;
constructor(app: App, plugin: MyPlugin) {
constructor(app: App, plugin: BulkRenamePlugin) {
super(app, plugin);
this.state = {
previewScroll: 0,
@ -54,6 +76,7 @@ class BulkRenameSettingsTab extends PluginSettingTab {
const { containerEl } = this;
containerEl.empty();
containerEl.createEl('h2', { text: 'Bulk Rename - Settings' });
this.renderFileLocation();
this.renderReplaceSymbol();
this.renderFilesAndPreview();
@ -75,7 +98,7 @@ class BulkRenameSettingsTab extends PluginSettingTab {
desc.appendChild(button);
new Setting(this.containerEl)
.setName('Symbols in existing file name')
.setName('Existing Symbol')
.setDesc(desc)
.addText((textComponent) => {
const previewLabel = createPreviewElement('Replacement symbols');
@ -131,10 +154,12 @@ class BulkRenameSettingsTab extends PluginSettingTab {
.setDesc(`Total Files: ${this.plugin.settings.fileNames.length}`)
.addTextArea((text) => {
text.setPlaceholder('Here you will see files under folder location');
existingFilesTextArea = text.inputEl;
const value = getRenderedFileNames(this.plugin);
text.setValue(value);
text.setDisabled(true);
existingFilesTextArea = text.inputEl;
const value = getFilesNamesInDirectory(this.plugin);
text.setValue(value);
const previewLabel = createPreviewElement();
text.inputEl.insertAdjacentElement('afterend', previewLabel);
text.inputEl.addClass('templater_cmd');
@ -143,10 +168,11 @@ class BulkRenameSettingsTab extends PluginSettingTab {
text.setPlaceholder(
'How filenames will looks like after replacement(click preview first)',
);
text.setDisabled(true);
replacedPreviewTextArea = text.inputEl;
const value = getRenderedFileNamesReplaced(this.plugin);
text.setValue(value);
text.setDisabled(true);
text.inputEl.addClass('templater_cmd');
})
.then((setting) => {
@ -172,18 +198,10 @@ class BulkRenameSettingsTab extends PluginSettingTab {
button.buttonEl.style.width = '100%';
button.setTooltip("FYI: We don't have undone button yet!");
button.setButtonText('Rename');
button.onClick(() => {
const { replacePattern, existingSymbol } = this.plugin.settings;
if (!replacePattern || !existingSymbol) {
return;
}
this.plugin.settings.fileNames.forEach((fileName) => {
this.app.fileManager.renameFile(
fileName,
replaceFilePath(this.plugin, fileName),
);
});
button.onClick(async () => {
button.setDisabled(true);
renameFilesInObsidian(this.app, this.plugin);
this.display();
});
});
}
@ -193,87 +211,8 @@ class BulkRenameSettingsTab extends PluginSettingTab {
}
calculateFiles() {
this.plugin.settings.fileNames = [
...getObsidianFiles(this.app, this.plugin.settings.folderName),
];
this.plugin.settings.fileNames = getObsidianFiles(this.app, this.plugin);
}
}
const getObsidianFiles = (app: App, folderName: string) => {
const abstractFiles = app.vault.getAllLoadedFiles();
const files = [] as TFile[];
abstractFiles.forEach((file) => {
if (file instanceof TFile && file.parent.name.includes(folderName)) {
files.push(file);
}
});
return sortByname(files);
};
const sortByname = (files: TFile[]) => {
return files.sort((a, b) => a.name.localeCompare(b.name));
};
const getRenderedFileNames = (plugin: MyPlugin) => {
return prepareFileNameString(plugin.settings.fileNames);
};
const prepareFileNameString = (filesNames: TFile[]) => {
let value = '';
filesNames.forEach((fileName, index) => {
const isLast = index + 1 === filesNames.length;
if (isLast) {
return (value += fileName.path);
}
value += fileName.path + '\r\n';
});
return value;
};
const getRenderedFileNamesReplaced = (plugin: MyPlugin) => {
const { fileNames } = plugin.settings;
const newFiles = fileNames.map((file) => {
return {
...file,
path: replaceFilePath(plugin, file),
};
});
return prepareFileNameString(newFiles);
};
const replaceFilePath = (plugin: MyPlugin, file: TFile) => {
const { replacePattern, existingSymbol } = plugin.settings;
return file.path.replaceAll(existingSymbol, replacePattern);
};
const createPreviewElement = (textContent = '=> => => =>') => {
const previewLabel = window.document.createElement('span');
previewLabel.className = 'previewLabel';
previewLabel.textContent = textContent;
previewLabel.style.margin = '0 20px';
return previewLabel;
};
const syncScrolls = (
existingFilesArea: HTMLTextAreaElement,
previewArea: HTMLTextAreaElement,
state: State,
) => {
existingFilesArea.addEventListener('scroll', (event) => {
const target = event.target as HTMLTextAreaElement;
if (target.scrollTop !== state.previewScroll) {
previewArea.scrollTop = target.scrollTop;
state.previewScroll = target.scrollTop;
}
});
previewArea.addEventListener('scroll', (event) => {
const target = event.target as HTMLTextAreaElement;
if (target.scrollTop !== state.filesScroll) {
existingFilesArea.scrollTop = target.scrollTop;
state.filesScroll = target.scrollTop;
}
});
};
export default BulkRenamePlugin;

18178
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -6,6 +6,8 @@
"scripts": {
"dev": "node esbuild.config.mjs",
"build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
"test": "jest ./src",
"test:watch": "jest ./src --watch",
"version": "node version-bump.mjs && git add manifest.json versions.json && git push origin --tags"
},
"keywords": [],
@ -15,14 +17,18 @@
"@popperjs/core": "^2.11.2"
},
"devDependencies": {
"@types/jest": "^28.1.8",
"@types/node": "^16.11.6",
"@typescript-eslint/eslint-plugin": "5.29.0",
"@typescript-eslint/parser": "5.29.0",
"builtin-modules": "3.3.0",
"esbuild": "0.14.47",
"esbuild": "0.15.5",
"esbuild-jest": "^0.5.0",
"eslint-plugin-jest": "^26.8.7",
"jest": "29.0.1",
"obsidian": "latest",
"prettier": "2.7.1",
"tslib": "2.4.0",
"typescript": "4.7.4",
"prettier": "2.7.1"
"typescript": "^4.7.4"
}
}

View File

@ -0,0 +1,7 @@
export const createPreviewElement = (textContent = '=> => => =>') => {
const previewLabel = window.document.createElement('span');
previewLabel.className = 'previewLabel';
previewLabel.textContent = textContent;
previewLabel.style.margin = '0 20px';
return previewLabel;
};

View File

@ -0,0 +1,100 @@
import { App, Notice, TFile } from 'obsidian';
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) => {
const { fileNames } = plugin.settings;
return getFilesAsString(fileNames);
};
const getFilesAsString = (fileNames: TFile[]) => {
let value = '';
fileNames.forEach((fileName, index) => {
const isLast = index + 1 === fileNames.length;
if (isLast) {
return (value += fileName.path);
}
value += fileName.path + '\r\n';
});
return value;
};
export const getRenderedFileNamesReplaced = (plugin: BulkRenamePlugin) => {
const newFiles = selectFilenamesWithReplacedPath(plugin);
return getFilesAsString(newFiles);
};
const selectFilenamesWithReplacedPath = (plugin: BulkRenamePlugin) => {
const { fileNames } = plugin.settings;
return fileNames.map((file) => {
return {
...file,
path: replaceFilePath(plugin, file),
};
});
};
export const replaceFilePath = (plugin: BulkRenamePlugin, file: TFile) => {
const { replacePattern, existingSymbol } = plugin.settings;
return file.path.replaceAll(existingSymbol, replacePattern);
};
export const renameFilesInObsidian = (app: App, plugin: BulkRenamePlugin) => {
const { replacePattern, existingSymbol } = plugin.settings;
if (!existingSymbol) {
new Notice('please fill Existing Symbol');
return;
}
if (replacePattern === existingSymbol) {
new Notice("Replace Pattern shouldn't much Existing Symbol");
return;
}
new Notice('renaming has been started');
for (const fileName of plugin.settings.fileNames) {
app.fileManager.renameFile(fileName, replaceFilePath(plugin, fileName));
}
new Notice('successfully renamed all files');
};
export const syncScrolls = (
existingFilesArea: HTMLTextAreaElement,
previewArea: HTMLTextAreaElement,
state: State,
) => {
existingFilesArea.addEventListener('scroll', (event) => {
const target = event.target as HTMLTextAreaElement;
if (target.scrollTop !== state.previewScroll) {
previewArea.scrollTop = target.scrollTop;
state.previewScroll = target.scrollTop;
}
});
previewArea.addEventListener('scroll', (event) => {
const target = event.target as HTMLTextAreaElement;
if (target.scrollTop !== state.filesScroll) {
existingFilesArea.scrollTop = target.scrollTop;
state.filesScroll = target.scrollTop;
}
});
};

View File

@ -0,0 +1,44 @@
import { TFile } from 'obsidian';
import { replaceFilePath } from './file.service';
import BulkRenamePlugin from '../../main';
describe('File Services', () => {
describe('Notifications', () => {
it.todo('should display notification before renaming');
it.todo('should display notification after renaming');
});
describe('Validation', () => {
it.todo('should display notification if there an error');
it.todo('should display notification if there an error');
it.todo("should display notification if Existing Symbol doesn't exists");
it.todo(
'should display notification if Existing Symbol match Replace Pattern',
);
});
describe('Renaming', () => {
it('should not rename extensions', () => {
const plugin = {
settings: {
replacePattern: '-',
existingSymbol: '.',
},
} as unknown as BulkRenamePlugin;
const file = {
path: '2022.10.13.md',
} as unknown as TFile;
const expectedResult = '2022-10-13.md';
const result = replaceFilePath(plugin, file);
expect(result).toEqual(expectedResult);
});
it.todo('should replace symbols in naming');
it.todo(
'Should rename files only in a particular directory if the name could match other directories',
);
});
});

View File

@ -0,0 +1 @@
export {};

View File

@ -4,14 +4,19 @@
"inlineSourceMap": true,
"inlineSources": true,
"module": "ESNext",
"target": "ES6",
"target": "es2018",
"allowJs": true,
"noImplicitAny": true,
"noImplicitThis": true,
"noUnusedLocals": true,
"moduleResolution": "node",
"importHelpers": true,
"isolatedModules": true,
"downlevelIteration": true,
"esModuleInterop": true,
"isolatedModules": false,
"strictNullChecks": true,
"lib": ["DOM", "ES5", "ES6", "ES7", "ES2021"]
},
"typeRoots": ["node_modules/@types", "obsidian"],
"include": ["**/*.ts"]
}