mirror of
https://github.com/dragonish/obsidian-heading-decorator.git
synced 2026-07-22 05:42:05 +00:00
feat: add support for blacklist function
Allow to specify the folder blacklist and the regex blacklist of note name. Resolve: #7
This commit is contained in:
parent
ca1026eb97
commit
c013db2e56
4 changed files with 229 additions and 7 deletions
|
|
@ -62,6 +62,8 @@ interface HeadingDecoratorSettings {
|
|||
|
||||
export type HeadingPluginSettings = {
|
||||
metadataKeyword: string;
|
||||
folderBlacklist: string[];
|
||||
fileRegexBlacklist: string[];
|
||||
enabledInReading: boolean;
|
||||
enabledInPreview: boolean;
|
||||
enabledInSource: boolean;
|
||||
|
|
@ -71,6 +73,8 @@ export type HeadingPluginSettings = {
|
|||
export type HeadingPluginData = Omit<
|
||||
HeadingPluginSettings,
|
||||
| "metadataKeyword"
|
||||
| "folderBlacklist"
|
||||
| "fileRegexBlacklist"
|
||||
| "enabledInReading"
|
||||
| "readingSettings"
|
||||
| "enabledInOutline"
|
||||
|
|
@ -284,3 +288,25 @@ export function checkEnabledCss(
|
|||
}
|
||||
return null; // default to null if no matching class is found
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a string to a regex object.
|
||||
* @param value The string to convert to a regex.
|
||||
* @returns A regex object if the string is a valid regex, otherwise null.
|
||||
*/
|
||||
export function stringToRegex(value: string) {
|
||||
const str = value.trim();
|
||||
if (!str) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const re = new RegExp(
|
||||
str.substring(1, str.lastIndexOf("/")),
|
||||
str.substring(str.lastIndexOf("/") + 1)
|
||||
);
|
||||
return re;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
21
components/suggest.ts
Normal file
21
components/suggest.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import { App, AbstractInputSuggest } from "obsidian";
|
||||
|
||||
export class FolderSuggest extends AbstractInputSuggest<string> {
|
||||
constructor(
|
||||
public app: App,
|
||||
public textInputEl: HTMLInputElement | HTMLDivElement
|
||||
) {
|
||||
super(app, textInputEl);
|
||||
}
|
||||
|
||||
getSuggestions(query: string): string[] {
|
||||
const folders = this.app.vault.getAllFolders();
|
||||
return folders
|
||||
.map((folder) => folder.path)
|
||||
.filter((path) => path.toLowerCase().includes(query.toLowerCase()));
|
||||
}
|
||||
|
||||
renderSuggestion(value: string, el: HTMLElement) {
|
||||
el.setText(value);
|
||||
}
|
||||
}
|
||||
179
main.ts
179
main.ts
|
|
@ -25,6 +25,7 @@ import {
|
|||
diffLevel,
|
||||
getBoolean,
|
||||
checkEnabledCss,
|
||||
stringToRegex,
|
||||
} from "./common/data";
|
||||
import { Counter, Querier } from "./common/counter";
|
||||
import { Heading } from "./common/heading";
|
||||
|
|
@ -43,6 +44,7 @@ import {
|
|||
updateEditorMode,
|
||||
} from "./components/view";
|
||||
import { OutlineChildComponent } from "./components/outline";
|
||||
import { FolderSuggest } from "./components/suggest";
|
||||
|
||||
interface ObsidianEditor extends Editor {
|
||||
cm: EditorView;
|
||||
|
|
@ -54,6 +56,8 @@ interface ObsidianWorkspaceLeaf extends WorkspaceLeaf {
|
|||
|
||||
const DEFAULT_SETTINGS: HeadingPluginSettings = {
|
||||
metadataKeyword: "heading",
|
||||
fileRegexBlacklist: [],
|
||||
folderBlacklist: [],
|
||||
enabledInReading: true,
|
||||
readingSettings: defaultHeadingDecoratorSettings(),
|
||||
enabledInPreview: true,
|
||||
|
|
@ -90,6 +94,7 @@ export default class HeadingPlugin extends Plugin {
|
|||
"reading",
|
||||
context.frontmatter
|
||||
);
|
||||
|
||||
if (metadataEnabled == null) {
|
||||
if (
|
||||
this.settings.readingSettings.enabledInEachNote != undefined &&
|
||||
|
|
@ -97,6 +102,10 @@ export default class HeadingPlugin extends Plugin {
|
|||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.getEnabledFromBlacklist(context.sourcePath)) {
|
||||
return;
|
||||
}
|
||||
} else if (!metadataEnabled) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -403,12 +412,20 @@ export default class HeadingPlugin extends Plugin {
|
|||
}
|
||||
|
||||
const frontmatter = fileCache.frontmatter;
|
||||
const enabled =
|
||||
this.getEnabledFromFrontmatter("outline", frontmatter) ??
|
||||
enabledInEachNote ??
|
||||
true;
|
||||
const metadataEnabled = this.getEnabledFromFrontmatter(
|
||||
"outline",
|
||||
frontmatter
|
||||
);
|
||||
|
||||
if (!enabled) {
|
||||
if (metadataEnabled == null) {
|
||||
if (enabledInEachNote != undefined && !enabledInEachNote) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.getEnabledFromBlacklist(state.file)) {
|
||||
return;
|
||||
}
|
||||
} else if (!metadataEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -526,14 +543,14 @@ export default class HeadingPlugin extends Plugin {
|
|||
enabledInPreview =
|
||||
this.getEnabledFromFrontmatter("preview", frontmatter) ??
|
||||
previewSettings.enabledInEachNote ??
|
||||
true;
|
||||
!this.getEnabledFromBlacklist(file.path);
|
||||
}
|
||||
|
||||
if (_enabledInSource) {
|
||||
enabledInSource =
|
||||
this.getEnabledFromFrontmatter("source", frontmatter) ??
|
||||
sourceSettings.enabledInEachNote ??
|
||||
true;
|
||||
!this.getEnabledFromBlacklist(file.path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -587,6 +604,29 @@ export default class HeadingPlugin extends Plugin {
|
|||
|
||||
return null;
|
||||
}
|
||||
|
||||
private getEnabledFromBlacklist(filepath: string): boolean {
|
||||
for (const folder of this.settings.folderBlacklist) {
|
||||
if (filepath.startsWith(`${folder}/`)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const filename = filepath.substring(
|
||||
filepath.lastIndexOf("/") + 1,
|
||||
filepath.lastIndexOf(".")
|
||||
);
|
||||
for (const regexStr of this.settings.fileRegexBlacklist) {
|
||||
const regex = stringToRegex(regexStr);
|
||||
if (regex) {
|
||||
if (regex.test(filename)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class HeadingSettingTab extends PluginSettingTab {
|
||||
|
|
@ -626,6 +666,24 @@ class HeadingSettingTab extends PluginSettingTab {
|
|||
);
|
||||
metadataKeywordSetting.descEl.appendChild(metadataKeywordDesc);
|
||||
|
||||
//* folderBlacklist
|
||||
new Setting(containerEl)
|
||||
.setName("Manage folder blacklist")
|
||||
.addButton((button) => {
|
||||
button.setButtonText("Manage").onClick(() => {
|
||||
this.manageFolderBlacklist();
|
||||
});
|
||||
});
|
||||
|
||||
//* fileRegexBlacklist
|
||||
new Setting(containerEl)
|
||||
.setName("Manage note name regex blacklist")
|
||||
.addButton((button) => {
|
||||
button.setButtonText("Manage").onClick(() => {
|
||||
this.manageFileRegexBlacklist();
|
||||
});
|
||||
});
|
||||
|
||||
new Setting(containerEl).setName("Reading view").setHeading();
|
||||
|
||||
//* enabledInReading
|
||||
|
|
@ -996,4 +1054,111 @@ class HeadingSettingTab extends PluginSettingTab {
|
|||
})
|
||||
);
|
||||
}
|
||||
|
||||
private manageFolderBlacklist() {
|
||||
const { containerEl } = this;
|
||||
|
||||
containerEl.empty();
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName("Manage folder blacklist")
|
||||
.setHeading()
|
||||
.addButton((button) => {
|
||||
button.setButtonText("Back").onClick(() => {
|
||||
this.display();
|
||||
});
|
||||
});
|
||||
|
||||
this.plugin.settings.folderBlacklist.forEach((folder, index) => {
|
||||
new Setting(containerEl)
|
||||
.setName(`Folder balcklist ${index + 1}`)
|
||||
.addText((text) => {
|
||||
text.setValue(folder).onChange(async (value) => {
|
||||
this.plugin.settings.folderBlacklist[index] = value;
|
||||
await this.plugin.saveSettings();
|
||||
});
|
||||
|
||||
const suggest = new FolderSuggest(this.app, text.inputEl);
|
||||
suggest.onSelect(async (value) => {
|
||||
text.setValue(value);
|
||||
this.plugin.settings.folderBlacklist[index] = value;
|
||||
suggest.close();
|
||||
await this.plugin.saveSettings();
|
||||
});
|
||||
})
|
||||
.addButton((button) => {
|
||||
button
|
||||
.setButtonText("Delete")
|
||||
.setWarning()
|
||||
.onClick(async () => {
|
||||
this.plugin.settings.folderBlacklist.splice(index, 1);
|
||||
await this.plugin.saveSettings();
|
||||
this.manageFolderBlacklist();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
new Setting(containerEl).addButton((button) => {
|
||||
button
|
||||
.setButtonText("Add")
|
||||
.setCta()
|
||||
.setTooltip("Add a new folder to the blacklist")
|
||||
.onClick(async () => {
|
||||
this.plugin.settings.folderBlacklist.push("");
|
||||
await this.plugin.saveSettings();
|
||||
this.manageFolderBlacklist();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private manageFileRegexBlacklist() {
|
||||
const { containerEl } = this;
|
||||
|
||||
containerEl.empty();
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName("Manage note name regex blacklist")
|
||||
.setHeading()
|
||||
.addButton((button) => {
|
||||
button.setButtonText("Back").onClick(() => {
|
||||
this.display();
|
||||
});
|
||||
});
|
||||
|
||||
this.plugin.settings.fileRegexBlacklist.forEach((regex, index) => {
|
||||
new Setting(containerEl)
|
||||
.setName(`Note name regex blacklist ${index + 1}`)
|
||||
.addText((text) =>
|
||||
text
|
||||
.setPlaceholder("e.g., /^daily.*/i")
|
||||
.setValue(regex)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.fileRegexBlacklist[index] = value.trim();
|
||||
await this.plugin.saveSettings();
|
||||
})
|
||||
)
|
||||
.addButton((button) => {
|
||||
button
|
||||
.setButtonText("Delete")
|
||||
.setWarning()
|
||||
.onClick(async () => {
|
||||
this.plugin.settings.fileRegexBlacklist.splice(index, 1);
|
||||
await this.plugin.saveSettings();
|
||||
this.manageFileRegexBlacklist();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
new Setting(containerEl).addButton((button) => {
|
||||
button
|
||||
.setButtonText("Add")
|
||||
.setCta()
|
||||
.setTooltip("Add a new note name regex blacklist")
|
||||
.onClick(async () => {
|
||||
this.plugin.settings.fileRegexBlacklist.push("");
|
||||
await this.plugin.saveSettings();
|
||||
this.manageFileRegexBlacklist();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import {
|
|||
compareMarkdownText,
|
||||
getBoolean,
|
||||
checkEnabledCss,
|
||||
stringToRegex,
|
||||
} from "../../common/data";
|
||||
|
||||
describe("common/data", function () {
|
||||
|
|
@ -91,4 +92,13 @@ describe("common/data", function () {
|
|||
expect(checkEnabledCss("disable-reading-heading enable-heading", "reading"))
|
||||
.to.be.false;
|
||||
});
|
||||
|
||||
it("stringToRegex", function () {
|
||||
expect(stringToRegex("")).to.be.null;
|
||||
expect(stringToRegex("abc")).to.be.null;
|
||||
expect(stringToRegex("/abc/")).to.be.an.instanceof(RegExp);
|
||||
expect(stringToRegex("/abc/ig")).to.be.an.instanceof(RegExp);
|
||||
expect(stringToRegex("/a\\/bc/ig")).to.be.an.instanceof(RegExp);
|
||||
expect(stringToRegex("/abc/a")).to.be.null;
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue