From e66d7be9d3dc517a2ca1b64a3c79aa4620013a03 Mon Sep 17 00:00:00 2001
From: SebastianMC <23032356+SebastianMC@users.noreply.github.com>
Date: Sun, 1 Sep 2024 15:23:38 +0200
Subject: [PATCH 1/4] Extracted setting to a separate settings.ts
---
src/main.ts | 197 ++----------------------------------------------
src/settings.ts | 195 +++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 201 insertions(+), 191 deletions(-)
create mode 100644 src/settings.ts
diff --git a/src/main.ts b/src/main.ts
index 61b4de7..d9d61fe 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -58,36 +58,12 @@ import {
HasSortingOrGrouping,
ImplicitSortspecForBookmarksIntegration
} from "./custom-sort/custom-sort-utils";
-
-interface CustomSortPluginSettings {
- additionalSortspecFile: string
- suspended: boolean
- statusBarEntryEnabled: boolean
- notificationsEnabled: boolean
- mobileNotificationsEnabled: boolean
- automaticBookmarksIntegration: boolean
- customSortContextSubmenu: boolean
- bookmarksContextMenus: boolean
- bookmarksGroupToConsumeAsOrderingReference: string
-}
-
-const DEFAULT_SETTINGS: CustomSortPluginSettings = {
- additionalSortspecFile: '',
- suspended: true, // if false by default, it would be hard to handle the auto-parse after plugin install
- statusBarEntryEnabled: true,
- notificationsEnabled: true,
- mobileNotificationsEnabled: false,
- customSortContextSubmenu: true,
- automaticBookmarksIntegration: false,
- bookmarksContextMenus: false,
- bookmarksGroupToConsumeAsOrderingReference: 'sortspec'
-}
-
-// On API 1.2.x+ enable the bookmarks integration by default
-const DEFAULT_SETTING_FOR_1_2_0_UP: Partial = {
- automaticBookmarksIntegration: true,
- bookmarksContextMenus: true
-}
+import {
+ CustomSortPluginSettings,
+ CustomSortSettingTab,
+ DEFAULT_SETTING_FOR_1_2_0_UP,
+ DEFAULT_SETTINGS
+} from "./settings";
const SORTSPEC_FILE_NAME: string = 'sortspec.md'
const SORTINGSPEC_YAML_KEY: string = 'sorting-spec'
@@ -747,164 +723,3 @@ export default class CustomSortPlugin extends Plugin {
}
}
-const pathToFlatString = (path: string): string => {
- return path.replace(/\//g,'_').replace(/\\/g, '_')
-}
-
-class CustomSortSettingTab extends PluginSettingTab {
- plugin: CustomSortPlugin;
-
- constructor(app: App, plugin: CustomSortPlugin) {
- super(app, plugin);
- this.plugin = plugin;
- }
-
- display(): void {
- const {containerEl} = this;
-
- containerEl.empty();
-
- // containerEl.createEl('h2', {text: 'Settings for Custom File Explorer Sorting Plugin'});
-
- const additionalSortspecFileDescr: DocumentFragment = sanitizeHTMLToDom(
- 'A note name or note path to scan (YAML frontmatter) for sorting specification in addition to the `sortspec` notes and Folder Notes*.'
- + '
'
- + ' The `.md` filename suffix is optional.'
- + '(*) if you employ the Index-File based approach to folder notes (as documented in '
- + 'Aidenlx Folder Note preferences'
- + ') you can enter here the index note name, e.g. _about_'
- + '
'
- + 'The Inside Folder, with Same Name Recommended mode of Folder Notes is handled automatically, no additional configuration needed.'
- + '
'
- + 'NOTE: After updating this setting remember to refresh the custom sorting via clicking on the ribbon icon or via the sort-on command'
- + ' or by restarting Obsidian or reloading the vault
'
- )
-
- new Setting(containerEl)
- .setName('Path or name of additional note(s) containing sorting specification')
- .setDesc(additionalSortspecFileDescr)
- .addText(text => text
- .setPlaceholder('e.g. _about_')
- .setValue(this.plugin.settings.additionalSortspecFile)
- .onChange(async (value) => {
- this.plugin.settings.additionalSortspecFile = value.trim() ? normalizePath(value) : '';
- await this.plugin.saveSettings();
- }));
-
- new Setting(containerEl)
- .setName('Enable the status bar entry')
- .setDesc('The status bar entry shows the label `Custom sort:ON` or `Custom sort:OFF`, representing the current state of the plugin.')
- .addToggle(toggle => toggle
- .setValue(this.plugin.settings.statusBarEntryEnabled)
- .onChange(async (value) => {
- this.plugin.settings.statusBarEntryEnabled = value;
- if (value) {
- // Enabling
- if (this.plugin.statusBarItemEl) {
- // for sanity
- this.plugin.statusBarItemEl.detach()
- }
- this.plugin.statusBarItemEl = this.plugin.addStatusBarItem();
- this.plugin.updateStatusBar()
-
- } else { // disabling
- if (this.plugin.statusBarItemEl) {
- this.plugin.statusBarItemEl.detach()
- }
- }
- await this.plugin.saveSettings();
- }));
-
- new Setting(containerEl)
- .setName('Enable notifications of plugin state changes')
- .setDesc('The plugin can show notifications about its state changes: e.g. when successfully parsed and applied'
- + ' the custom sorting specification, or, when the parsing failed. If the notifications are disabled,'
- + ' the only indicator of plugin state is the ribbon button icon. The developer console presents the parsing'
- + ' error messages regardless if the notifications are enabled or not.')
- .addToggle(toggle => toggle
- .setValue(this.plugin.settings.notificationsEnabled)
- .onChange(async (value) => {
- this.plugin.settings.notificationsEnabled = value;
- await this.plugin.saveSettings();
- }));
-
- new Setting(containerEl)
- .setName('Enable notifications of plugin state changes for mobile devices only')
- .setDesc('See above.')
- .addToggle(toggle => toggle
- .setValue(this.plugin.settings.mobileNotificationsEnabled)
- .onChange(async (value) => {
- this.plugin.settings.mobileNotificationsEnabled = value;
- await this.plugin.saveSettings();
- }));
-
- new Setting(containerEl)
- .setName('Enable File Explorer context submenu`Custom sort:`')
- .setDesc('Gives access to operations relevant for custom sorting, e.g. applying custom sorting.')
- .addToggle(toggle => toggle
- .setValue(this.plugin.settings.customSortContextSubmenu)
- .onChange(async (value) => {
- this.plugin.settings.customSortContextSubmenu = value;
- await this.plugin.saveSettings();
- }));
-
- containerEl.createEl('h2', {text: 'Bookmarks integration'});
- const bookmarksIntegrationDescription: DocumentFragment = sanitizeHTMLToDom(
- 'If enabled, order of files and folders in File Explorer will reflect the order '
- + 'of bookmarked items in the bookmarks (core plugin) view. Automatically, without any '
- + 'need for sorting configuration. At the same time, it integrates seamlessly with'
- + ' sorting-spec:
configurations and they can nicely cooperate.'
- + '
'
- + 'To separate regular bookmarks from the bookmarks created for sorting, you can put '
- + 'the latter in a separate dedicated bookmarks group. The default name of the group is '
- + "'" + DEFAULT_SETTINGS.bookmarksGroupToConsumeAsOrderingReference + "' "
- + 'and you can change the group name in the configuration field below.'
- + '
'
- + 'If left empty, all the bookmarked items will be used to impose the order in File Explorer.
'
- + 'More information on this functionality in the '
- + ''
- + 'manual of this custom-sort plugin.'
- + '
'
- )
-
- new Setting(containerEl)
- .setName('Automatic integration with core Bookmarks plugin (for indirect drag & drop ordering)')
- .setDesc(bookmarksIntegrationDescription)
- .addToggle(toggle => toggle
- .setValue(this.plugin.settings.automaticBookmarksIntegration)
- .onChange(async (value) => {
- this.plugin.settings.automaticBookmarksIntegration = value;
- await this.plugin.saveSettings();
- }));
-
- new Setting(containerEl)
- .setName('Name of the group in Bookmarks from which to read the order of items')
- .setDesc('See above.')
- .addText(text => text
- .setPlaceholder('e.g. Group for sorting')
- .setValue(this.plugin.settings.bookmarksGroupToConsumeAsOrderingReference)
- .onChange(async (value) => {
- value = groupNameForPath(value.trim()).trim()
- this.plugin.settings.bookmarksGroupToConsumeAsOrderingReference = value ? pathToFlatString(normalizePath(value)) : '';
- await this.plugin.saveSettings();
- }));
-
- const bookmarksIntegrationContextMenusDescription: DocumentFragment = sanitizeHTMLToDom(
- 'Enable Custom-sort: bookmark for sorting and Custom-sort: bookmark+siblings for sorting (and related) entries '
- + 'in context menu in File Explorer'
- )
- new Setting(containerEl)
- .setName('Context menus for Bookmarks integration')
- .setDesc(bookmarksIntegrationContextMenusDescription)
- .addToggle(toggle => toggle
- .setValue(this.plugin.settings.bookmarksContextMenus)
- .onChange(async (value) => {
- this.plugin.settings.bookmarksContextMenus = value;
- if (value) {
- this.plugin.settings.customSortContextSubmenu = true; // automatically enable custom sort context submenu
- }
- await this.plugin.saveSettings();
- }))
- }
-}
diff --git a/src/settings.ts b/src/settings.ts
new file mode 100644
index 0000000..de165b0
--- /dev/null
+++ b/src/settings.ts
@@ -0,0 +1,195 @@
+import {App, normalizePath, PluginSettingTab, sanitizeHTMLToDom, Setting} from "obsidian";
+import {groupNameForPath} from "./utils/BookmarksCorePluginSignature";
+import CustomSortPlugin from "./main";
+
+export interface CustomSortPluginSettings {
+ additionalSortspecFile: string
+ suspended: boolean
+ statusBarEntryEnabled: boolean
+ notificationsEnabled: boolean
+ mobileNotificationsEnabled: boolean
+ automaticBookmarksIntegration: boolean
+ customSortContextSubmenu: boolean
+ bookmarksContextMenus: boolean
+ bookmarksGroupToConsumeAsOrderingReference: string
+}
+
+export const DEFAULT_SETTINGS: CustomSortPluginSettings = {
+ additionalSortspecFile: '',
+ suspended: true, // if false by default, it would be hard to handle the auto-parse after plugin install
+ statusBarEntryEnabled: true,
+ notificationsEnabled: true,
+ mobileNotificationsEnabled: false,
+ customSortContextSubmenu: true,
+ automaticBookmarksIntegration: false,
+ bookmarksContextMenus: false,
+ bookmarksGroupToConsumeAsOrderingReference: 'sortspec'
+}
+
+// On API 1.2.x+ enable the bookmarks integration by default
+export const DEFAULT_SETTING_FOR_1_2_0_UP: Partial = {
+ automaticBookmarksIntegration: true,
+ bookmarksContextMenus: true
+}
+
+const pathToFlatString = (path: string): string => {
+ return path.replace(/\//g,'_').replace(/\\/g, '_')
+}
+
+export class CustomSortSettingTab extends PluginSettingTab {
+ plugin: CustomSortPlugin;
+
+ constructor(app: App, plugin: CustomSortPlugin) {
+ super(app, plugin);
+ this.plugin = plugin;
+ }
+
+ display(): void {
+ const {containerEl} = this;
+
+ containerEl.empty();
+
+ // containerEl.createEl('h2', {text: 'Settings for Custom File Explorer Sorting Plugin'});
+
+ const additionalSortspecFileDescr: DocumentFragment = sanitizeHTMLToDom(
+ 'A note name or note path to scan (YAML frontmatter) for sorting specification in addition to the `sortspec` notes and Folder Notes*.'
+ + '
'
+ + ' The `.md` filename suffix is optional.'
+ + '(*) if you employ the Index-File based approach to folder notes (as documented in '
+ + 'Aidenlx Folder Note preferences'
+ + ') you can enter here the index note name, e.g. _about_'
+ + '
'
+ + 'The Inside Folder, with Same Name Recommended mode of Folder Notes is handled automatically, no additional configuration needed.'
+ + '
'
+ + 'NOTE: After updating this setting remember to refresh the custom sorting via clicking on the ribbon icon or via the sort-on command'
+ + ' or by restarting Obsidian or reloading the vault
'
+ )
+
+ new Setting(containerEl)
+ .setName('Path or name of additional note(s) containing sorting specification')
+ .setDesc(additionalSortspecFileDescr)
+ .addText(text => text
+ .setPlaceholder('e.g. _about_')
+ .setValue(this.plugin.settings.additionalSortspecFile)
+ .onChange(async (value) => {
+ this.plugin.settings.additionalSortspecFile = value.trim() ? normalizePath(value) : '';
+ await this.plugin.saveSettings();
+ }));
+
+ new Setting(containerEl)
+ .setName('Enable the status bar entry')
+ .setDesc('The status bar entry shows the label `Custom sort:ON` or `Custom sort:OFF`, representing the current state of the plugin.')
+ .addToggle(toggle => toggle
+ .setValue(this.plugin.settings.statusBarEntryEnabled)
+ .onChange(async (value) => {
+ this.plugin.settings.statusBarEntryEnabled = value;
+ if (value) {
+ // Enabling
+ if (this.plugin.statusBarItemEl) {
+ // for sanity
+ this.plugin.statusBarItemEl.detach()
+ }
+ this.plugin.statusBarItemEl = this.plugin.addStatusBarItem();
+ this.plugin.updateStatusBar()
+
+ } else { // disabling
+ if (this.plugin.statusBarItemEl) {
+ this.plugin.statusBarItemEl.detach()
+ }
+ }
+ await this.plugin.saveSettings();
+ }));
+
+ new Setting(containerEl)
+ .setName('Enable notifications of plugin state changes')
+ .setDesc('The plugin can show notifications about its state changes: e.g. when successfully parsed and applied'
+ + ' the custom sorting specification, or, when the parsing failed. If the notifications are disabled,'
+ + ' the only indicator of plugin state is the ribbon button icon. The developer console presents the parsing'
+ + ' error messages regardless if the notifications are enabled or not.')
+ .addToggle(toggle => toggle
+ .setValue(this.plugin.settings.notificationsEnabled)
+ .onChange(async (value) => {
+ this.plugin.settings.notificationsEnabled = value;
+ await this.plugin.saveSettings();
+ }));
+
+ new Setting(containerEl)
+ .setName('Enable notifications of plugin state changes for mobile devices only')
+ .setDesc('See above.')
+ .addToggle(toggle => toggle
+ .setValue(this.plugin.settings.mobileNotificationsEnabled)
+ .onChange(async (value) => {
+ this.plugin.settings.mobileNotificationsEnabled = value;
+ await this.plugin.saveSettings();
+ }));
+
+ new Setting(containerEl)
+ .setName('Enable File Explorer context submenu`Custom sort:`')
+ .setDesc('Gives access to operations relevant for custom sorting, e.g. applying custom sorting.')
+ .addToggle(toggle => toggle
+ .setValue(this.plugin.settings.customSortContextSubmenu)
+ .onChange(async (value) => {
+ this.plugin.settings.customSortContextSubmenu = value;
+ await this.plugin.saveSettings();
+ }));
+
+ containerEl.createEl('h2', {text: 'Bookmarks integration'});
+ const bookmarksIntegrationDescription: DocumentFragment = sanitizeHTMLToDom(
+ 'If enabled, order of files and folders in File Explorer will reflect the order '
+ + 'of bookmarked items in the bookmarks (core plugin) view. Automatically, without any '
+ + 'need for sorting configuration. At the same time, it integrates seamlessly with'
+ + ' sorting-spec:
configurations and they can nicely cooperate.'
+ + '
'
+ + 'To separate regular bookmarks from the bookmarks created for sorting, you can put '
+ + 'the latter in a separate dedicated bookmarks group. The default name of the group is '
+ + "'" + DEFAULT_SETTINGS.bookmarksGroupToConsumeAsOrderingReference + "' "
+ + 'and you can change the group name in the configuration field below.'
+ + '
'
+ + 'If left empty, all the bookmarked items will be used to impose the order in File Explorer.
'
+ + 'More information on this functionality in the '
+ + ''
+ + 'manual of this custom-sort plugin.'
+ + '
'
+ )
+
+ new Setting(containerEl)
+ .setName('Automatic integration with core Bookmarks plugin (for indirect drag & drop ordering)')
+ .setDesc(bookmarksIntegrationDescription)
+ .addToggle(toggle => toggle
+ .setValue(this.plugin.settings.automaticBookmarksIntegration)
+ .onChange(async (value) => {
+ this.plugin.settings.automaticBookmarksIntegration = value;
+ await this.plugin.saveSettings();
+ }));
+
+ new Setting(containerEl)
+ .setName('Name of the group in Bookmarks from which to read the order of items')
+ .setDesc('See above.')
+ .addText(text => text
+ .setPlaceholder('e.g. Group for sorting')
+ .setValue(this.plugin.settings.bookmarksGroupToConsumeAsOrderingReference)
+ .onChange(async (value) => {
+ value = groupNameForPath(value.trim()).trim()
+ this.plugin.settings.bookmarksGroupToConsumeAsOrderingReference = value ? pathToFlatString(normalizePath(value)) : '';
+ await this.plugin.saveSettings();
+ }));
+
+ const bookmarksIntegrationContextMenusDescription: DocumentFragment = sanitizeHTMLToDom(
+ 'Enable Custom-sort: bookmark for sorting and Custom-sort: bookmark+siblings for sorting (and related) entries '
+ + 'in context menu in File Explorer'
+ )
+ new Setting(containerEl)
+ .setName('Context menus for Bookmarks integration')
+ .setDesc(bookmarksIntegrationContextMenusDescription)
+ .addToggle(toggle => toggle
+ .setValue(this.plugin.settings.bookmarksContextMenus)
+ .onChange(async (value) => {
+ this.plugin.settings.bookmarksContextMenus = value;
+ if (value) {
+ this.plugin.settings.customSortContextSubmenu = true; // automatically enable custom sort context submenu
+ }
+ await this.plugin.saveSettings();
+ }))
+ }
+}
From 605d5026a61b0d8a4e6f296c0dbd54ef2b78ba88 Mon Sep 17 00:00:00 2001
From: SebastianMC <23032356+SebastianMC@users.noreply.github.com>
Date: Sun, 1 Sep 2024 17:07:56 +0200
Subject: [PATCH 2/4] #156 - improved support for index-note-based folder
notes: PUT ON HOLD - turned out to be not so trivial with one challenging
place - PUT ON HOLD - added inline annotations with !!! (intentionally
breaking the TS syntax)
---
src/custom-sort/custom-sort.ts | 16 ++++++++++++--
src/main.ts | 8 +++++--
src/settings.ts | 39 +++++++++++++++++++++++++++-------
3 files changed, 51 insertions(+), 12 deletions(-)
diff --git a/src/custom-sort/custom-sort.ts b/src/custom-sort/custom-sort.ts
index 1261261..686a252 100644
--- a/src/custom-sort/custom-sort.ts
+++ b/src/custom-sort/custom-sort.ts
@@ -466,12 +466,18 @@ export const determineSortingGroup = function (entry: TFile | TFolder, spec: Cus
case CustomSortGroupType.HasMetadataField:
if (group.withMetadataFieldName) {
if (ctx?._mCache) {
- // For folders - scan metadata of 'folder note'
+ // For folders - scan metadata of 'folder note' in same-name-as-folder mode
const notePathToScan: string = aFile ? entry.path : `${entry.path}/${entry.name}.md`
const frontMatterCache: FrontMatterCache | undefined = ctx._mCache.getCache(notePathToScan)?.frontmatter
const hasMetadata: boolean | undefined = frontMatterCache?.hasOwnProperty(group.withMetadataFieldName)
+ // For folders, if index-based folder note mode, scan the index file, giving it the priority
+ !!! Non trivial part: - need to know the folder-index name, this is hidden
+ !!! in plugin settings, not exposed - refactoring is a must.
+ if (aFolder && ctx?.plugin?.s) {
- if (hasMetadata) {
+ }
+
+ if (hasMetadata || folderIndexNoteHasMetadata) {
determined = true
}
}
@@ -554,6 +560,12 @@ export const determineSortingGroup = function (entry: TFile | TFolder, spec: Cus
if (isPrimaryOrderByMetadata || isSecondaryOrderByMetadata || isDerivedPrimaryByMetadata || isDerivedSecondaryByMetadata) {
if (ctx?._mCache) {
// For folders - scan metadata of 'folder note'
+ // and if index-based folder note mode, scan the index file, giving it the priority
+ !!! Non trivial part: - need to know the folder-index-note name, this is hidden
+ !!! in plugin settings, not exposed - refactoring is a must.
+ !!!
+ !!! Then challenging part - how to easily rewrite the below code to handle scanning
+ !!! of two frontMatterCaches for folders and give the priority to folder-index-note based cache, if configured
const notePathToScan: string = aFile ? entry.path : `${entry.path}/${entry.name}.md`
const frontMatterCache: FrontMatterCache | undefined = ctx._mCache.getCache(notePathToScan)?.frontmatter
if (isPrimaryOrderByMetadata) metadataValueToSortBy = frontMatterCache?.[group?.byMetadataField || group?.withMetadataFieldName || DEFAULT_METADATA_FIELD_FOR_SORTING]
diff --git a/src/main.ts b/src/main.ts
index d9d61fe..60c4fcb 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -125,10 +125,14 @@ export default class CustomSortPlugin extends Plugin {
if (aFile.name === SORTSPEC_FILE_NAME || // file name == sortspec.md ?
aFile.name === `${SORTSPEC_FILE_NAME}.md` || // file name == sortspec.md.md ?
aFile.basename === parent.name || // Folder Note mode: inside folder, same name
+
aFile.basename === this.settings.additionalSortspecFile || // when user configured _about_
aFile.name === this.settings.additionalSortspecFile || // when user configured _about_.md
aFile.path === this.settings.additionalSortspecFile || // when user configured Inbox/sort.md
- aFile.path === `${this.settings.additionalSortspecFile}.md` // when user configured Inbox/sort
+ aFile.path === `${this.settings.additionalSortspecFile}.md` || // when user configured Inbox/sort
+
+ aFile.basename === this.settings.indexNoteNameForFolderNotes || // when user configured as index
+ aFile.name === this.settings.indexNoteNameForFolderNotes // when user configured as index.md
) {
const sortingSpecTxt: string = mCache.getCache(aFile.path)?.frontmatter?.[SORTINGSPEC_YAML_KEY]
// Warning: newer Obsidian versions can return objects as well, hence the explicit check for string value
@@ -713,7 +717,7 @@ export default class CustomSortPlugin extends Plugin {
const data: any = await this.loadData() || {}
const isFreshInstall: boolean = Object.keys(data).length === 0
this.settings = Object.assign({}, DEFAULT_SETTINGS, data);
- if (requireApiVersion('1.2.0')) {
+ if (requireApiVersion('1.2.0') && isFreshInstall) {
this.settings = Object.assign(this.settings, DEFAULT_SETTING_FOR_1_2_0_UP)
}
}
diff --git a/src/settings.ts b/src/settings.ts
index de165b0..3a0d251 100644
--- a/src/settings.ts
+++ b/src/settings.ts
@@ -4,6 +4,7 @@ import CustomSortPlugin from "./main";
export interface CustomSortPluginSettings {
additionalSortspecFile: string
+ indexNoteNameForFolderNotes: string
suspended: boolean
statusBarEntryEnabled: boolean
notificationsEnabled: boolean
@@ -16,6 +17,7 @@ export interface CustomSortPluginSettings {
export const DEFAULT_SETTINGS: CustomSortPluginSettings = {
additionalSortspecFile: '',
+ indexNoteNameForFolderNotes: '',
suspended: true, // if false by default, it would be hard to handle the auto-parse after plugin install
statusBarEntryEnabled: true,
notificationsEnabled: true,
@@ -52,16 +54,10 @@ export class CustomSortSettingTab extends PluginSettingTab {
// containerEl.createEl('h2', {text: 'Settings for Custom File Explorer Sorting Plugin'});
const additionalSortspecFileDescr: DocumentFragment = sanitizeHTMLToDom(
- 'A note name or note path to scan (YAML frontmatter) for sorting specification in addition to the `sortspec` notes and Folder Notes*.'
+ 'A note name or note path to scan (YAML frontmatter) for sorting specification in addition to the `sortspec` notes and Folder Notes.'
+ '
'
+ ' The `.md` filename suffix is optional.'
- + '(*) if you employ the Index-File based approach to folder notes (as documented in '
- + 'Aidenlx Folder Note preferences'
- + ') you can enter here the index note name, e.g. _about_'
+ '
'
- + 'The Inside Folder, with Same Name Recommended mode of Folder Notes is handled automatically, no additional configuration needed.'
- + '
'
+ 'NOTE: After updating this setting remember to refresh the custom sorting via clicking on the ribbon icon or via the sort-on command'
+ ' or by restarting Obsidian or reloading the vault
'
)
@@ -70,13 +66,40 @@ export class CustomSortSettingTab extends PluginSettingTab {
.setName('Path or name of additional note(s) containing sorting specification')
.setDesc(additionalSortspecFileDescr)
.addText(text => text
- .setPlaceholder('e.g. _about_')
+ .setPlaceholder('e.g. sorting-configuration')
.setValue(this.plugin.settings.additionalSortspecFile)
.onChange(async (value) => {
this.plugin.settings.additionalSortspecFile = value.trim() ? normalizePath(value) : '';
await this.plugin.saveSettings();
}));
+ const indexNoteNameDescr: DocumentFragment = sanitizeHTMLToDom(
+ 'If you employ the Index-File based approach to folder notes (as documented in '
+ + 'Aidenlx Folder Note preferences'
+ + ') enter here the index note name, e.g. _about_ or index'
+ + '
'
+ + ' The `.md` filename suffix is optional.'
+ + '
'
+ + 'This will tell the plugin to read sorting specs and also folders metadata from these files.'
+ + '
'
+ + 'The Inside Folder, with Same Name Recommended mode of Folder Notes is handled automatically, no additional configuration needed.'
+ + '
'
+ + 'NOTE: After updating this setting remember to refresh the custom sorting via clicking on the ribbon icon or via the sort-on command'
+ + ' or by restarting Obsidian or reloading the vault
'
+ )
+
+ new Setting(containerEl)
+ .setName('Name of index note (Folder Notes support)')
+ .setDesc(indexNoteNameDescr)
+ .addText(text => text
+ .setPlaceholder('e.g. _about_ or index')
+ .setValue(this.plugin.settings.indexNoteNameForFolderNotes)
+ .onChange(async (value) => {
+ this.plugin.settings.indexNoteNameForFolderNotes = value.trim() ? normalizePath(value) : '';
+ await this.plugin.saveSettings();
+ }));
+
new Setting(containerEl)
.setName('Enable the status bar entry')
.setDesc('The status bar entry shows the label `Custom sort:ON` or `Custom sort:OFF`, representing the current state of the plugin.')
From 60a60dfb72da7766ca5a3f265ab60770e53c77aa Mon Sep 17 00:00:00 2001
From: SebastianMC <23032356+SebastianMC@users.noreply.github.com>
Date: Mon, 2 Sep 2024 00:11:29 +0200
Subject: [PATCH 3/4] #156 - improved support for index-note-based folder notes
- implementation completed, basic tests done - needs code review with a fresh
head - needs more regression tests of affected sorting methods
---
src/custom-sort-plugin.ts | 7 +++++
src/custom-sort/custom-sort.ts | 52 +++++++++++++++++++++++-----------
src/main.ts | 28 +++++++++++++++---
src/test/unit/utils.spec.ts | 20 ++++++++++++-
src/utils/utils.ts | 8 ++++++
5 files changed, 93 insertions(+), 22 deletions(-)
create mode 100644 src/custom-sort-plugin.ts
diff --git a/src/custom-sort-plugin.ts b/src/custom-sort-plugin.ts
new file mode 100644
index 0000000..858189f
--- /dev/null
+++ b/src/custom-sort-plugin.ts
@@ -0,0 +1,7 @@
+import {
+ Plugin
+} from 'obsidian'
+
+export interface CustomSortPluginAPI extends Plugin {
+ indexNoteBasename(): string|undefined
+}
diff --git a/src/custom-sort/custom-sort.ts b/src/custom-sort/custom-sort.ts
index 686a252..dc4bb9f 100644
--- a/src/custom-sort/custom-sort.ts
+++ b/src/custom-sort/custom-sort.ts
@@ -34,10 +34,11 @@ import {
import {
BookmarksPluginInterface
} from "../utils/BookmarksCorePluginSignature";
+import {CustomSortPluginAPI} from "../custom-sort-plugin";
export interface ProcessingContext {
// For internal transient use
- plugin?: Plugin // to hand over the access to App instance to the sorting engine
+ plugin?: CustomSortPluginAPI // to hand over the access to App instance to the sorting engine
_mCache?: MetadataCache
starredPluginInstance?: Starred_PluginInstance
bookmarksPluginInstance?: BookmarksPluginInterface,
@@ -371,6 +372,15 @@ export const matchGroupRegex = (theRegex: RegExpSpec, nameForMatching: string):
return [false, undefined, undefined]
}
+const mdataValueFromFMCaches = (mdataFieldName: string, fc?: FrontMatterCache, fcPrio?: FrontMatterCache): any => {
+ let prioValue = undefined
+ if (fcPrio) {
+ prioValue = fcPrio?.[mdataFieldName]
+ }
+
+ return prioValue ?? fc?.[mdataFieldName]
+}
+
export const determineSortingGroup = function (entry: TFile | TFolder, spec: CustomSortSpec, ctx?: ProcessingContext): FolderItemForSorting {
let groupIdx: number
let determined: boolean = false
@@ -468,16 +478,18 @@ export const determineSortingGroup = function (entry: TFile | TFolder, spec: Cus
if (ctx?._mCache) {
// For folders - scan metadata of 'folder note' in same-name-as-folder mode
const notePathToScan: string = aFile ? entry.path : `${entry.path}/${entry.name}.md`
- const frontMatterCache: FrontMatterCache | undefined = ctx._mCache.getCache(notePathToScan)?.frontmatter
- const hasMetadata: boolean | undefined = frontMatterCache?.hasOwnProperty(group.withMetadataFieldName)
+ let frontMatterCache: FrontMatterCache | undefined = ctx._mCache.getCache(notePathToScan)?.frontmatter
+ let hasMetadata: boolean | undefined = frontMatterCache?.hasOwnProperty(group.withMetadataFieldName)
// For folders, if index-based folder note mode, scan the index file, giving it the priority
- !!! Non trivial part: - need to know the folder-index name, this is hidden
- !!! in plugin settings, not exposed - refactoring is a must.
- if (aFolder && ctx?.plugin?.s) {
-
+ if (aFolder) {
+ const indexNoteBasename = ctx?.plugin?.indexNoteBasename()
+ if (indexNoteBasename) {
+ frontMatterCache = ctx._mCache.getCache(`${entry.path}/${indexNoteBasename}.md`)?.frontmatter
+ hasMetadata = hasMetadata || frontMatterCache?.hasOwnProperty(group.withMetadataFieldName)
+ }
}
- if (hasMetadata || folderIndexNoteHasMetadata) {
+ if (hasMetadata) {
determined = true
}
}
@@ -561,17 +573,23 @@ export const determineSortingGroup = function (entry: TFile | TFolder, spec: Cus
if (ctx?._mCache) {
// For folders - scan metadata of 'folder note'
// and if index-based folder note mode, scan the index file, giving it the priority
- !!! Non trivial part: - need to know the folder-index-note name, this is hidden
- !!! in plugin settings, not exposed - refactoring is a must.
- !!!
- !!! Then challenging part - how to easily rewrite the below code to handle scanning
- !!! of two frontMatterCaches for folders and give the priority to folder-index-note based cache, if configured
const notePathToScan: string = aFile ? entry.path : `${entry.path}/${entry.name}.md`
const frontMatterCache: FrontMatterCache | undefined = ctx._mCache.getCache(notePathToScan)?.frontmatter
- if (isPrimaryOrderByMetadata) metadataValueToSortBy = frontMatterCache?.[group?.byMetadataField || group?.withMetadataFieldName || DEFAULT_METADATA_FIELD_FOR_SORTING]
- if (isSecondaryOrderByMetadata) metadataValueSecondaryToSortBy = frontMatterCache?.[group?.byMetadataFieldSecondary || group?.withMetadataFieldName || DEFAULT_METADATA_FIELD_FOR_SORTING]
- if (isDerivedPrimaryByMetadata) metadataValueDerivedPrimaryToSortBy = frontMatterCache?.[spec.byMetadataField || DEFAULT_METADATA_FIELD_FOR_SORTING]
- if (isDerivedSecondaryByMetadata) metadataValueDerivedSecondaryToSortBy = frontMatterCache?.[spec.byMetadataFieldSecondary || DEFAULT_METADATA_FIELD_FOR_SORTING]
+ let prioFrontMatterCache: FrontMatterCache | undefined = undefined
+ if (aFolder) {
+ const indexNoteBasename = ctx?.plugin?.indexNoteBasename()
+ if (indexNoteBasename) {
+ prioFrontMatterCache = ctx._mCache.getCache(`${entry.path}/${indexNoteBasename}.md`)?.frontmatter
+ }
+ }
+ if (isPrimaryOrderByMetadata) metadataValueToSortBy =
+ mdataValueFromFMCaches (group?.byMetadataField || group?.withMetadataFieldName || DEFAULT_METADATA_FIELD_FOR_SORTING, frontMatterCache, prioFrontMatterCache)
+ if (isSecondaryOrderByMetadata) metadataValueSecondaryToSortBy =
+ mdataValueFromFMCaches (group?.byMetadataFieldSecondary || group?.withMetadataFieldName || DEFAULT_METADATA_FIELD_FOR_SORTING, frontMatterCache, prioFrontMatterCache)
+ if (isDerivedPrimaryByMetadata) metadataValueDerivedPrimaryToSortBy =
+ mdataValueFromFMCaches (spec.byMetadataField || DEFAULT_METADATA_FIELD_FOR_SORTING, frontMatterCache, prioFrontMatterCache)
+ if (isDerivedSecondaryByMetadata) metadataValueDerivedSecondaryToSortBy =
+ mdataValueFromFMCaches (spec.byMetadataFieldSecondary || DEFAULT_METADATA_FIELD_FOR_SORTING, frontMatterCache, prioFrontMatterCache)
}
}
}
diff --git a/src/main.ts b/src/main.ts
index 60c4fcb..62564c1 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -50,8 +50,13 @@ import {
getBookmarksPlugin,
groupNameForPath
} from "./utils/BookmarksCorePluginSignature";
-import {getIconFolderPlugin} from "./utils/ObsidianIconFolderPluginSignature";
-import {lastPathComponent} from "./utils/utils";
+import {
+ getIconFolderPlugin
+} from "./utils/ObsidianIconFolderPluginSignature";
+import {
+ extractBasename,
+ lastPathComponent
+} from "./utils/utils";
import {
collectSortingAndGroupingTypes,
hasOnlyByBookmarkOrStandardObsidian,
@@ -64,6 +69,7 @@ import {
DEFAULT_SETTING_FOR_1_2_0_UP,
DEFAULT_SETTINGS
} from "./settings";
+import {CustomSortPluginAPI} from "./custom-sort-plugin";
const SORTSPEC_FILE_NAME: string = 'sortspec.md'
const SORTINGSPEC_YAML_KEY: string = 'sorting-spec'
@@ -75,7 +81,10 @@ type MonkeyAroundUninstaller = () => void
type ContextMenuProvider = (item: MenuItem) => void
-export default class CustomSortPlugin extends Plugin {
+export default class CustomSortPlugin
+ extends Plugin
+ implements CustomSortPluginAPI
+{
settings: CustomSortPluginSettings
statusBarItemEl: HTMLElement
ribbonIconEl: HTMLElement // On small-screen mobile devices this is useless (ribbon is re-created on-the-fly)
@@ -725,5 +734,16 @@ export default class CustomSortPlugin extends Plugin {
async saveSettings() {
await this.saveData(this.settings);
}
-}
+ // API
+ derivedIndexNoteNameForFolderNotes: string | undefined
+ indexNoteNameForFolderNotesDerivedFrom: any
+
+ indexNoteBasename(): string | undefined {
+ if (!(this.indexNoteNameForFolderNotesDerivedFrom === this.settings.indexNoteNameForFolderNotes)) {
+ this.derivedIndexNoteNameForFolderNotes = extractBasename(this.settings.indexNoteNameForFolderNotes)
+ this.indexNoteNameForFolderNotesDerivedFrom = this.settings.indexNoteNameForFolderNotes
+ }
+ return this.derivedIndexNoteNameForFolderNotes
+ }
+}
diff --git a/src/test/unit/utils.spec.ts b/src/test/unit/utils.spec.ts
index d03ba24..1f65045 100644
--- a/src/test/unit/utils.spec.ts
+++ b/src/test/unit/utils.spec.ts
@@ -1,6 +1,7 @@
import {
lastPathComponent,
- extractParentFolderPath
+ extractParentFolderPath,
+ extractBasename
} from "../../utils/utils";
describe('lastPathComponent and extractParentFolderPath', () => {
@@ -25,3 +26,20 @@ describe('lastPathComponent and extractParentFolderPath', () => {
}
)
})
+
+describe('extractBasenane', () => {
+ const params: Array<(string|undefined)[]> = [
+ // Obvious
+ ['index', 'index'],
+ ['index.md', 'index'],
+ // Edge cases
+ ['',''],
+ [undefined,undefined],
+ ['.','.'],
+ ['.md',''],
+ ['.md.md','.md']
+ ];
+ it.each(params)('>%s< should become %s', (s: string|undefined, out: string|undefined) => {
+ expect(extractBasename(s)).toBe(out)
+ })
+})
diff --git a/src/utils/utils.ts b/src/utils/utils.ts
index 4e767d5..65d9257 100644
--- a/src/utils/utils.ts
+++ b/src/utils/utils.ts
@@ -16,3 +16,11 @@ export function extractParentFolderPath(path: string): string {
const lastPathSeparatorIdx = (path ?? '').lastIndexOf('/')
return lastPathSeparatorIdx > 0 ? path.substring(0, lastPathSeparatorIdx) : ''
}
+
+export function extractBasename (configEntry: string | undefined): string | undefined {
+ if (typeof configEntry === 'string' && configEntry.endsWith('.md')) {
+ return configEntry.slice(0, -'.md'.length)
+ } else {
+ return configEntry
+ }
+}
From efe8e13267d7c3167e78b9a02ac94f6f801fc19e Mon Sep 17 00:00:00 2001
From: SebastianMC <23032356+SebastianMC@users.noreply.github.com>
Date: Thu, 5 Sep 2024 20:01:11 +0200
Subject: [PATCH 4/4] #156 - improved support for index-note-based folder notes
- fresh head review - typos correction - comment updates for clarity
---
src/custom-sort/custom-sort.ts | 2 +-
src/test/unit/utils.spec.ts | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/custom-sort/custom-sort.ts b/src/custom-sort/custom-sort.ts
index dc4bb9f..d320f56 100644
--- a/src/custom-sort/custom-sort.ts
+++ b/src/custom-sort/custom-sort.ts
@@ -476,7 +476,7 @@ export const determineSortingGroup = function (entry: TFile | TFolder, spec: Cus
case CustomSortGroupType.HasMetadataField:
if (group.withMetadataFieldName) {
if (ctx?._mCache) {
- // For folders - scan metadata of 'folder note' in same-name-as-folder mode
+ // For folders - scan metadata of 'folder note' in same-name-as-parent-folder mode
const notePathToScan: string = aFile ? entry.path : `${entry.path}/${entry.name}.md`
let frontMatterCache: FrontMatterCache | undefined = ctx._mCache.getCache(notePathToScan)?.frontmatter
let hasMetadata: boolean | undefined = frontMatterCache?.hasOwnProperty(group.withMetadataFieldName)
diff --git a/src/test/unit/utils.spec.ts b/src/test/unit/utils.spec.ts
index 1f65045..2a60bcb 100644
--- a/src/test/unit/utils.spec.ts
+++ b/src/test/unit/utils.spec.ts
@@ -27,7 +27,7 @@ describe('lastPathComponent and extractParentFolderPath', () => {
)
})
-describe('extractBasenane', () => {
+describe('extractBasename', () => {
const params: Array<(string|undefined)[]> = [
// Obvious
['index', 'index'],
@@ -39,7 +39,7 @@ describe('extractBasenane', () => {
['.md',''],
['.md.md','.md']
];
- it.each(params)('>%s< should become %s', (s: string|undefined, out: string|undefined) => {
+ it.each(params)('>%s< should result in %s', (s: string|undefined, out: string|undefined) => {
expect(extractBasename(s)).toBe(out)
})
})