Exclude folder wildcard and regex support

This commit is contained in:
Lost Paul 2023-06-06 22:11:22 +02:00
parent 24ee7f72a1
commit da68103ac1
8 changed files with 371 additions and 125 deletions

View file

@ -1,7 +1,7 @@
import { App, TFolder, Menu, TAbstractFile, Notice, TFile } from 'obsidian';
import FolderNotesPlugin from './main';
import { ExcludedFolder } from './settings';
import { getFolderNote, createFolderNote, deleteFolderNote } from './folderNoteFunctions';
import { ExcludedFolder } from './excludedFolder';
export class Commands {
plugin: FolderNotesPlugin;
app: App;

View file

@ -1,13 +1,14 @@
import { Keymap } from 'obsidian';
import FolderNotesPlugin from 'src/main';
import { openFolderNote, createFolderNote, getFolderNote } from 'src/folderNoteFunctions';
import { getExcludedFolder } from 'src/excludedFolder';
export async function handleViewHeaderClick(event: MouseEvent, plugin: FolderNotesPlugin) {
if (!(event.target instanceof HTMLElement)) return;
if (!plugin.settings.openFolderNoteOnClickInPath) return;
const folderPath = event.target.getAttribute('data-path');
if (!folderPath) { return; }
const excludedFolder = plugin.getExcludedFolderByPath(folderPath);
const excludedFolder = getExcludedFolder(plugin, folderPath);
if (excludedFolder?.disableFolderNote) {
event.target.onclick = null;
event.target.click();
@ -37,7 +38,7 @@ export async function handleFolderClick(event: MouseEvent, plugin: FolderNotesPl
const folderPath = event.target.parentElement?.getAttribute('data-path');
if (!folderPath) { return; }
const excludedFolder = plugin.getExcludedFolderByPath(folderPath);
const excludedFolder = getExcludedFolder(plugin, folderPath);
if (excludedFolder?.disableFolderNote) {
event.target.onclick = null;
event.target.click();

View file

@ -1,6 +1,7 @@
import { TFile, TFolder, TAbstractFile, Notice } from 'obsidian';
import FolderNotesPlugin from 'src/main';
import { extractFolderName, getFolderNote } from '../folderNoteFunctions';
import { getExcludedFolder } from '../excludedFolder';
export function handleFolderRename(file: TFolder, oldPath: string, plugin: FolderNotesPlugin) {
const fileName = plugin.settings.folderNoteName.replace('{{folder_name}}', file.name);
const folder = plugin.app.vault.getAbstractFileByPath(file.path);
@ -27,7 +28,7 @@ export function handleFolderRename(file: TFolder, oldPath: string, plugin: Folde
});
plugin.saveSettings();
const excludedFolder = plugin.getExcludedFolderByPath(file.path);
const excludedFolder = getExcludedFolder(plugin, file.path);
if (excludedFolder?.disableSync && !folderNote) {
return plugin.removeCSSClassFromEL(file.path, 'has-folder-note');
}
@ -56,7 +57,7 @@ export function handleFileRename(file: TFile, oldPath: string, plugin: FolderNot
const oldFolder = plugin.app.vault.getAbstractFileByPath(oldFilePath);
const newFilePath = plugin.getFolderPathFromString(file.path);
const newFolder = plugin.app.vault.getAbstractFileByPath(newFilePath);
const excludedFolder = plugin.getExcludedFolderByPath(newFolder?.path || '');
const excludedFolder = getExcludedFolder(plugin,newFolder?.path || '');
if (excludedFolder?.disableSync && extractFolderName(plugin.settings.folderNoteName, file.name.slice(0, file.name.lastIndexOf('.'))) === newFolder?.name) {
plugin.addCSSClassToTitleEL(file.path, 'is-folder-note');

259
src/excludedFolder.ts Normal file
View file

@ -0,0 +1,259 @@
import { Setting } from 'obsidian';
import FolderNotesPlugin from './main';
import ExcludedFolderSettings from './modals/exludeFolderSettings';
import { FolderSuggest } from './suggesters/FolderSuggester';
import { SettingsTab } from './settings';
import PatternSettings from './modals/patternSettings';
export class ExcludedFolder {
type: string;
path: string;
string: string;
subFolders: boolean;
disableSync: boolean;
disableAutoCreate: boolean;
disableFolderNote: boolean;
enableCollapsing: boolean;
position: number;
constructor(path: string, position: number) {
this.type = 'folder';
this.path = path;
this.subFolders = true;
this.disableSync = true;
this.disableAutoCreate = true;
this.disableFolderNote = false;
this.enableCollapsing = false;
this.position = position;
this.string = '';
}
}
export class ExcludePattern {
type: string;
string: string;
path: string;
position: number;
subFolders: boolean;
disableSync: boolean;
disableAutoCreate: boolean;
disableFolderNote: boolean;
enableCollapsing: boolean;
constructor(pattern: string, position: number) {
this.type = 'pattern';
this.string = pattern;
this.position = position;
this.subFolders = false;
this.disableSync = true;
this.disableAutoCreate = true;
this.disableFolderNote = false;
this.enableCollapsing = false;
this.path = '';
}
}
export function getExcludedFolder(plugin: FolderNotesPlugin, path: string) {
const folderName = plugin.getFolderNameFromPathString(path);
const matchedPattern = getExcludedFolderByPattern(plugin, folderName);
if (matchedPattern) { return matchedPattern; }
const excludedFolder = getExcludedFolderByPath(plugin, path);
return excludedFolder;
}
export function getExcludedFolderByPattern(plugin: FolderNotesPlugin, folderName: string) {
return plugin.settings.excludeFolders.filter((s) => s.path === '').find((pattern) => {
if (!pattern.string) { return false; }
if (pattern.string.toLocaleLowerCase().startsWith('{regex}') && pattern.string.slice(7).trim() !== '') {
const match = new RegExp(pattern.string.slice(7).trim()).exec(folderName);
if (match) {
return true;
}
} else if (pattern.string.startsWith('*') && pattern.string.endsWith('*')) {
if (folderName.includes(pattern.string.slice(1, -1))) {
return true;
}
} else if (pattern.string.startsWith('*')) {
if (folderName.endsWith(pattern.string.slice(1))) {
return true;
}
} else if (pattern.string.endsWith('*')) {
if (folderName.startsWith(pattern.string.slice(0, -1))) {
return true;
}
}
});
}
export function getExcludedFolderByPath(plugin: FolderNotesPlugin, path: string) {
return plugin.settings.excludeFolders.find((excludedFolder) => {
if (excludedFolder.path === path) { return true; }
if (!excludedFolder.subFolders) { return false; }
return plugin.getFolderPathFromString(path).startsWith(excludedFolder.path);
});
}
export function addExcludedFolder(plugin: FolderNotesPlugin, excludedFolder: ExcludedFolder) {
plugin.settings.excludeFolders.push(excludedFolder);
plugin.saveSettings();
}
export function deleteExcludedFolder(plugin: FolderNotesPlugin, excludedFolder: ExcludedFolder) {
plugin.settings.excludeFolders = plugin.settings.excludeFolders.filter((folder) => folder.path !== excludedFolder.path || folder.type === 'pattern');
plugin.saveSettings();
}
export function deletePattern(plugin: FolderNotesPlugin, pattern: ExcludePattern) {
plugin.settings.excludeFolders = plugin.settings.excludeFolders.filter((folder) => folder.string !== pattern.string || folder.type === 'folder');
plugin.saveSettings();
}
export function updateExcludedFolder(plugin: FolderNotesPlugin, excludedFolder: ExcludedFolder | ExcludePattern, newExcludeFolder: ExcludedFolder | ExcludePattern) {
plugin.settings.excludeFolders = plugin.settings.excludeFolders.filter((folder) => folder.path !== excludedFolder.path);
addExcludedFolder(plugin, newExcludeFolder);
}
export function updatePattern(plugin: FolderNotesPlugin, pattern: ExcludePattern, newPattern: ExcludePattern) {
plugin.settings.excludeFolders = plugin.settings.excludeFolders.filter((folder) => folder.string !== pattern.string);
addExcludedFolder(plugin, newPattern);
}
export function addExcludePatternListItem(settings: SettingsTab, containerEl: HTMLElement, pattern: ExcludePattern) {
const plugin: FolderNotesPlugin = settings.plugin;
const setting = new Setting(containerEl);
setting.setClass('fn-exclude-folder-list-item');
setting.addSearch((cb) => {
// @ts-ignore
cb.containerEl.addClass('fn-exclude-folder-path');
cb.setPlaceholder('Pattern');
cb.setValue(pattern.string);
cb.onChange((value) => {
if (plugin.settings.excludeFolders.find((folder) => folder.string === value)) { return; }
pattern.string = value;
updatePattern(plugin, pattern, pattern);
});
});
setting.addButton((cb) => {
cb.setIcon('edit');
cb.setTooltip('Edit pattern');
cb.onClick(() => {
new PatternSettings(plugin.app, plugin, pattern).open();
});
});
setting.addButton((cb) => {
cb.setIcon('up-chevron-glyph');
cb.setTooltip('Move up');
cb.onClick(() => {
if (pattern.position === 0) return;
pattern.position -= 1;
updateExcludedFolder(plugin, pattern, pattern);
const oldPattern = plugin.settings.excludeFolders.find((folder) => folder.position === pattern.position);
if (oldPattern) {
oldPattern.position += 1;
updateExcludedFolder(plugin, oldPattern, oldPattern);
}
settings.display();
});
});
setting.addButton((cb) => {
cb.setIcon('down-chevron-glyph');
cb.setTooltip('Move down');
cb.onClick(() => {
if (pattern.position === plugin.settings.excludeFolders.length - 1) return;
pattern.position += 1;
updateExcludedFolder(plugin, pattern, pattern);
const oldPattern = plugin.settings.excludeFolders.find((folder) => folder.position === pattern.position);
if (oldPattern) {
oldPattern.position -= 1;
updateExcludedFolder(plugin, oldPattern, oldPattern);
}
settings.display();
});
});
setting.addButton((cb) => {
cb.setIcon('trash-2');
cb.setTooltip('Delete pattern');
cb.onClick(() => {
deleteExcludedFolder(plugin, pattern);
setting.clear();
setting.settingEl.remove();
});
});
}
export function addExcludeFolderListItem(settings: SettingsTab, containerEl: HTMLElement, excludedFolder: ExcludedFolder) {
const plugin: FolderNotesPlugin = settings.plugin;
const setting = new Setting(containerEl);
setting.setClass('fn-exclude-folder-list-item');
setting.addSearch((cb) => {
new FolderSuggest(
cb.inputEl,
plugin
);
// @ts-ignore
cb.containerEl.addClass('fn-exclude-folder-path');
cb.setPlaceholder('Folder path');
cb.setValue(excludedFolder.path);
cb.onChange((value) => {
if (value.startsWith('{regex}') || value.includes('*')) {
deleteExcludedFolder(plugin, excludedFolder);
const pattern = new ExcludePattern(value, plugin.settings.excludeFolders.length);
addExcludedFolder(plugin, pattern);
addExcludePatternListItem(settings, containerEl, pattern);
setting.clear();
setting.settingEl.remove();
}
if (!plugin.app.vault.getAbstractFileByPath(value)) return;
excludedFolder.path = value;
updateExcludedFolder(plugin, excludedFolder, excludedFolder);
});
});
setting.addButton((cb) => {
cb.setIcon('edit');
cb.setTooltip('Edit folder note');
cb.onClick(() => {
new ExcludedFolderSettings(plugin.app, plugin, excludedFolder).open();
});
});
setting.addButton((cb) => {
cb.setIcon('up-chevron-glyph');
cb.setTooltip('Move up');
cb.onClick(() => {
if (excludedFolder.position === 0) return;
excludedFolder.position -= 1;
updateExcludedFolder(plugin, excludedFolder, excludedFolder);
const oldExcludedFolder = plugin.settings.excludeFolders.find((folder) => folder.position === excludedFolder.position);
if (oldExcludedFolder) {
oldExcludedFolder.position += 1;
updateExcludedFolder(plugin, oldExcludedFolder, oldExcludedFolder);
}
settings.display();
});
});
setting.addButton((cb) => {
cb.setIcon('down-chevron-glyph');
cb.setTooltip('Move down');
cb.onClick(() => {
if (excludedFolder.position === plugin.settings.excludeFolders.length - 1) return;
excludedFolder.position += 1;
updateExcludedFolder(plugin, excludedFolder, excludedFolder);
const oldExcludedFolder = plugin.settings.excludeFolders.find((folder) => folder.position === excludedFolder.position);
if (oldExcludedFolder) {
oldExcludedFolder.position -= 1;
updateExcludedFolder(plugin, oldExcludedFolder, oldExcludedFolder);
}
settings.display();
});
});
setting.addButton((cb) => {
cb.setIcon('trash-2');
cb.setTooltip('Delete excluded folder');
cb.onClick(() => {
deleteExcludedFolder(plugin, excludedFolder);
setting.clear();
setting.settingEl.remove();
});
});
}

View file

@ -1,10 +1,11 @@
import { Plugin, TFile, TFolder, TAbstractFile } from 'obsidian';
import { DEFAULT_SETTINGS, ExcludedFolder, FolderNotesSettings, SettingsTab } from './settings';
import { DEFAULT_SETTINGS, FolderNotesSettings, SettingsTab } from './settings';
import { Commands } from './commands';
import { FileExplorerWorkspaceLeaf } from './globals';
import { handleViewHeaderClick, handleFolderClick } from './events/handleClick';
import { handleFileRename, handleFolderRename } from './events/handleRename';
import { createFolderNote, extractFolderName, getFolderNote, getFolder } from './folderNoteFunctions';
import { getExcludedFolder } from './excludedFolder';
export default class FolderNotesPlugin extends Plugin {
observer: MutationObserver;
settings: FolderNotesSettings;
@ -81,8 +82,7 @@ export default class FolderNotesPlugin extends Plugin {
if (!this.app.workspace.layoutReady) return;
if (!this.settings.autoCreate) return;
if (!(file instanceof TFolder)) return;
const excludedFolder = this.getExcludedFolderByPath(file.path);
const excludedFolder = getExcludedFolder(this, file.path);
if (excludedFolder?.disableAutoCreate) return;
const folderNote = getFolderNote(this, file.path);
if (folderNote) return;
@ -164,14 +164,6 @@ export default class FolderNotesPlugin extends Plugin {
return this.getFolderPathFromString(this.getFolderPathFromString(path));
}
getExcludedFolderByPath(path: string): ExcludedFolder | undefined {
return this.settings.excludeFolders.find((excludedFolder) => {
if (excludedFolder.path === path) { return true; }
if (!excludedFolder.subFolders) { return false; }
return this.getFolderPathFromString(path).startsWith(excludedFolder.path);
});
}
getFileExplorer() {
return this.app.workspace.getLeavesOfType('file-explorer')[0] as FileExplorerWorkspaceLeaf;
}
@ -231,7 +223,7 @@ export default class FolderNotesPlugin extends Plugin {
return;
}
const excludedFolder = this.getExcludedFolderByPath(file.path);
const excludedFolder = getExcludedFolder(this, file.path);
// cleanup after ourselves
// Incase settings have changed
if (excludedFolder?.disableFolderNote) {

View file

@ -1,6 +1,6 @@
import { App, Modal, Setting } from 'obsidian';
import FolderNotesPlugin from '../main';
import { ExcludedFolder } from '../settings';
import { ExcludedFolder } from 'src/excludedFolder';
export default class ExcludedFolderSettings extends Modal {
plugin: FolderNotesPlugin;
app: App;

View file

@ -0,0 +1,78 @@
import { App, Modal, Setting } from 'obsidian';
import FolderNotesPlugin from '../main';
import { ExcludePattern } from 'src/excludedFolder';
export default class PatternSettings extends Modal {
plugin: FolderNotesPlugin;
app: App;
pattern: ExcludePattern;
constructor(app: App, plugin: FolderNotesPlugin, pattern: ExcludePattern) {
super(app);
this.plugin = plugin;
this.app = app;
this.pattern = pattern;
}
onOpen() {
this.display();
}
display() {
const { contentEl } = this;
contentEl.empty();
contentEl.createEl('h2', { text: 'Pattern settings' });
new Setting(contentEl)
.setName('Disable folder name sync')
.setDesc('Choose if the folder note should be renamed when the folder name is changed')
.addToggle((toggle) =>
toggle
.setValue(this.pattern.disableSync)
.onChange(async (value) => {
this.pattern.disableSync = value;
await this.plugin.saveSettings();
})
);
new Setting(contentEl)
.setName('Disable auto creation of folder notes in this folder')
.setDesc('Choose if a folder note should be created when a new folder is created')
.addToggle((toggle) =>
toggle
.setValue(this.pattern.disableAutoCreate)
.onChange(async (value) => {
this.pattern.disableAutoCreate = value;
await this.plugin.saveSettings();
})
);
new Setting(contentEl)
.setName('Disable open folder note')
.setDesc('Choose if the folder note should be opened when the folder is opened')
.addToggle((toggle) =>
toggle
.setValue(this.pattern.disableFolderNote)
.onChange(async (value) => {
this.pattern.disableFolderNote = value;
await this.plugin.saveSettings();
this.display();
})
);
if (!this.pattern.disableFolderNote) {
new Setting(contentEl)
.setName('Collapse folder when opening folder note')
.setDesc('Choose if the folder should be collapsed when the folder note is opened')
.addToggle((toggle) =>
toggle
.setValue(this.pattern.enableCollapsing)
.onChange(async (value) => {
this.pattern.enableCollapsing = value;
await this.plugin.saveSettings();
})
);
}
}
onClose() {
const { contentEl } = this;
contentEl.empty();
}
}

View file

@ -1,9 +1,8 @@
import { App, Notice, Platform, PluginSettingTab, Setting, TFile, TFolder } from 'obsidian';
import FolderNotesPlugin from './main';
import { FolderSuggest } from './suggesters/FolderSuggester';
import ExcludedFolderSettings from './modals/exludeFolderSettings';
import { TemplateSuggest } from './suggesters/templateSuggester';
import { extractFolderName, getFolderNote } from './folderNoteFunctions';
import { addExcludeFolderListItem, ExcludedFolder, addExcludedFolder, ExcludePattern, addExcludePatternListItem } from './excludedFolder';
// import ConfirmationModal from "./modals/confirmCreation";
export interface FolderNotesSettings {
syncFolderName: boolean;
@ -13,7 +12,7 @@ export interface FolderNotesSettings {
templatePath: string;
autoCreate: boolean;
enableCollapsing: boolean;
excludeFolders: ExcludedFolder[];
excludeFolders: (ExcludePattern | ExcludedFolder)[];
showDeleteConfirmation: boolean;
underlineFolder: boolean;
allowWhitespaceCollapsing: boolean;
@ -354,9 +353,21 @@ export class SettingsTab extends PluginSettingTab {
*/
new Setting(containerEl)
const manageExcluded = new Setting(containerEl)
.setHeading()
.setName('Manage excluded folders');
const desc3 = document.createDocumentFragment();
desc3.append(
'Add {regex} at the beginning of the folder name to use a regex pattern.',
desc.createEl('br'),
'Use * before and after to exclude folders that include the name between the *s.',
desc.createEl('br'),
'Use * before the folder name to exclude folders that end with the folder name.',
desc.createEl('br'),
'Use * after the folder name to exclude folders that start with the folder name.',
);
manageExcluded.setDesc(desc3);
new Setting(containerEl)
.setName('Add excluded folder')
.addButton((cb) => {
@ -365,97 +376,19 @@ export class SettingsTab extends PluginSettingTab {
cb.setTooltip('Add excluded folder');
cb.onClick(() => {
const excludedFolder = new ExcludedFolder('', this.plugin.settings.excludeFolders.length);
this.addExcludeFolderListItem(containerEl, excludedFolder);
this.addExcludedFolder(excludedFolder);
addExcludeFolderListItem(this, containerEl, excludedFolder);
addExcludedFolder(this.plugin, excludedFolder);
this.display();
});
});
this.plugin.settings.excludeFolders.sort((a, b) => a.position - b.position).forEach((excludedFolder) => {
this.addExcludeFolderListItem(containerEl, excludedFolder);
if (excludedFolder.string?.trim() !== '' && excludedFolder.path?.trim() === '') {
addExcludePatternListItem(this, containerEl, excludedFolder);
} else {
addExcludeFolderListItem(this, containerEl, excludedFolder);
}
});
}
addExcludeFolderListItem(containerEl: HTMLElement, excludedFolder: ExcludedFolder) {
const setting = new Setting(containerEl);
setting.setClass('fn-exclude-folder-list-item');
setting.addSearch((cb) => {
new FolderSuggest(
cb.inputEl,
this.plugin
);
// @ts-ignore
cb.containerEl.addClass('fn-exclude-folder-path');
cb.setPlaceholder('Folder path');
cb.setValue(excludedFolder.path);
cb.onChange((value) => {
if (!this.app.vault.getAbstractFileByPath(value)) return;
excludedFolder.path = value;
this.updateExcludedFolder(excludedFolder, excludedFolder);
});
});
setting.addButton((cb) => {
cb.setIcon('edit');
cb.setTooltip('Edit folder note');
cb.onClick(() => {
new ExcludedFolderSettings(this.app, this.plugin, excludedFolder).open();
});
});
setting.addButton((cb) => {
cb.setIcon('up-chevron-glyph');
cb.setTooltip('Move up');
cb.onClick(() => {
if (excludedFolder.position === 0) return;
excludedFolder.position -= 1;
this.updateExcludedFolder(excludedFolder, excludedFolder);
const oldExcludedFolder = this.plugin.settings.excludeFolders.find((folder) => folder.position === excludedFolder.position);
if (oldExcludedFolder) {
oldExcludedFolder.position += 1;
this.updateExcludedFolder(oldExcludedFolder, oldExcludedFolder);
}
this.display();
});
});
setting.addButton((cb) => {
cb.setIcon('down-chevron-glyph');
cb.setTooltip('Move down');
cb.onClick(() => {
if (excludedFolder.position === this.plugin.settings.excludeFolders.length - 1) return;
excludedFolder.position += 1;
this.updateExcludedFolder(excludedFolder, excludedFolder);
const oldExcludedFolder = this.plugin.settings.excludeFolders.find((folder) => folder.position === excludedFolder.position);
if (oldExcludedFolder) {
oldExcludedFolder.position -= 1;
this.updateExcludedFolder(oldExcludedFolder, oldExcludedFolder);
}
this.display();
});
});
setting.addButton((cb) => {
cb.setIcon('trash-2');
cb.setTooltip('Delete excluded folder');
cb.onClick(() => {
this.deleteExcludedFolder(excludedFolder);
setting.clear();
setting.settingEl.remove();
});
});
}
addExcludedFolder(excludedFolder: ExcludedFolder) {
this.plugin.settings.excludeFolders.push(excludedFolder);
this.plugin.saveSettings();
}
deleteExcludedFolder(excludedFolder: ExcludedFolder) {
this.plugin.settings.excludeFolders = this.plugin.settings.excludeFolders.filter((folder) => folder.path !== excludedFolder.path);
this.plugin.saveSettings();
}
updateExcludedFolder(excludedFolder: ExcludedFolder, newExcludeFolder: ExcludedFolder) {
this.plugin.settings.excludeFolders = this.plugin.settings.excludeFolders.filter((folder) => folder.path !== excludedFolder.path);
this.addExcludedFolder(newExcludeFolder);
}
updateFolderNotes(oldTemplate: string, newTemplate: string) {
this.plugin.settings.folderNoteName = newTemplate;
@ -516,21 +449,3 @@ export class SettingsTab extends PluginSettingTab {
}
}
export class ExcludedFolder {
path: string;
subFolders: boolean;
disableSync: boolean;
disableAutoCreate: boolean;
disableFolderNote: boolean;
enableCollapsing: boolean;
position: number;
constructor(path: string, position: number) {
this.path = path;
this.subFolders = true;
this.disableSync = true;
this.disableAutoCreate = true;
this.disableFolderNote = false;
this.enableCollapsing = false;
this.position = position;
}
}