Excluded folders

This commit is contained in:
Lost Paul 2025-08-10 12:21:16 +02:00
parent 8adf6186ac
commit 113cf5f40c
12 changed files with 493 additions and 260 deletions

View file

@ -25,6 +25,7 @@ export class ExcludedFolder {
this.disableFolderNote = plugin.settings.excludeFolderDefaultSettings.disableFolderNote;
this.enableCollapsing = plugin.settings.excludeFolderDefaultSettings.enableCollapsing;
this.position = position;
// eslint-disable-next-line max-len
this.excludeFromFolderOverview = plugin.settings.excludeFolderDefaultSettings.excludeFromFolderOverview;
this.string = '';
this.hideInSettings = false;

View file

@ -15,7 +15,12 @@ export class ExcludePattern {
detached: boolean;
detachedFilePath?: string;
showFolderNote: boolean;
constructor(pattern: string, position: number, id: string | undefined, plugin: FolderNotesPlugin) {
constructor(
pattern: string,
position: number,
id: string | undefined,
plugin: FolderNotesPlugin,
) {
this.type = 'pattern';
this.id = id || crypto.randomUUID();
this.string = pattern;
@ -25,6 +30,7 @@ export class ExcludePattern {
this.disableAutoCreate = plugin.settings.excludePatternDefaultSettings.disableAutoCreate;
this.disableFolderNote = plugin.settings.excludePatternDefaultSettings.disableFolderNote;
this.enableCollapsing = plugin.settings.excludePatternDefaultSettings.enableCollapsing;
// eslint-disable-next-line max-len
this.excludeFromFolderOverview = plugin.settings.excludePatternDefaultSettings.excludeFromFolderOverview;
this.path = '';
this.hideInSettings = false;

View file

@ -13,7 +13,12 @@ export class WhitelistedPattern {
showInFolderOverview: boolean;
hideInFileExplorer: boolean;
hideInSettings: boolean;
constructor(pattern: string, position: number, id: string | undefined, plugin: FolderNotesPlugin) {
constructor(
pattern: string,
position: number,
id: string | undefined,
plugin: FolderNotesPlugin,
) {
this.type = 'pattern';
this.id = id || crypto.randomUUID();
this.subFolders = plugin.settings.excludePatternDefaultSettings.subFolders;

View file

@ -6,24 +6,34 @@ import { Platform, Setting } from 'obsidian';
import { FolderSuggest } from '../../suggesters/FolderSuggester';
import type { SettingsTab } from '../../settings/SettingsTab';
import ExcludedFolderSettings from '../modals/ExcludeFolderSettings';
import { updatePattern, getExcludedFoldersByPattern, addExcludePatternListItem } from './patternFunctions';
import {
updatePattern,
getExcludedFoldersByPattern,
addExcludePatternListItem,
} from './patternFunctions';
import { getWhitelistedFolder } from './whitelistFolderFunctions';
import type { WhitelistedFolder } from '../WhitelistFolder';
import type { WhitelistedPattern } from '../WhitelistPattern';
export function getExcludedFolder(plugin: FolderNotesPlugin, path: string, includeDetached: boolean, pathOnly?: boolean, ignoreWhitelist?: boolean) {
let excludedFolder = {} as ExcludedFolder | ExcludePattern | undefined;
const whitelistedFolder = getWhitelistedFolder(plugin, path) as WhitelistedFolder | WhitelistedPattern | undefined;
function combineExcluded(
plugin: FolderNotesPlugin,
path: string,
includeDetached: boolean,
pathOnly?: boolean,
): Array<ExcludedFolder | ExcludePattern> {
const folderName = getFolderNameFromPathString(path);
let matchedPatterns = getExcludedFoldersByPattern(plugin, folderName);
const excludedFolders = getExcludedFoldersByPath(plugin, path);
if (pathOnly) { matchedPatterns = []; }
let combinedExcludedFolders = [...matchedPatterns, ...excludedFolders];
if (!includeDetached) {
combinedExcludedFolders = combinedExcludedFolders.filter((f) => !f.detached);
}
const matchedPatterns = pathOnly ? [] : getExcludedFoldersByPattern(plugin, folderName);
const excludedByPath = getExcludedFoldersByPath(plugin, path);
let combined = [...matchedPatterns, ...excludedByPath];
if (!includeDetached) combined = combined.filter((f) => !f.detached);
return combined;
}
function aggregateFlags(
combinedExcludedFolders: Array<ExcludedFolder | ExcludePattern>,
): Partial<ExcludedFolder> | undefined {
if (combinedExcludedFolders.length === 0) return undefined;
const result: Partial<ExcludedFolder> = {};
const propertiesToCopy: (keyof ExcludedFolder)[] = [
'disableAutoCreate',
'disableFolderNote',
@ -35,32 +45,46 @@ export function getExcludedFolder(plugin: FolderNotesPlugin, path: string, inclu
'id',
'showFolderNote',
];
if (combinedExcludedFolders.length > 0) {
for (const matchedFolder of combinedExcludedFolders) {
propertiesToCopy.forEach((property) => {
if (matchedFolder[property] === true) {
(excludedFolder as any)[property] = true;
} else if (!matchedFolder[property]) {
(excludedFolder as any)[property] = false;
}
});
for (const matchedFolder of combinedExcludedFolders) {
for (const property of propertiesToCopy) {
const value = (matchedFolder as Partial<ExcludedFolder>)[property];
if (value === true) {
(result as Partial<ExcludedFolder>)[property] = true as never;
} else if (!value) {
(result as Partial<ExcludedFolder>)[property] = false as never;
}
}
} else {
excludedFolder = undefined;
}
return result;
}
if (excludedFolder?.detached) { ignoreWhitelist = true; }
function applyWhitelistOverrides(
excluded: Partial<ExcludedFolder>,
whitelisted: WhitelistedFolder | WhitelistedPattern,
): Partial<ExcludedFolder> {
const out: Partial<ExcludedFolder> = { ...excluded };
if (out.disableAutoCreate !== undefined) {
out.disableAutoCreate = !whitelisted.enableAutoCreate;
}
if (out.disableFolderNote !== undefined) {
out.disableFolderNote = !whitelisted.enableFolderNote;
}
if (out.disableSync !== undefined) {
out.disableSync = !whitelisted.enableSync;
}
out.enableCollapsing = !whitelisted.disableCollapsing;
if (out.excludeFromFolderOverview !== undefined) {
out.excludeFromFolderOverview = !whitelisted.showInFolderOverview;
}
out.showFolderNote = !whitelisted.hideInFileExplorer;
return out;
}
if (whitelistedFolder && excludedFolder && !ignoreWhitelist) {
excludedFolder.disableAutoCreate ? excludedFolder.disableAutoCreate = !whitelistedFolder.enableAutoCreate : '';
excludedFolder.disableFolderNote ? excludedFolder.disableFolderNote = !whitelistedFolder.enableFolderNote : '';
excludedFolder.disableSync ? excludedFolder.disableSync = !whitelistedFolder.enableSync : '';
excludedFolder.enableCollapsing = !whitelistedFolder.disableCollapsing;
excludedFolder.excludeFromFolderOverview ? excludedFolder.excludeFromFolderOverview = !whitelistedFolder.showInFolderOverview : '';
excludedFolder.showFolderNote = !whitelistedFolder.hideInFileExplorer;
} else if (excludedFolder && Object.keys(excludedFolder).length === 0) {
excludedFolder = {
function defaultExcludedIfEmpty(
value: Partial<ExcludedFolder> | undefined,
): ExcludedFolder | undefined {
if (value && Object.keys(value).length === 0) {
return {
type: 'folder',
id: '',
path: '',
@ -77,23 +101,54 @@ export function getExcludedFolder(plugin: FolderNotesPlugin, path: string, inclu
showFolderNote: false,
};
}
return excludedFolder;
return value as ExcludedFolder | undefined;
}
export function getDetachedFolder(plugin: FolderNotesPlugin, path: string) {
export function getExcludedFolder(
plugin: FolderNotesPlugin,
path: string,
includeDetached: boolean,
pathOnly?: boolean,
ignoreWhitelist?: boolean,
): ExcludedFolder | ExcludePattern | undefined {
const combined = combineExcluded(plugin, path, includeDetached, pathOnly);
let excluded = aggregateFlags(combined);
const whitelist = getWhitelistedFolder(
plugin,
path,
) as WhitelistedFolder | WhitelistedPattern | undefined;
let skipWhitelist = ignoreWhitelist ?? false;
if (excluded?.detached) skipWhitelist = true;
if (whitelist && excluded && !skipWhitelist) {
excluded = applyWhitelistOverrides(excluded, whitelist);
}
return defaultExcludedIfEmpty(excluded) as ExcludedFolder | ExcludePattern | undefined;
}
export function getDetachedFolder(
plugin: FolderNotesPlugin,
path: string,
): ExcludedFolder | undefined {
return plugin.settings.excludeFolders.find((f) => f.path === path && f.detached);
}
export function getExcludedFolderByPath(plugin: FolderNotesPlugin, path: string) {
export function getExcludedFolderByPath(
plugin: FolderNotesPlugin,
path: string,
): ExcludedFolder | undefined {
return plugin.settings.excludeFolders.find((excludedFolder) => {
if (path.trim() === '' || !excludedFolder.path) { return false; }
if (excludedFolder.path === path) { return true; }
if (!excludedFolder.subFolders) { return false; }
const excludedFolderPath = excludedFolder.path.includes('/') ? excludedFolder.path : excludedFolder.path + '/';
const excludedFolderPath = excludedFolder.path.includes('/')
? excludedFolder.path
: `${excludedFolder.path}/`;
let folderPath = getFolderPathFromString(path);
folderPath = folderPath.includes('/') ? folderPath : folderPath + '/';
folderPath = folderPath.includes('/') ? folderPath : `${folderPath}/`;
if (folderPath.includes('/') || folderPath.includes('\\')) {
return folderPath.startsWith(excludedFolderPath) || folderPath === excludedFolderPath;
@ -103,14 +158,19 @@ export function getExcludedFolderByPath(plugin: FolderNotesPlugin, path: string)
});
}
export function getExcludedFoldersByPath(plugin: FolderNotesPlugin, path: string) {
export function getExcludedFoldersByPath(
plugin: FolderNotesPlugin,
path: string,
): ExcludedFolder[] {
return plugin.settings.excludeFolders.filter((excludedFolder) => {
if (path.trim() === '' || !excludedFolder.path) { return false; }
if (excludedFolder.path === path) { return true; }
if (!excludedFolder.subFolders) { return false; }
const excludedFolderPath = excludedFolder.path.includes('/') ? excludedFolder.path : excludedFolder.path + '/';
const excludedFolderPath = excludedFolder.path.includes('/')
? excludedFolder.path
: `${excludedFolder.path}/`;
let folderPath = getFolderPathFromString(path);
folderPath = folderPath.includes('/') ? folderPath : folderPath + '/';
folderPath = folderPath.includes('/') ? folderPath : `${folderPath}/`;
if (folderPath.includes('/') || folderPath.includes('\\')) {
return folderPath.startsWith(excludedFolderPath) || folderPath === excludedFolderPath;
@ -120,32 +180,52 @@ export function getExcludedFoldersByPath(plugin: FolderNotesPlugin, path: string
});
}
export function addExcludedFolder(plugin: FolderNotesPlugin, excludedFolder: ExcludedFolder, reloadStyles = true) {
export function addExcludedFolder(
plugin: FolderNotesPlugin,
excludedFolder: ExcludedFolder,
reloadStyles = true,
): void {
plugin.settings.excludeFolders.push(excludedFolder);
plugin.saveSettings(reloadStyles);
void plugin.saveSettings(reloadStyles);
}
export async function deleteExcludedFolder(plugin: FolderNotesPlugin, excludedFolder: ExcludedFolder) {
plugin.settings.excludeFolders = plugin.settings.excludeFolders.filter((folder) => folder.id !== excludedFolder.id || folder.type === 'pattern');
plugin.saveSettings(true);
export async function deleteExcludedFolder(
plugin: FolderNotesPlugin,
excludedFolder: ExcludedFolder,
): Promise<void> {
plugin.settings.excludeFolders = plugin.settings.excludeFolders.filter(
(folder) => folder.id !== excludedFolder.id || folder.type === 'pattern',
);
await plugin.saveSettings(true);
resyncArray(plugin);
}
export function updateExcludedFolder(plugin: FolderNotesPlugin, excludedFolder: ExcludePattern, newExcludeFolder: ExcludePattern) {
plugin.settings.excludeFolders = plugin.settings.excludeFolders.filter((folder) => folder.id !== excludedFolder.id);
export function updateExcludedFolder(
plugin: FolderNotesPlugin,
excludedFolder: ExcludePattern,
newExcludeFolder: ExcludePattern,
): void {
plugin.settings.excludeFolders = plugin.settings.excludeFolders.filter(
(folder) => folder.id !== excludedFolder.id,
);
addExcludedFolder(plugin, newExcludeFolder);
}
export function resyncArray(plugin: FolderNotesPlugin) {
plugin.settings.excludeFolders = plugin.settings.excludeFolders.sort((a, b) => a.position - b.position);
export function resyncArray(plugin: FolderNotesPlugin): void {
plugin.settings.excludeFolders = plugin.settings.excludeFolders.sort(
(a, b) => a.position - b.position,
);
plugin.settings.excludeFolders.forEach((folder, index) => {
folder.position = index;
});
plugin.saveSettings();
void plugin.saveSettings();
}
export function addExcludeFolderListItem(settings: SettingsTab, containerEl: HTMLElement, excludedFolder: ExcludedFolder) {
export function addExcludeFolderListItem(
settings: SettingsTab,
containerEl: HTMLElement,
excludedFolder: ExcludedFolder,
): void {
const { plugin } = settings;
const setting = new Setting(containerEl);
setting.setClass('fn-exclude-folder-list');
@ -155,14 +235,19 @@ export function addExcludeFolderListItem(settings: SettingsTab, containerEl: HTM
plugin,
false,
);
// @ts-ignore
// @ts-expect-error Obsidian's public types don't include this property
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, undefined, plugin);
const pattern = new ExcludePattern(
value,
plugin.settings.excludeFolders.length,
undefined,
plugin,
);
addExcludedFolder(plugin, pattern);
addExcludePatternListItem(settings, containerEl, pattern);
setting.clear();
@ -190,7 +275,9 @@ export function addExcludeFolderListItem(settings: SettingsTab, containerEl: HTM
if (excludedFolder.position === 0) { return; }
excludedFolder.position -= 1;
updateExcludedFolder(plugin, excludedFolder, excludedFolder);
const oldExcludedFolder = plugin.settings.excludeFolders.find((folder) => folder.position === excludedFolder.position);
const oldExcludedFolder = plugin.settings.excludeFolders.find(
(folder) => folder.position === excludedFolder.position,
);
if (oldExcludedFolder) {
oldExcludedFolder.position += 1;
if (oldExcludedFolder.type === 'pattern') {
@ -213,7 +300,9 @@ export function addExcludeFolderListItem(settings: SettingsTab, containerEl: HTM
excludedFolder.position += 1;
updateExcludedFolder(plugin, excludedFolder, excludedFolder);
const oldExcludedFolder = plugin.settings.excludeFolders.find((folder) => folder.position === excludedFolder.position);
const oldExcludedFolder = plugin.settings.excludeFolders.find(
(folder) => folder.position === excludedFolder.position,
);
if (oldExcludedFolder) {
oldExcludedFolder.position -= 1;
if (oldExcludedFolder.type === 'pattern') {

View file

@ -5,79 +5,97 @@ import type { SettingsTab } from '../../settings/SettingsTab';
import { addExcludedFolder, resyncArray, updateExcludedFolder } from './folderFunctions';
import PatternSettings from '../modals/PatternSettings';
export function updatePattern(plugin: FolderNotesPlugin, pattern: ExcludePattern, newPattern: ExcludePattern) {
plugin.settings.excludeFolders = plugin.settings.excludeFolders.filter((folder) => folder.id !== pattern.id);
const REGEX_PREFIX = '{regex}';
const STAR = '*';
const INDEX_START = 0;
const SLICE_START_ONE = 1;
const SLICE_EXCLUDE_LAST = -1;
function matchesPatternSpec(raw: string | undefined, folderName: string): boolean {
if (!raw) return false;
const string = raw.trim();
const isRegex = string.startsWith(REGEX_PREFIX);
const hasStartStar = string.startsWith(STAR);
const hasEndStar = string.endsWith(STAR);
if (!isRegex && !(hasStartStar || hasEndStar)) return false;
if (isRegex) {
const body = string.replace(REGEX_PREFIX, '').trim();
if (body === '') return false;
try {
return new RegExp(body).test(folderName);
} catch {
return false;
}
}
if (hasStartStar && hasEndStar) {
const inner = string.slice(SLICE_START_ONE, SLICE_EXCLUDE_LAST);
return folderName.includes(inner);
}
if (hasStartStar) {
const suffix = string.slice(SLICE_START_ONE);
return folderName.endsWith(suffix);
}
if (hasEndStar) {
const prefix = string.slice(INDEX_START, SLICE_EXCLUDE_LAST);
return folderName.startsWith(prefix);
}
return false;
}
export function updatePattern(
plugin: FolderNotesPlugin,
pattern: ExcludePattern,
newPattern: ExcludePattern,
): void {
plugin.settings.excludeFolders = plugin.settings.excludeFolders.filter(
(folder) => folder.id !== pattern.id,
);
addExcludedFolder(plugin, newPattern);
}
export function deletePattern(plugin: FolderNotesPlugin, pattern: ExcludePattern) {
plugin.settings.excludeFolders = plugin.settings.excludeFolders.filter((folder) => folder.id !== pattern.id || folder.type === 'folder');
plugin.saveSettings(true);
export async function deletePattern(
plugin: FolderNotesPlugin,
pattern: ExcludePattern,
): Promise<void> {
plugin.settings.excludeFolders = plugin.settings.excludeFolders.filter(
(folder) => folder.id !== pattern.id || folder.type === 'folder',
);
await plugin.saveSettings(true);
resyncArray(plugin);
}
export function getExcludedFoldersByPattern(plugin: FolderNotesPlugin, folderName: string): ExcludePattern[] {
return plugin.settings.excludeFolders.filter((s) => s.type === 'pattern').filter((pattern) => {
if (!pattern.string) { return false; }
const string = pattern.string.trim();
if (!string.startsWith('{regex}') && !(string.startsWith('*') || string.endsWith('*'))) { return false; }
const regex = string.replace('{regex}', '').trim();
if (string.startsWith('{regex}') && regex === '') { return false; }
if (regex !== undefined && string.startsWith('{regex}')) {
const match = new RegExp(regex).exec(folderName);
if (match) {
return true;
}
} else if (string.startsWith('*') && string.endsWith('*')) {
if (folderName.includes(string.slice(1, -1))) {
return true;
}
} else if (string.startsWith('*')) {
if (folderName.endsWith(string.slice(1))) {
return true;
}
} else if (string.endsWith('*')) {
if (folderName.startsWith(string.slice(0, -1))) {
return true;
}
}
});
export function getExcludedFoldersByPattern(
plugin: FolderNotesPlugin,
folderName: string,
): ExcludePattern[] {
return plugin.settings.excludeFolders
.filter((s) => s.type === 'pattern')
.filter((pattern) => matchesPatternSpec(pattern.string, folderName)) as ExcludePattern[];
}
export function getExcludedFolderByPattern(plugin: FolderNotesPlugin, folderName: string): ExcludePattern | undefined{
return plugin.settings.excludeFolders.filter((s) => s.type === 'pattern').find((pattern) => {
if (!pattern.string) { return false; }
const string = pattern.string.trim();
if (!string.startsWith('{regex}') && !(string.startsWith('*') || string.endsWith('*'))) { return false; }
const regex = string.replace('{regex}', '').trim();
if (string.startsWith('{regex}') && regex === '') { return false; }
if (regex !== undefined && string.startsWith('{regex}')) {
const match = new RegExp(regex).exec(folderName);
if (match) {
return true;
}
} else if (string.startsWith('*') && string.endsWith('*')) {
if (folderName.includes(string.slice(1, -1))) {
return true;
}
} else if (string.startsWith('*')) {
if (folderName.endsWith(string.slice(1))) {
return true;
}
} else if (string.endsWith('*')) {
if (folderName.startsWith(string.slice(0, -1))) {
return true;
}
}
});
export function getExcludedFolderByPattern(
plugin: FolderNotesPlugin,
folderName: string,
): ExcludePattern | undefined {
return (
plugin.settings.excludeFolders
.filter((s) => s.type === 'pattern')
.find((pattern) => matchesPatternSpec(pattern.string, folderName))
) as ExcludePattern | undefined;
}
export function addExcludePatternListItem(settings: SettingsTab, containerEl: HTMLElement, pattern: ExcludePattern) {
export function addExcludePatternListItem(
settings: SettingsTab,
containerEl: HTMLElement,
pattern: ExcludePattern,
): void {
const { plugin } = settings;
const setting = new Setting(containerEl);
setting.setClass('fn-exclude-folder-list');
setting.addSearch((cb) => {
// @ts-ignore
// @ts-expect-error Obsidian's public types don't include containerEl on this control
cb.containerEl.addClass('fn-exclude-folder-path');
cb.setPlaceholder('Pattern');
cb.setValue(pattern.string);
@ -102,11 +120,18 @@ export function addExcludePatternListItem(settings: SettingsTab, containerEl: HT
if (pattern.position === 0) { return; }
pattern.position -= 1;
updatePattern(plugin, pattern, pattern);
const oldPattern = plugin.settings.excludeFolders.find((folder) => folder.position === pattern.position);
const oldPattern = plugin.settings.excludeFolders.find(
(folder) => folder.position === pattern.position,
);
if (oldPattern) {
oldPattern.position += 1;
if (oldPattern.type === 'pattern') {
updatePattern(plugin, oldPattern, oldPattern);
const pat = oldPattern as ExcludePattern;
updatePattern(
plugin,
pat,
pat,
);
} else {
updateExcludedFolder(plugin, oldPattern, oldPattern);
}
@ -125,11 +150,18 @@ export function addExcludePatternListItem(settings: SettingsTab, containerEl: HT
pattern.position += 1;
updatePattern(plugin, pattern, pattern);
const oldPattern = plugin.settings.excludeFolders.find((folder) => folder.position === pattern.position);
const oldPattern = plugin.settings.excludeFolders.find(
(folder) => folder.position === pattern.position,
);
if (oldPattern) {
oldPattern.position -= 1;
if (oldPattern.type === 'pattern') {
updatePattern(plugin, oldPattern, oldPattern);
const pat = oldPattern as ExcludePattern;
updatePattern(
plugin,
pat,
pat,
);
} else {
updateExcludedFolder(plugin, oldPattern, oldPattern);
}
@ -143,7 +175,7 @@ export function addExcludePatternListItem(settings: SettingsTab, containerEl: HT
cb.setIcon('trash-2');
cb.setTooltip('Delete pattern');
cb.onClick(() => {
deletePattern(plugin, pattern);
void deletePattern(plugin, pattern);
setting.clear();
setting.settingEl.remove();
});

View file

@ -2,15 +2,21 @@ import type FolderNotesPlugin from '../../main';
import { getFolderNameFromPathString, getFolderPathFromString } from '../../functions/utils';
import type { WhitelistedFolder } from '../WhitelistFolder';
import { WhitelistedPattern } from '../WhitelistPattern';
import { Setting, Platform, ButtonComponent } from 'obsidian';
import { Setting, ButtonComponent } from 'obsidian';
import { FolderSuggest } from '../../suggesters/FolderSuggester';
import type { SettingsTab } from '../../settings/SettingsTab';
import WhitelistFolderSettings from '../modals/WhitelistFolderSettings';
import { updateWhitelistedPattern, getWhitelistedFoldersByPattern, addWhitelistedPatternListItem } from './whitelistPatternFunctions';
Platform.isMobileApp;
import {
updateWhitelistedPattern,
getWhitelistedFoldersByPattern,
addWhitelistedPatternListItem,
} from './whitelistPatternFunctions';
export function getWhitelistedFolder(plugin: FolderNotesPlugin, path: string) {
let whitelistedFolder = {} as WhitelistedFolder | WhitelistedPattern | undefined;
export function getWhitelistedFolder(
plugin: FolderNotesPlugin,
path: string,
): WhitelistedFolder | WhitelistedPattern | undefined {
let whitelistedFolder: Partial<WhitelistedFolder> | undefined = {};
const folderName = getFolderNameFromPathString(path);
const matchedPatterns = getWhitelistedFoldersByPattern(plugin, folderName);
const whitelistedFolders = getWhitelistedFoldersByPath(plugin, path);
@ -25,21 +31,30 @@ export function getWhitelistedFolder(plugin: FolderNotesPlugin, path: string) {
if (combinedWhitelistedFolders.length > 0) {
for (const matchedFolder of combinedWhitelistedFolders) {
propertiesToCopy.forEach((property) => {
if (matchedFolder[property] === true) {
(whitelistedFolder as any)[property] = true;
} else if (!matchedFolder[property]) {
(whitelistedFolder as any)[property] = false;
const value = (matchedFolder as Partial<WhitelistedFolder>)[property];
if (value === true) {
(whitelistedFolder as Partial<WhitelistedFolder>)[property] = true as never;
} else if (!value) {
(whitelistedFolder as Partial<WhitelistedFolder>)[property] = false as never;
}
});
}
}
if ((whitelistedFolder instanceof Object) && Object.keys(whitelistedFolder).length === 0) { whitelistedFolder = undefined; }
if (
whitelistedFolder
&& Object.keys(whitelistedFolder).length === 0
) {
whitelistedFolder = undefined;
}
return whitelistedFolder;
return whitelistedFolder as WhitelistedFolder | WhitelistedPattern | undefined;
}
export function getWhitelistedFolderByPath(plugin: FolderNotesPlugin, path: string) {
export function getWhitelistedFolderByPath(
plugin: FolderNotesPlugin,
path: string,
): WhitelistedFolder | WhitelistedPattern | undefined {
return plugin.settings.whitelistFolders.find((whitelistedFolder) => {
if (whitelistedFolder.path === path) { return true; }
if (!whitelistedFolder.subFolders) { return false; }
@ -47,7 +62,10 @@ export function getWhitelistedFolderByPath(plugin: FolderNotesPlugin, path: stri
});
}
export function getWhitelistedFoldersByPath(plugin: FolderNotesPlugin, path: string) {
export function getWhitelistedFoldersByPath(
plugin: FolderNotesPlugin,
path: string,
): Array<WhitelistedFolder | WhitelistedPattern> {
return plugin.settings.whitelistFolders.filter((whitelistedFolder) => {
if (whitelistedFolder.path === path) { return true; }
if (!whitelistedFolder.subFolders) { return false; }
@ -55,37 +73,58 @@ export function getWhitelistedFoldersByPath(plugin: FolderNotesPlugin, path: str
});
}
export function addWhitelistedFolder(plugin: FolderNotesPlugin, whitelistedFolder: WhitelistedFolder) {
export function addWhitelistedFolder(
plugin: FolderNotesPlugin,
whitelistedFolder: WhitelistedFolder | WhitelistedPattern,
): void {
plugin.settings.whitelistFolders.push(whitelistedFolder);
plugin.saveSettings(true);
void plugin.saveSettings(true);
}
export function deleteWhitelistedFolder(plugin: FolderNotesPlugin, whitelistedFolder: WhitelistedFolder) {
plugin.settings.whitelistFolders = plugin.settings.whitelistFolders.filter((folder) => folder.id !== whitelistedFolder.id || folder.type === 'pattern');
plugin.saveSettings(true);
export async function deleteWhitelistedFolder(
plugin: FolderNotesPlugin,
whitelistedFolder: WhitelistedFolder | WhitelistedPattern,
): Promise<void> {
plugin.settings.whitelistFolders = plugin.settings.whitelistFolders.filter(
(folder) => folder.id !== whitelistedFolder.id || folder.type === 'pattern',
);
await plugin.saveSettings(true);
resyncArray(plugin);
}
export function updateWhitelistedFolder(plugin: FolderNotesPlugin, whitelistedFolder: WhitelistedFolder, newWhitelistFolder: WhitelistedFolder) {
plugin.settings.whitelistFolders = plugin.settings.whitelistFolders.filter((folder) => folder.id !== whitelistedFolder.id);
export function updateWhitelistedFolder(
plugin: FolderNotesPlugin,
whitelistedFolder: WhitelistedFolder,
newWhitelistFolder: WhitelistedFolder,
): void {
plugin.settings.whitelistFolders = plugin.settings.whitelistFolders.filter(
(folder) => folder.id !== whitelistedFolder.id,
);
addWhitelistedFolder(plugin, newWhitelistFolder);
}
export function resyncArray(plugin: FolderNotesPlugin) {
plugin.settings.whitelistFolders = plugin.settings.whitelistFolders.sort((a, b) => a.position - b.position);
export function resyncArray(plugin: FolderNotesPlugin): void {
plugin.settings.whitelistFolders = plugin.settings.whitelistFolders.sort(
(a, b) => a.position - b.position,
);
plugin.settings.whitelistFolders.forEach((folder, index) => {
folder.position = index;
});
plugin.saveSettings();
void plugin.saveSettings();
}
export function addWhitelistFolderListItem(settings: SettingsTab, containerEl: HTMLElement, whitelistedFolder: WhitelistedFolder) {
export function addWhitelistFolderListItem(
settings: SettingsTab,
containerEl: HTMLElement,
whitelistedFolder: WhitelistedFolder,
): void {
const { plugin } = settings;
const setting = new Setting(containerEl);
setting.setClass('fn-exclude-folder-list');
const inputContainer = setting.settingEl.createDiv({ cls: 'fn-whitelist-folder-input-container' });
const inputContainer = setting.settingEl.createDiv({
cls: 'fn-whitelist-folder-input-container',
});
const SearchComponent = new Setting(inputContainer);
SearchComponent.addSearch((cb) => {
new FolderSuggest(
@ -93,14 +132,19 @@ export function addWhitelistFolderListItem(settings: SettingsTab, containerEl: H
plugin,
true,
);
// @ts-ignore
// @ts-expect-error Obsidian's public types don't include this property
cb.containerEl.addClass('fn-exclude-folder-path');
cb.setPlaceholder('Folder path');
cb.setValue(whitelistedFolder.path);
cb.onChange((value) => {
if (value.startsWith('{regex}') || value.includes('*')) {
deleteWhitelistedFolder(plugin, whitelistedFolder);
const pattern = new WhitelistedPattern(value, plugin.settings.whitelistFolders.length, undefined, plugin);
void deleteWhitelistedFolder(plugin, whitelistedFolder);
const pattern = new WhitelistedPattern(
value,
plugin.settings.whitelistFolders.length,
undefined,
plugin,
);
addWhitelistedFolder(plugin, pattern);
addWhitelistedPatternListItem(settings, containerEl, pattern);
setting.clear();
@ -127,7 +171,9 @@ export function addWhitelistFolderListItem(settings: SettingsTab, containerEl: H
if (whitelistedFolder.position === 0) { return; }
whitelistedFolder.position -= 1;
updateWhitelistedFolder(plugin, whitelistedFolder, whitelistedFolder);
const oldWhitelistedFolder = plugin.settings.whitelistFolders.find((folder) => folder.position === whitelistedFolder.position);
const oldWhitelistedFolder = plugin.settings.whitelistFolders.find(
(folder) => folder.position === whitelistedFolder.position,
);
if (oldWhitelistedFolder) {
oldWhitelistedFolder.position += 1;
if (oldWhitelistedFolder.type === 'pattern') {
@ -149,7 +195,9 @@ export function addWhitelistFolderListItem(settings: SettingsTab, containerEl: H
whitelistedFolder.position += 1;
updateWhitelistedFolder(plugin, whitelistedFolder, whitelistedFolder);
const oldWhitelistedFolder = plugin.settings.whitelistFolders.find((folder) => folder.position === whitelistedFolder.position);
const oldWhitelistedFolder = plugin.settings.whitelistFolders.find(
(folder) => folder.position === whitelistedFolder.position,
);
if (oldWhitelistedFolder) {
oldWhitelistedFolder.position -= 1;
if (oldWhitelistedFolder.type === 'pattern') {
@ -166,7 +214,7 @@ export function addWhitelistFolderListItem(settings: SettingsTab, containerEl: H
.setIcon('trash-2')
.setTooltip('Delete excluded folder')
.onClick(() => {
deleteWhitelistedFolder(plugin, whitelistedFolder);
void deleteWhitelistedFolder(plugin, whitelistedFolder);
setting.clear();
setting.settingEl.remove();
});

View file

@ -6,84 +6,106 @@ import WhitelistPatternSettings from '../modals/WhitelistPatternSettings';
import type { WhitelistedPattern } from '../WhitelistPattern';
import { addWhitelistedFolder, updateWhitelistedFolder } from './whitelistFolderFunctions';
export function updateWhitelistedPattern(plugin: FolderNotesPlugin, pattern: WhitelistedPattern, newPattern: WhitelistedPattern) {
plugin.settings.whitelistFolders = plugin.settings.whitelistFolders.filter((folder) => folder.id !== pattern.id);
const REGEX_PREFIX = '{regex}';
const STAR = '*';
const SLICE_START_ONE = 1;
const SLICE_EXCLUDE_LAST = -1;
function matchesPatternSpec(raw: string | undefined, folderName: string): boolean {
if (!raw) return false;
const string = raw.trim();
const isRegex = string.startsWith(REGEX_PREFIX);
const hasStartStar = string.startsWith(STAR);
const hasEndStar = string.endsWith(STAR);
if (!isRegex && !(hasStartStar || hasEndStar)) return false;
if (isRegex) {
const body = string.replace(REGEX_PREFIX, '').trim();
if (body === '') return false;
try {
return new RegExp(body).test(folderName);
} catch {
return false;
}
}
if (hasStartStar && hasEndStar) {
const inner = string.slice(SLICE_START_ONE, SLICE_EXCLUDE_LAST);
return folderName.includes(inner);
}
if (hasStartStar) {
const suffix = string.slice(SLICE_START_ONE);
return folderName.endsWith(suffix);
}
if (hasEndStar) {
const prefix = string.slice(0, SLICE_EXCLUDE_LAST);
return folderName.startsWith(prefix);
}
return false;
}
export function updateWhitelistedPattern(
plugin: FolderNotesPlugin,
pattern: WhitelistedPattern,
newPattern: WhitelistedPattern,
): void {
plugin.settings.whitelistFolders = plugin.settings.whitelistFolders.filter(
(folder) => folder.id !== pattern.id,
);
addWhitelistedFolder(plugin, newPattern);
}
export function deletePattern(plugin: FolderNotesPlugin, pattern: WhitelistedPattern) {
plugin.settings.whitelistFolders = plugin.settings.whitelistFolders.filter((folder) => folder.id !== pattern.id || folder.type === 'folder');
plugin.saveSettings(true);
export async function deletePattern(
plugin: FolderNotesPlugin,
pattern: WhitelistedPattern,
): Promise<void> {
plugin.settings.whitelistFolders = plugin.settings.whitelistFolders.filter(
(folder) => folder.id !== pattern.id || folder.type === 'folder',
);
await plugin.saveSettings(true);
resyncArray(plugin);
}
export function getWhitelistedFolderByPattern(plugin: FolderNotesPlugin, folderName: string) {
return plugin.settings.whitelistFolders.filter((s) => s.type === 'pattern').find((pattern) => {
if (!pattern.string) { return false; }
const string = pattern.string.trim();
if (!string.startsWith('{regex}') && !(string.startsWith('*') || string.endsWith('*'))) { return false; }
const regex = string.replace('{regex}', '').trim();
if (string.startsWith('{regex}') && regex === '') { return false; }
if (regex !== undefined && string.startsWith('{regex}')) {
const match = new RegExp(regex).exec(folderName);
if (match) {
return true;
}
} else if (string.startsWith('*') && string.endsWith('*')) {
if (folderName.includes(string.slice(1, -1))) {
return true;
}
} else if (string.startsWith('*')) {
if (folderName.endsWith(string.slice(1))) {
return true;
}
} else if (string.endsWith('*')) {
if (folderName.startsWith(string.slice(0, -1))) {
return true;
}
}
});
export function getWhitelistedFolderByPattern(
plugin: FolderNotesPlugin,
folderName: string,
): WhitelistedPattern | undefined {
return (
plugin.settings.whitelistFolders
.filter((s) => s.type === 'pattern')
.find((pattern) => matchesPatternSpec(pattern.string, folderName))
) as WhitelistedPattern | undefined;
}
export function getWhitelistedFoldersByPattern(plugin: FolderNotesPlugin, folderName: string) {
return plugin.settings.whitelistFolders.filter((s) => s.type === 'pattern').filter((pattern) => {
if (!pattern.string) { return false; }
const string = pattern.string.trim();
if (!string.startsWith('{regex}') && !(string.startsWith('*') || string.endsWith('*'))) { return false; }
const regex = string.replace('{regex}', '').trim();
if (string.startsWith('{regex}') && regex === '') { return false; }
if (regex !== undefined && string.startsWith('{regex}')) {
const match = new RegExp(regex).exec(folderName);
if (match) {
return true;
}
} else if (string.startsWith('*') && string.endsWith('*')) {
if (folderName.includes(string.slice(1, -1))) {
return true;
}
} else if (string.startsWith('*')) {
if (folderName.endsWith(string.slice(1))) {
return true;
}
} else if (string.endsWith('*')) {
if (folderName.startsWith(string.slice(0, -1))) {
return true;
}
}
});
export function getWhitelistedFoldersByPattern(
plugin: FolderNotesPlugin,
folderName: string,
): WhitelistedPattern[] {
return (
plugin.settings.whitelistFolders
.filter((s) => s.type === 'pattern')
.filter((pattern) => matchesPatternSpec(pattern.string, folderName))
) as WhitelistedPattern[];
}
export function addWhitelistedPatternListItem(settings: SettingsTab, containerEl: HTMLElement, pattern: WhitelistedPattern) {
export function addWhitelistedPatternListItem(
settings: SettingsTab,
containerEl: HTMLElement,
pattern: WhitelistedPattern,
): void {
const { plugin } = settings;
const setting = new Setting(containerEl);
setting.setClass('fn-exclude-folder-list');
setting.addSearch((cb) => {
// @ts-ignore
// @ts-expect-error Obsidian's public types don't expose containerEl on this control
cb.containerEl.addClass('fn-exclude-folder-path');
cb.setPlaceholder('Pattern');
cb.setValue(pattern.string);
cb.onChange((value) => {
if (plugin.settings.whitelistFolders.find((folder) => folder.string === value)) { return; }
const exists = plugin.settings.whitelistFolders.some(
(folder) => folder.string === value,
);
if (exists) { return; }
pattern.string = value;
updateWhitelistedPattern(plugin, pattern, pattern);
});
@ -103,11 +125,17 @@ export function addWhitelistedPatternListItem(settings: SettingsTab, containerEl
if (pattern.position === 0) { return; }
pattern.position -= 1;
updateWhitelistedPattern(plugin, pattern, pattern);
const oldPattern = plugin.settings.whitelistFolders.find((folder) => folder.position === pattern.position);
const oldPattern = plugin.settings.whitelistFolders.find(
(folder) => folder.position === pattern.position,
);
if (oldPattern) {
oldPattern.position += 1;
if (oldPattern.type === 'pattern') {
updateWhitelistedPattern(plugin, oldPattern, oldPattern);
updateWhitelistedPattern(
plugin,
oldPattern as WhitelistedPattern,
oldPattern as WhitelistedPattern,
);
} else {
updateWhitelistedFolder(plugin, oldPattern, oldPattern);
}
@ -126,11 +154,17 @@ export function addWhitelistedPatternListItem(settings: SettingsTab, containerEl
pattern.position += 1;
updateWhitelistedPattern(plugin, pattern, pattern);
const oldPattern = plugin.settings.whitelistFolders.find((folder) => folder.position === pattern.position);
const oldPattern = plugin.settings.whitelistFolders.find(
(folder) => folder.position === pattern.position,
);
if (oldPattern) {
oldPattern.position -= 1;
if (oldPattern.type === 'pattern') {
updateWhitelistedPattern(plugin, oldPattern, oldPattern);
updateWhitelistedPattern(
plugin,
oldPattern as WhitelistedPattern,
oldPattern as WhitelistedPattern,
);
} else {
updateWhitelistedFolder(plugin, oldPattern, oldPattern);
}
@ -143,7 +177,7 @@ export function addWhitelistedPatternListItem(settings: SettingsTab, containerEl
cb.setIcon('trash-2');
cb.setTooltip('Delete pattern');
cb.onClick(() => {
deletePattern(plugin, pattern);
void deletePattern(plugin, pattern);
setting.clear();
setting.settingEl.remove();
});

View file

@ -1,5 +1,4 @@
import type { App } from 'obsidian';
import { Modal, Setting } from 'obsidian';
import { Modal, Setting, type App } from 'obsidian';
import type FolderNotesPlugin from '../../main';
import type { ExcludedFolder } from 'src/ExcludeFolders/ExcludeFolder';
import { updateCSSClassesForFolder } from 'src/functions/styleFunctions';
@ -13,10 +12,10 @@ export default class ExcludedFolderSettings extends Modal {
this.app = app;
this.excludedFolder = excludedFolder;
}
onOpen() {
onOpen(): void {
this.display();
}
display() {
display(): void {
const { contentEl } = this;
contentEl.empty();
contentEl.createEl('h2', { text: 'Excluded folder settings' });
@ -108,9 +107,9 @@ export default class ExcludedFolderSettings extends Modal {
}),
);
}
}
onClose() {
onClose(): void {
const { contentEl } = this;
contentEl.empty();
}

View file

@ -1,5 +1,4 @@
import type { App } from 'obsidian';
import { Modal, Setting } from 'obsidian';
import { Modal, Setting, type App } from 'obsidian';
import type FolderNotesPlugin from '../../main';
import type { ExcludePattern } from 'src/ExcludeFolders/ExcludePattern';
import { refreshAllFolderStyles } from 'src/functions/styleFunctions';
@ -14,16 +13,19 @@ export default class PatternSettings extends Modal {
this.app = app;
this.pattern = pattern;
}
onOpen() {
onOpen(): void {
this.display();
}
display() {
display(): void {
const { contentEl } = this;
contentEl.empty();
contentEl.createEl('h2', { text: 'Pattern settings' });
new Setting(contentEl)
.setName('Disable folder name sync')
// eslint-disable-next-line max-len
.setDesc('Choose if the folder name should be renamed when the file name has been changed')
.addToggle((toggle) =>
toggle
@ -36,6 +38,7 @@ export default class PatternSettings extends Modal {
new Setting(contentEl)
.setName('Disable auto creation of folder notes in this folder')
// eslint-disable-next-line max-len
.setDesc('Choose if a folder note should be created when a new folder is created that matches this pattern')
.addToggle((toggle) =>
toggle
@ -98,9 +101,9 @@ export default class PatternSettings extends Modal {
}),
);
}
}
onClose() {
onClose(): void {
const { contentEl } = this;
contentEl.empty();
}

View file

@ -1,5 +1,4 @@
import type { App } from 'obsidian';
import { Modal, Setting } from 'obsidian';
import { Modal, Setting, type App } from 'obsidian';
import type FolderNotesPlugin from '../../main';
import type { WhitelistedFolder } from '../WhitelistFolder';
export default class WhitelistFolderSettings extends Modal {
@ -12,10 +11,12 @@ export default class WhitelistFolderSettings extends Modal {
this.app = app;
this.whitelistedFolder = whitelistedFolder;
}
onOpen() {
onOpen(): void {
this.display();
}
display() {
display(): void {
const { contentEl } = this;
contentEl.empty();
contentEl.createEl('h2', { text: 'Whitelisted folder settings' });
@ -33,6 +34,7 @@ export default class WhitelistFolderSettings extends Modal {
new Setting(contentEl)
.setName('Enable folder name sync')
// eslint-disable-next-line max-len
.setDesc('Choose if the name of a folder note should be renamed when the folder name is changed')
.addToggle((toggle) =>
toggle
@ -107,7 +109,7 @@ export default class WhitelistFolderSettings extends Modal {
}
}
onClose() {
onClose(): void {
const { contentEl } = this;
contentEl.empty();
}

View file

@ -1,5 +1,4 @@
import type { App } from 'obsidian';
import { Modal, Setting } from 'obsidian';
import { Modal, Setting, type App } from 'obsidian';
import type FolderNotesPlugin from '../../main';
import type { WhitelistedPattern } from '../WhitelistPattern';
@ -13,15 +12,18 @@ export default class WhitelistPatternSettings extends Modal {
this.app = app;
this.pattern = pattern;
}
onOpen() {
onOpen(): void {
this.display();
}
display() {
display(): void {
const { contentEl } = this;
contentEl.empty();
contentEl.createEl('h2', { text: 'Whitelisted pattern settings' });
new Setting(contentEl)
.setName('Enable folder name sync')
// eslint-disable-next-line max-len
.setDesc('Choose if the name of a folder note should be renamed when the folder name is changed')
.addToggle((toggle) =>
toggle
@ -82,9 +84,9 @@ export default class WhitelistPatternSettings extends Modal {
}),
);
}
}
onClose() {
onClose(): void {
const { contentEl } = this;
contentEl.empty();
}

View file

@ -1,9 +1,11 @@
import type { App } from 'obsidian';
import { Modal, Setting } from 'obsidian';
import { Modal, Setting, type App } from 'obsidian';
import type { SettingsTab } from 'src/settings/SettingsTab';
import type FolderNotesPlugin from '../../main';
import { WhitelistedFolder } from '../WhitelistFolder';
import { addWhitelistFolderListItem, addWhitelistedFolder } from '../functions/whitelistFolderFunctions';
import {
addWhitelistFolderListItem,
addWhitelistedFolder,
} from '../functions/whitelistFolderFunctions';
import { addWhitelistedPatternListItem } from '../functions/whitelistPatternFunctions';
export default class WhitelistedFoldersSettings extends Modal {
@ -16,7 +18,8 @@ export default class WhitelistedFoldersSettings extends Modal {
this.settingsTab = settingsTab;
this.app = settingsTab.app;
}
onOpen() {
onOpen(): void {
const { contentEl } = this;
contentEl.createEl('h2', { text: 'Manage whitelisted folders' });
@ -29,22 +32,31 @@ export default class WhitelistedFoldersSettings extends Modal {
cb.setClass('add-exclude-folder');
cb.setTooltip('Add whitelisted folder');
cb.onClick(() => {
const whitelistedFolder = new WhitelistedFolder('', this.plugin.settings.whitelistFolders.length, undefined, this.plugin);
addWhitelistFolderListItem(this.plugin.settingsTab, contentEl, whitelistedFolder);
const whitelistedFolder = new WhitelistedFolder(
'', this.plugin.settings.whitelistFolders.length,
undefined, this.plugin,
);
addWhitelistFolderListItem(
this.plugin.settingsTab, contentEl, whitelistedFolder,
);
addWhitelistedFolder(this.plugin, whitelistedFolder);
this.settingsTab.display();
});
});
this.plugin.settings.whitelistFolders.sort((a, b) => a.position - b.position).forEach((whitelistedFolder) => {
if (whitelistedFolder.string?.trim() !== '' && whitelistedFolder.path?.trim() === '') {
addWhitelistedPatternListItem(this.settingsTab, contentEl, whitelistedFolder);
} else {
addWhitelistFolderListItem(this.settingsTab, contentEl, whitelistedFolder);
}
});
this.plugin.settings.whitelistFolders
.sort((a, b) => a.position - b.position)
.forEach((whitelistedFolder) => {
if (whitelistedFolder.string?.trim() !== '' &&
whitelistedFolder.path?.trim() === '') {
addWhitelistedPatternListItem(this.settingsTab, contentEl, whitelistedFolder);
} else {
addWhitelistFolderListItem(this.settingsTab, contentEl, whitelistedFolder);
}
});
}
onClose() {
onClose(): void {
const { contentEl } = this;
contentEl.empty();
}