This commit is contained in:
Michael Naumov 2024-11-04 22:19:24 -06:00
parent 1f333d68d9
commit d26056e403
6 changed files with 275 additions and 1334 deletions

1356
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -16,10 +16,10 @@
"license": "MIT",
"devDependencies": {
"@tsconfig/strictest": "^2.0.5",
"@types/node": "^22.8.7",
"@types/node": "^22.9.0",
"obsidian": "^1.7.2",
"obsidian-dev-utils": "^4.0.0",
"obsidian-typings": "^2.2.1-beta.34"
"obsidian-dev-utils": "^4.4.0",
"obsidian-typings": "^2.3.0"
},
"type": "module"
}

View file

@ -1,21 +1,24 @@
import type {
CachedMetadata,
LinkCache
LinkCache,
PluginSettingTab
} from 'obsidian';
import {
Notice,
parseFrontMatterAliases,
Plugin,
Platform,
TFile
} from 'obsidian';
import type { MaybePromise } from 'obsidian-dev-utils/Async';
import { invokeAsyncSafely } from 'obsidian-dev-utils/Async';
import { PluginBase } from 'obsidian-dev-utils/obsidian/Plugin/PluginBase';
import { InvalidCharacterAction } from './InvalidCharacterAction.ts';
import prompt from './prompt.ts';
import SmartRenameSettings from './SmartRenameSettings.ts';
import SmartRenameSettingsTab from './SmartRenameSettingsTab.ts';
import SmartRenamePluginSettings from './SmartRenamePluginSettings.ts';
import SmartRenamePluginSettingsTab from './SmartRenamePluginSettingsTab.ts';
export default class SmartRenamePlugin extends Plugin {
export default class SmartRenamePlugin extends PluginBase<SmartRenamePluginSettings> {
private systemForbiddenCharactersRegExp!: RegExp;
private readonly obsidianForbiddenCharactersRegExp = /[#^[\]|]/g;
private currentNoteFile!: TFile;
@ -24,11 +27,16 @@ export default class SmartRenamePlugin extends Plugin {
private newPath!: string;
private readonly backlinksToFix: Map<string, Set<number>> = new Map<string, Set<number>>();
private isReadyToFixBacklinks!: boolean;
public settings!: SmartRenameSettings;
public override async onload(): Promise<void> {
await this.loadSettings();
protected override createDefaultPluginSettings(): SmartRenamePluginSettings {
return new SmartRenamePluginSettings();
}
protected override createPluginSettingsTab(): PluginSettingTab | null {
return new SmartRenamePluginSettingsTab(this);
}
protected override onloadComplete(): MaybePromise<void> {
this.addCommand({
id: 'smart-rename',
name: 'Smart Rename',
@ -39,15 +47,13 @@ export default class SmartRenamePlugin extends Plugin {
}
if (!checking) {
void this.smartRename(activeFile);
invokeAsyncSafely(() => this.smartRename(activeFile));
}
return true;
}
});
this.addSettingTab(new SmartRenameSettingsTab(this.app, this));
const isWindows = document.body.hasClass('mod-windows');
const isWindows = Platform.isWin;
this.systemForbiddenCharactersRegExp = isWindows ? /[*"\\/<>:|?]/g : /[\\/]/g;
this.registerEvent(this.app.metadataCache.on('resolved', () => {
@ -246,14 +252,6 @@ export default class SmartRenamePlugin extends Plugin {
this.backlinksToFix.clear();
}
private async loadSettings(): Promise<void> {
this.settings = Object.assign(new SmartRenameSettings(), await this.loadData() as SmartRenameSettings | undefined);
}
public async saveSettings(): Promise<void> {
await this.saveData(this.settings);
}
public hasInvalidCharacters(str: string): boolean {
return this.systemForbiddenCharactersRegExp.test(str) || this.obsidianForbiddenCharactersRegExp.test(str);
}

View file

@ -1,6 +1,6 @@
import { InvalidCharacterAction } from './InvalidCharacterAction.ts';
export default class SmartRenameSettings {
export default class SmartRenamePluginSettings {
public invalidCharacterAction = InvalidCharacterAction.Error;
public replacementCharacter = '_';
public shouldStoreInvalidTitle = true;

View file

@ -0,0 +1,84 @@
import { Setting } from 'obsidian';
import { PluginSettingsTabBase } from 'obsidian-dev-utils/obsidian/Plugin/PluginSettingsTabBase';
import { extend } from 'obsidian-dev-utils/obsidian/Plugin/ValueComponent';
import { InvalidCharacterAction } from './InvalidCharacterAction.ts';
import type SmartRenamePlugin from './SmartRenamePlugin.ts';
export default class SmartRenamePluginSettingsTab extends PluginSettingsTabBase<SmartRenamePlugin> {
public display(): void {
this.containerEl.empty();
new Setting(this.containerEl)
.setName('Invalid characters action')
.setDesc('How to process invalid characters in the new title')
.addDropdown((dropdown) => {
dropdown
.addOptions({
Error: 'Show error',
Remove: 'Remove invalid characters',
Replace: 'Replace invalid character with'
});
extend(dropdown)
.bind(this.plugin, 'invalidCharacterAction', {
onChanged: () => { this.display(); }
});
});
if (this.plugin.settingsCopy.invalidCharacterAction === InvalidCharacterAction.Replace) {
new Setting(this.containerEl)
.setName('Replacement character')
.setDesc('Character to replace invalid character with')
.addText((text) => {
text.inputEl.maxLength = 1;
text.inputEl.required = true;
extend(text)
.bind(this.plugin, 'replacementCharacter', {
valueValidator: (value) => {
if (this.plugin.hasInvalidCharacters(value)) {
return 'Invalid replacement character';
} else {
return null;
}
}
});
});
}
if (this.plugin.settingsCopy.invalidCharacterAction !== InvalidCharacterAction.Error) {
new Setting(this.containerEl)
.setName('Store invalid title')
.setDesc('If enabled, stores title with invalid characters. If disabled, stores the sanitized version')
.addToggle((toggle) =>
extend(toggle)
.bind(this.plugin, 'shouldStoreInvalidTitle')
);
}
new Setting(this.containerEl)
.setName('Update title key')
.setDesc('Update title key in frontmatter')
.addToggle((toggle) =>
extend(toggle)
.bind(this.plugin, 'shouldUpdateTitleKey')
);
new Setting(this.containerEl)
.setName('Update first header')
.setDesc(createFragment((f) => {
f.appendText('Update first header if it is present in the document. May conflict with the ');
f.createEl('a', {
attr: {
href: 'https://obsidian.md/plugins?id=obsidian-filename-heading-sync'
},
text: 'Filename Heading Sync'
});
f.appendText(' plugin.');
}))
.addToggle((toggle) =>
extend(toggle)
.bind(this.plugin, 'shouldUpdateFirstHeader')
);
}
}

View file

@ -1,123 +0,0 @@
import {
App,
PluginSettingTab,
Setting
} from 'obsidian';
import { InvalidCharacterAction } from './InvalidCharacterAction.ts';
import type SmartRenamePlugin from './SmartRenamePlugin.ts';
export default class SmartRenameSettingsTab extends PluginSettingTab {
public override plugin: SmartRenamePlugin;
public constructor(app: App, plugin: SmartRenamePlugin) {
super(app, plugin);
this.plugin = plugin;
}
public display(): void {
const { containerEl } = this;
containerEl.empty();
containerEl.createEl('h2', { text: 'Smart Rename' });
new Setting(containerEl)
.setName('Invalid characters action')
.setDesc('How to process invalid characters in the new title')
.addDropdown((dropdownComponent) => {
dropdownComponent
.addOptions({
Error: 'Show error',
Remove: 'Remove invalid characters',
Replace: 'Replace invalid character with'
})
.setValue(this.plugin.settings.invalidCharacterAction)
.onChange(async (value) => {
this.plugin.settings.invalidCharacterAction = InvalidCharacterAction[value as keyof typeof InvalidCharacterAction];
await this.plugin.saveSettings();
this.renderReplacementCharacterSettingEl(replacementCharacterSettingEl);
this.renderStoreInvalidTitleSettingEl(storeInvalidTitleSettingEl);
});
const replacementCharacterSettingEl = containerEl.createDiv();
this.renderReplacementCharacterSettingEl(replacementCharacterSettingEl);
const storeInvalidTitleSettingEl = containerEl.createDiv();
this.renderStoreInvalidTitleSettingEl(storeInvalidTitleSettingEl);
});
new Setting(containerEl)
.setName('Update title key')
.setDesc('Update title key in frontmatter')
.addToggle((toggleComponent) => {
toggleComponent
.setValue(this.plugin.settings.shouldUpdateTitleKey)
.onChange(async (value) => {
this.plugin.settings.shouldUpdateTitleKey = value;
await this.plugin.saveSettings();
});
});
new Setting(containerEl)
.setName('Update first header')
.setDesc('Update first header if it is present in the document. May conflict with the `Filename Heading Sync` plugin')
.addToggle((toggleComponent) => {
toggleComponent
.setValue(this.plugin.settings.shouldUpdateFirstHeader)
.onChange(async (value) => {
this.plugin.settings.shouldUpdateFirstHeader = value;
await this.plugin.saveSettings();
});
});
}
private renderReplacementCharacterSettingEl(replacementCharacterSettingEl: HTMLDivElement): void {
replacementCharacterSettingEl.empty();
if (this.plugin.settings.invalidCharacterAction === InvalidCharacterAction.Replace) {
new Setting(replacementCharacterSettingEl)
.setName('Replacement character')
.setDesc('Character to replace invalid character with')
.addText((textComponent) => {
textComponent.inputEl.maxLength = 1;
textComponent.inputEl.required = true;
textComponent.inputEl.addEventListener('blur', () => {
textComponent.inputEl.reportValidity();
});
textComponent
.setValue(this.plugin.settings.replacementCharacter)
.onChange(async (value) => {
if (this.plugin.hasInvalidCharacters(value)) {
textComponent.inputEl.setCustomValidity('Invalid replacement character');
} else {
textComponent.inputEl.setCustomValidity('');
}
if (textComponent.inputEl.reportValidity()) {
this.plugin.settings.replacementCharacter = value;
await this.plugin.saveSettings();
}
});
});
}
}
private renderStoreInvalidTitleSettingEl(storeInvalidTitleSettingEl: HTMLDivElement): void {
storeInvalidTitleSettingEl.empty();
if (this.plugin.settings.invalidCharacterAction === InvalidCharacterAction.Error) {
return;
}
new Setting(storeInvalidTitleSettingEl)
.setName('Store invalid title')
.setDesc('If enabled, stores title with invalid characters. If disabled, stores the sanitized version')
.addToggle((toggleComponent) => {
toggleComponent
.setValue(this.plugin.settings.shouldStoreInvalidTitle)
.onChange(async (value) => {
this.plugin.settings.shouldStoreInvalidTitle = value;
await this.plugin.saveSettings();
});
});
}
}