#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
This commit is contained in:
parent
605d5026a6
commit
60a60dfb72
|
@ -0,0 +1,7 @@
|
||||||
|
import {
|
||||||
|
Plugin
|
||||||
|
} from 'obsidian'
|
||||||
|
|
||||||
|
export interface CustomSortPluginAPI extends Plugin {
|
||||||
|
indexNoteBasename(): string|undefined
|
||||||
|
}
|
|
@ -34,10 +34,11 @@ import {
|
||||||
import {
|
import {
|
||||||
BookmarksPluginInterface
|
BookmarksPluginInterface
|
||||||
} from "../utils/BookmarksCorePluginSignature";
|
} from "../utils/BookmarksCorePluginSignature";
|
||||||
|
import {CustomSortPluginAPI} from "../custom-sort-plugin";
|
||||||
|
|
||||||
export interface ProcessingContext {
|
export interface ProcessingContext {
|
||||||
// For internal transient use
|
// 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
|
_mCache?: MetadataCache
|
||||||
starredPluginInstance?: Starred_PluginInstance
|
starredPluginInstance?: Starred_PluginInstance
|
||||||
bookmarksPluginInstance?: BookmarksPluginInterface,
|
bookmarksPluginInstance?: BookmarksPluginInterface,
|
||||||
|
@ -371,6 +372,15 @@ export const matchGroupRegex = (theRegex: RegExpSpec, nameForMatching: string):
|
||||||
return [false, undefined, undefined]
|
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 {
|
export const determineSortingGroup = function (entry: TFile | TFolder, spec: CustomSortSpec, ctx?: ProcessingContext): FolderItemForSorting {
|
||||||
let groupIdx: number
|
let groupIdx: number
|
||||||
let determined: boolean = false
|
let determined: boolean = false
|
||||||
|
@ -468,16 +478,18 @@ export const determineSortingGroup = function (entry: TFile | TFolder, spec: Cus
|
||||||
if (ctx?._mCache) {
|
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-folder mode
|
||||||
const notePathToScan: string = aFile ? entry.path : `${entry.path}/${entry.name}.md`
|
const notePathToScan: string = aFile ? entry.path : `${entry.path}/${entry.name}.md`
|
||||||
const frontMatterCache: FrontMatterCache | undefined = ctx._mCache.getCache(notePathToScan)?.frontmatter
|
let frontMatterCache: FrontMatterCache | undefined = ctx._mCache.getCache(notePathToScan)?.frontmatter
|
||||||
const hasMetadata: boolean | undefined = frontMatterCache?.hasOwnProperty(group.withMetadataFieldName)
|
let hasMetadata: boolean | undefined = frontMatterCache?.hasOwnProperty(group.withMetadataFieldName)
|
||||||
// For folders, if index-based folder note mode, scan the index file, giving it the priority
|
// 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
|
if (aFolder) {
|
||||||
!!! in plugin settings, not exposed - refactoring is a must.
|
const indexNoteBasename = ctx?.plugin?.indexNoteBasename()
|
||||||
if (aFolder && ctx?.plugin?.s) {
|
if (indexNoteBasename) {
|
||||||
|
frontMatterCache = ctx._mCache.getCache(`${entry.path}/${indexNoteBasename}.md`)?.frontmatter
|
||||||
|
hasMetadata = hasMetadata || frontMatterCache?.hasOwnProperty(group.withMetadataFieldName)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasMetadata || folderIndexNoteHasMetadata) {
|
if (hasMetadata) {
|
||||||
determined = true
|
determined = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -561,17 +573,23 @@ export const determineSortingGroup = function (entry: TFile | TFolder, spec: Cus
|
||||||
if (ctx?._mCache) {
|
if (ctx?._mCache) {
|
||||||
// For folders - scan metadata of 'folder note'
|
// For folders - scan metadata of 'folder note'
|
||||||
// and if index-based folder note mode, scan the index file, giving it the priority
|
// 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 notePathToScan: string = aFile ? entry.path : `${entry.path}/${entry.name}.md`
|
||||||
const frontMatterCache: FrontMatterCache | undefined = ctx._mCache.getCache(notePathToScan)?.frontmatter
|
const frontMatterCache: FrontMatterCache | undefined = ctx._mCache.getCache(notePathToScan)?.frontmatter
|
||||||
if (isPrimaryOrderByMetadata) metadataValueToSortBy = frontMatterCache?.[group?.byMetadataField || group?.withMetadataFieldName || DEFAULT_METADATA_FIELD_FOR_SORTING]
|
let prioFrontMatterCache: FrontMatterCache | undefined = undefined
|
||||||
if (isSecondaryOrderByMetadata) metadataValueSecondaryToSortBy = frontMatterCache?.[group?.byMetadataFieldSecondary || group?.withMetadataFieldName || DEFAULT_METADATA_FIELD_FOR_SORTING]
|
if (aFolder) {
|
||||||
if (isDerivedPrimaryByMetadata) metadataValueDerivedPrimaryToSortBy = frontMatterCache?.[spec.byMetadataField || DEFAULT_METADATA_FIELD_FOR_SORTING]
|
const indexNoteBasename = ctx?.plugin?.indexNoteBasename()
|
||||||
if (isDerivedSecondaryByMetadata) metadataValueDerivedSecondaryToSortBy = frontMatterCache?.[spec.byMetadataFieldSecondary || DEFAULT_METADATA_FIELD_FOR_SORTING]
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
28
src/main.ts
28
src/main.ts
|
@ -50,8 +50,13 @@ import {
|
||||||
getBookmarksPlugin,
|
getBookmarksPlugin,
|
||||||
groupNameForPath
|
groupNameForPath
|
||||||
} from "./utils/BookmarksCorePluginSignature";
|
} from "./utils/BookmarksCorePluginSignature";
|
||||||
import {getIconFolderPlugin} from "./utils/ObsidianIconFolderPluginSignature";
|
import {
|
||||||
import {lastPathComponent} from "./utils/utils";
|
getIconFolderPlugin
|
||||||
|
} from "./utils/ObsidianIconFolderPluginSignature";
|
||||||
|
import {
|
||||||
|
extractBasename,
|
||||||
|
lastPathComponent
|
||||||
|
} from "./utils/utils";
|
||||||
import {
|
import {
|
||||||
collectSortingAndGroupingTypes,
|
collectSortingAndGroupingTypes,
|
||||||
hasOnlyByBookmarkOrStandardObsidian,
|
hasOnlyByBookmarkOrStandardObsidian,
|
||||||
|
@ -64,6 +69,7 @@ import {
|
||||||
DEFAULT_SETTING_FOR_1_2_0_UP,
|
DEFAULT_SETTING_FOR_1_2_0_UP,
|
||||||
DEFAULT_SETTINGS
|
DEFAULT_SETTINGS
|
||||||
} from "./settings";
|
} from "./settings";
|
||||||
|
import {CustomSortPluginAPI} from "./custom-sort-plugin";
|
||||||
|
|
||||||
const SORTSPEC_FILE_NAME: string = 'sortspec.md'
|
const SORTSPEC_FILE_NAME: string = 'sortspec.md'
|
||||||
const SORTINGSPEC_YAML_KEY: string = 'sorting-spec'
|
const SORTINGSPEC_YAML_KEY: string = 'sorting-spec'
|
||||||
|
@ -75,7 +81,10 @@ type MonkeyAroundUninstaller = () => void
|
||||||
|
|
||||||
type ContextMenuProvider = (item: MenuItem) => void
|
type ContextMenuProvider = (item: MenuItem) => void
|
||||||
|
|
||||||
export default class CustomSortPlugin extends Plugin {
|
export default class CustomSortPlugin
|
||||||
|
extends Plugin
|
||||||
|
implements CustomSortPluginAPI
|
||||||
|
{
|
||||||
settings: CustomSortPluginSettings
|
settings: CustomSortPluginSettings
|
||||||
statusBarItemEl: HTMLElement
|
statusBarItemEl: HTMLElement
|
||||||
ribbonIconEl: HTMLElement // On small-screen mobile devices this is useless (ribbon is re-created on-the-fly)
|
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() {
|
async saveSettings() {
|
||||||
await this.saveData(this.settings);
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import {
|
import {
|
||||||
lastPathComponent,
|
lastPathComponent,
|
||||||
extractParentFolderPath
|
extractParentFolderPath,
|
||||||
|
extractBasename
|
||||||
} from "../../utils/utils";
|
} from "../../utils/utils";
|
||||||
|
|
||||||
describe('lastPathComponent and extractParentFolderPath', () => {
|
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)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
|
@ -16,3 +16,11 @@ export function extractParentFolderPath(path: string): string {
|
||||||
const lastPathSeparatorIdx = (path ?? '').lastIndexOf('/')
|
const lastPathSeparatorIdx = (path ?? '').lastIndexOf('/')
|
||||||
return lastPathSeparatorIdx > 0 ? path.substring(0, lastPathSeparatorIdx) : ''
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue