diff --git a/src/custom-sort/custom-sort.spec.ts b/src/custom-sort/custom-sort.spec.ts index dd1e398..45675c6 100644 --- a/src/custom-sort/custom-sort.spec.ts +++ b/src/custom-sort/custom-sort.spec.ts @@ -16,7 +16,6 @@ import { ObsidianIconFolder_PluginInstance, ObsidianIconFolderPlugin_Data } from "../utils/ObsidianIconFolderPluginSignature"; -import {determineBookmarkOrder} from "../utils/BookmarksCorePluginSignature"; const mockTFile = (basename: string, ext: string, size?: number, ctime?: number, mtime?: number): TFile => { return { diff --git a/src/custom-sort/custom-sort.ts b/src/custom-sort/custom-sort.ts index 36b4d57..ad47cc4 100644 --- a/src/custom-sort/custom-sort.ts +++ b/src/custom-sort/custom-sort.ts @@ -57,6 +57,8 @@ export interface FolderItemForSorting { } export type SorterFn = (a: FolderItemForSorting, b: FolderItemForSorting) => number +export type PlainSorterFn = (a: TAbstractFile, b: TAbstractFile) => number +export type PlainFileOnlySorterFn = (a: TFile, b: TFile) => number export type CollatorCompareFn = (a: string, b: string) => number // Syntax sugar @@ -129,13 +131,32 @@ let Sorters: { [key in CustomSortOrder]: SorterFn } = { [CustomSortOrder.standardObsidian]: (a: FolderItemForSorting, b: FolderItemForSorting) => CollatorCompare(a.sortString, b.sortString), }; +// OS - Obsidian Sort +const OS_alphabetical = 'alphabetical' +const OS_alphabeticalReverse = 'alphabeticalReverse' +const OS_byModifiedTime = 'byModifiedTime' +const OS_byModifiedTimeReverse = 'byModifiedTimeReverse' +const OS_byCreatedTime = 'byCreatedTime' +const OS_byCreatedTimeReverse = 'byCreatedTimeReverse' + +export const ObsidianStandardDefaultSortingName = OS_alphabetical + const StandardObsidianToCustomSort: {[key: string]: CustomSortOrder} = { - "alphabetical": CustomSortOrder.alphabetical, - "alphabeticalReverse": CustomSortOrder.alphabeticalReverse, - "byModifiedTime": CustomSortOrder.byModifiedTimeReverse, // In Obsidian labeled as 'Modified time (new to old)' - "byModifiedTimeReverse": CustomSortOrder.byModifiedTime, // In Obsidian labeled as 'Modified time (old to new)' - "byCreatedTime": CustomSortOrder.byCreatedTimeReverse, // In Obsidian labeled as 'Created time (new to old)' - "byCreatedTimeReverse": CustomSortOrder.byCreatedTime // In Obsidian labeled as 'Created time (old to new)' + [OS_alphabetical]: CustomSortOrder.alphabetical, + [OS_alphabeticalReverse]: CustomSortOrder.alphabeticalReverse, + [OS_byModifiedTime]: CustomSortOrder.byModifiedTimeReverse, // In Obsidian labeled as 'Modified time (new to old)' + [OS_byModifiedTimeReverse]: CustomSortOrder.byModifiedTime, // In Obsidian labeled as 'Modified time (old to new)' + [OS_byCreatedTime]: CustomSortOrder.byCreatedTimeReverse, // In Obsidian labeled as 'Created time (new to old)' + [OS_byCreatedTimeReverse]: CustomSortOrder.byCreatedTime // In Obsidian labeled as 'Created time (old to new)' +} + +const StandardObsidianToPlainSortFn: {[key: string]: PlainFileOnlySorterFn} = { + [OS_alphabetical]: (a: TFile, b: TFile) => CollatorCompare(a.basename, b.basename), + [OS_alphabeticalReverse]: (a: TFile, b: TFile) => -StandardObsidianToPlainSortFn[OS_alphabetical](a,b), + [OS_byModifiedTime]: (a: TFile, b: TFile) => b.stat.mtime - a.stat.mtime, + [OS_byModifiedTimeReverse]: (a: TFile, b: TFile) => -StandardObsidianToPlainSortFn[OS_byModifiedTime](a,b), + [OS_byCreatedTime]: (a: TFile, b: TFile) => b.stat.ctime - a.stat.ctime, + [OS_byCreatedTimeReverse]: (a: TFile, b: TFile) => -StandardObsidianToPlainSortFn[OS_byCreatedTime](a,b) } // Standard Obsidian comparator keeps folders in the top sorted alphabetically @@ -150,6 +171,20 @@ const StandardObsidianComparator = (order: CustomSortOrder): SorterFn => { } } +// Equivalent of StandardObsidianComparator working directly on TAbstractFile items +export const StandardPlainObsidianComparator = (order: string): PlainSorterFn => { + const fileSorterFn = StandardObsidianToPlainSortFn[order] || StandardObsidianToCustomSort[OS_alphabetical] + return (a: TAbstractFile, b: TAbstractFile): number => { + const aIsFolder: boolean = a instanceof TFolder + const bIsFolder: boolean = b instanceof TFolder + return aIsFolder || bIsFolder + ? + (aIsFolder && !bIsFolder ? -1 : (bIsFolder && !aIsFolder ? 1 : CollatorCompare(a.name,b.name))) + : + fileSorterFn(a as TFile, b as TFile); + } +} + export const getSorterFnFor = (sorting: CustomSortOrder, currentUIselectedSorting?: string): SorterFn => { if (sorting === CustomSortOrder.standardObsidian) { sorting = StandardObsidianToCustomSort[currentUIselectedSorting ?? 'alphabetical'] ?? CustomSortOrder.alphabetical @@ -545,3 +580,36 @@ export const folderSort = function (sortingSpec: CustomSortSpec, ctx: Processing this.children = items; } }; + +// Returns a sorted copy of the input array, intentionally to keep it intact +export const sortFolderItemsForBookmarking = function (items: Array, sortingSpec: CustomSortSpec|null|undefined, ctx: ProcessingContext, uiSortOrder: string): Array { + if (sortingSpec) { + const folderItemsByPath: { [key: string]: TAbstractFile } = {} + + const folderItems: Array = items.map((entry: TFile | TFolder) => { + folderItemsByPath[entry.path] = entry + const itemForSorting: FolderItemForSorting = determineSortingGroup(entry, sortingSpec, ctx) + return itemForSorting + }) + + // Finally, for advanced sorting by modified date, for some folders the modified date has to be determined + determineFolderDatesIfNeeded(folderItems, sortingSpec) + + if (ctx.bookmarksPlugin?.instance) { + determineBookmarksOrderIfNeeded(folderItems, sortingSpec, ctx.bookmarksPlugin.instance, ctx.bookmarksPlugin.groupNameForSorting) + } + + const comparator: SorterFn = getComparator(sortingSpec, uiSortOrder) + + folderItems.sort(comparator) + + const sortedItems: Array = folderItems.map((entry) => folderItemsByPath[entry.path]) + + return sortedItems + } else { // No custom sorting or the custom sort disabled - apply standard Obsidian sorting (internally 1:1 recreated implementation) + const folderItems: Array = items.map((entry: TFile | TFolder) => entry) + const plainSorterFn: PlainSorterFn = StandardPlainObsidianComparator(uiSortOrder) + folderItems.sort(plainSorterFn) + return folderItems + } +}; diff --git a/src/main.ts b/src/main.ts index f7e4c4a..a42fd96 100644 --- a/src/main.ts +++ b/src/main.ts @@ -16,9 +16,14 @@ import { Vault, WorkspaceLeaf } from 'obsidian'; import {around} from 'monkey-around'; -import {folderSort, ProcessingContext} from './custom-sort/custom-sort'; +import { + folderSort, + ObsidianStandardDefaultSortingName, + ProcessingContext, + sortFolderItemsForBookmarking +} from './custom-sort/custom-sort'; import {SortingSpecProcessor, SortSpecsCollection} from './custom-sort/sorting-spec-processor'; -import {CustomSortOrder, CustomSortSpec} from './custom-sort/custom-sort-types'; +import {CustomSortSpec} from './custom-sort/custom-sort-types'; import { addIcons, @@ -30,8 +35,14 @@ import { ICON_SORT_SUSPENDED_SYNTAX_ERROR } from "./custom-sort/icons"; import {getStarredPlugin} from "./utils/StarredPluginSignature"; -import {getBookmarksPlugin} from "./utils/BookmarksCorePluginSignature"; +import { + getBookmarksPlugin, + bookmarkFolderItem, + saveDataAndUpdateBookmarkViews, + bookmarkSiblings +} from "./utils/BookmarksCorePluginSignature"; import {getIconFolderPlugin} from "./utils/ObsidianIconFolderPluginSignature"; +import {lastPathComponent} from "./utils/utils"; interface CustomSortPluginSettings { additionalSortspecFile: string @@ -317,14 +328,25 @@ export default class CustomSortPlugin extends Plugin { item.setTitle('Custom sort: bookmark for sorting.'); item.setIcon('hashtag'); item.onClick(() => { - console.log(`custom-sort: bookmark this clicked ${source}`) + const bookmarksPlugin = getBookmarksPlugin(plugin.app) + console.log(`custom-sort: bookmark this clicked ${source} and the leaf is`) + if (bookmarksPlugin) { + bookmarkFolderItem(file, bookmarksPlugin, plugin.settings.bookmarksGroupToConsumeAsOrderingReference) + saveDataAndUpdateBookmarkViews(bookmarksPlugin, plugin.app) + } }); }; const bookmarkAllMenuItem = (item: MenuItem) => { - item.setTitle('Custom sort: bookmark all siblings for sorting.'); + item.setTitle('Custom sort: bookmark+siblings for sorting.'); item.setIcon('hashtag'); item.onClick(() => { console.log(`custom-sort: bookmark all siblings clicked ${source}`) + const bookmarksPlugin = getBookmarksPlugin(plugin.app) + if (bookmarksPlugin) { + const orderedChildren: Array = plugin.orderedFolderItemsForBookmarking(file.parent) + bookmarkSiblings(orderedChildren, bookmarksPlugin, plugin.settings.bookmarksGroupToConsumeAsOrderingReference) + saveDataAndUpdateBookmarkViews(bookmarksPlugin, plugin.app) + } }); }; @@ -358,6 +380,32 @@ export default class CustomSortPlugin extends Plugin { }) } + determineSortSpecForFolder(folderPath: string, folderName?: string): CustomSortSpec|null|undefined { + folderName = folderName ?? lastPathComponent(folderPath) + let sortSpec: CustomSortSpec | null | undefined = this.sortSpecCache?.sortSpecByPath?.[folderPath] + sortSpec = sortSpec ?? this.sortSpecCache?.sortSpecByName?.[folderName] + + if (!sortSpec && this.sortSpecCache?.sortSpecByWildcard) { + // when no sorting spec found directly by folder path, check for wildcard-based match + sortSpec = this.sortSpecCache?.sortSpecByWildcard.folderMatch(folderPath, folderName) + } + return sortSpec + } + + createProcessingContextForSorting(): ProcessingContext { + const ctx: ProcessingContext = { + _mCache: this.app.metadataCache, + starredPluginInstance: getStarredPlugin(this.app), + bookmarksPlugin: { + instance: this.settings.automaticBookmarksIntegration ? getBookmarksPlugin(this.app) : undefined, + groupNameForSorting: this.settings.bookmarksGroupToConsumeAsOrderingReference + }, + iconFolderPluginInstance: getIconFolderPlugin(this.app), + plugin: this + } + return ctx + } + // For the idea of monkey-patching credits go to https://github.com/nothingislost/obsidian-bartender patchFileExplorerFolder(patchableFileExplorer?: FileExplorerView): boolean { let plugin = this; @@ -371,10 +419,6 @@ export default class CustomSortPlugin extends Plugin { const uninstallerOfFolderSortFunctionWrapper: MonkeyAroundUninstaller = around(Folder.prototype, { sort(old: any) { return function (...args: any[]) { - console.log(this) - console.log(this.fileExplorer.sortOrder) - - // quick check for plugin status if (plugin.settings.suspended) { return old.call(this, ...args); @@ -385,38 +429,12 @@ export default class CustomSortPlugin extends Plugin { setIcon(plugin.ribbonIconEl, ICON_SORT_ENABLED_ACTIVE) } - // if custom sort is not specified, use the UI-selected const folder: TFolder = this.file - let sortSpec: CustomSortSpec | null | undefined = plugin.sortSpecCache?.sortSpecByPath?.[folder.path] - sortSpec = sortSpec ?? plugin.sortSpecCache?.sortSpecByName?.[folder.name] - - if (!sortSpec && plugin.sortSpecCache?.sortSpecByWildcard) { - // when no sorting spec found directly by folder path, check for wildcard-based match - sortSpec = plugin.sortSpecCache?.sortSpecByWildcard.folderMatch(folder.path, folder.name) - /* SM??? if (sortSpec?.defaultOrder === CustomSortOrder.standardObsidian) { - explicitlyStandardSort = true - sortSpec = null // A folder is explicitly excluded from custom sorting plugin - }*/ - } - - // TODO: ensure that explicitly configured standard sort excludes the auto-applied on-the-fly + let sortSpec: CustomSortSpec | null | undefined = plugin.determineSortSpecForFolder(folder.path, folder.name) if (sortSpec) { - console.log(`Sortspec for folder ${folder.path}`) - console.log(sortSpec) - const ctx: ProcessingContext = { - _mCache: plugin.app.metadataCache, - starredPluginInstance: getStarredPlugin(plugin.app), - bookmarksPlugin: { - instance: plugin.settings.automaticBookmarksIntegration ? getBookmarksPlugin(this.app) : undefined, - groupNameForSorting: plugin.settings.bookmarksGroupToConsumeAsOrderingReference - }, - iconFolderPluginInstance: getIconFolderPlugin(this.app), - plugin: plugin - } - return folderSort.call(this, sortSpec, ctx); + return folderSort.call(this, sortSpec, plugin.createProcessingContextForSorting()); } else { - console.log(`NO Sortspec for folder ${folder.path}`) return old.call(this, ...args); } }; @@ -429,6 +447,16 @@ export default class CustomSortPlugin extends Plugin { } } + orderedFolderItemsForBookmarking(folder: TFolder): Array { + let sortSpec: CustomSortSpec | null | undefined = undefined + if (!this.settings.suspended) { + sortSpec = this.determineSortSpecForFolder(folder.path, folder.name) + } + let uiSortOrder: string = this.getFileExplorer()?.sortOrder || ObsidianStandardDefaultSortingName + + return sortFolderItemsForBookmarking(folder.children, sortSpec, this.createProcessingContextForSorting(), uiSortOrder) + } + // Credits go to https://github.com/nothingislost/obsidian-bartender getFileExplorer(): FileExplorerView | undefined { let fileExplorer: FileExplorerView | undefined = this.app.workspace.getLeavesOfType("file-explorer")?.first() @@ -437,7 +465,6 @@ export default class CustomSortPlugin extends Plugin { } onunload() { - } updateStatusBar() { @@ -455,6 +482,10 @@ export default class CustomSortPlugin extends Plugin { } } +const pathToFlatString = (path: string): string => { + return path.replace('/','_').replace('\\', '_') +} + class CustomSortSettingTab extends PluginSettingTab { plugin: CustomSortPlugin; @@ -577,33 +608,23 @@ class CustomSortSettingTab extends PluginSettingTab { .setPlaceholder('e.g. Group for sorting') .setValue(this.plugin.settings.bookmarksGroupToConsumeAsOrderingReference) .onChange(async (value) => { - this.plugin.settings.bookmarksGroupToConsumeAsOrderingReference = value.trim(); + this.plugin.settings.bookmarksGroupToConsumeAsOrderingReference = value.trim() ? pathToFlatString(normalizePath(value)) : ''; await this.plugin.saveSettings(); })); } } - // TODO: clear bookmarks cache upon each tap on ribbon or on the command of 'sorting-on' -// TODO: clear bookmarks cache upon each context menu - before and after (maybe after is not needed, implicitlty empty after first clearing) - -// TODO: if a folder doesn't have any bookmarked items, it should remain under control of standard obsidian sorting +// TODO: clear bookmarks cache upon each context menu - before and after (maybe after is not needed, implicitly empty after first clearing) // TODO: in discussion sections add (and pin) announcement "DRAG & DROP ORDERING AVAILABLE VIA THE BOOKMARKS CORE PLUGIN INTEGRATION" // TODO: in community, add update message with announcement of drag & drop support via Bookmarks plugin -// TODO: if folder has explicit sorting: standard, don't apply bookmarks +// TODO: context menu only if bookmarks plugin enabled and new setting (yet to be exposed) doesn't disable it -// TODO: fix error -// bookmarks integration - for root folder and for other folders -// (check for the case: -// target-folder: /* -// sorting: standard +// TODO: defensive programming with ?. and equivalents to protect against crash if Obsidian API changes +// Better the plugin to fail an operation than crash with errors -// TODO: unbookmarked items in partially bookmarked -> can it apply the system sort ??? - -// TODO: unblock syntax 'sorting: standard' also for groups --> since I have access to currently configured sorting :-) - -// TODO: bug? On auto-bookmark integration strange behavior +// TODO: remove console.log (many places added) diff --git a/src/types/types.d.ts b/src/types/types.d.ts index ef295f5..22ea304 100644 --- a/src/types/types.d.ts +++ b/src/types/types.d.ts @@ -53,5 +53,7 @@ declare module 'obsidian' { createFolderDom(folder: TFolder): FileExplorerFolder; requestSort(): void; + + sortOrder: string } } diff --git a/src/utils/BookmarksCorePluginSignature.ts b/src/utils/BookmarksCorePluginSignature.ts index 8ca6b10..394670a 100644 --- a/src/utils/BookmarksCorePluginSignature.ts +++ b/src/utils/BookmarksCorePluginSignature.ts @@ -1,7 +1,10 @@ -import {App, InstalledPlugin, PluginInstance} from "obsidian"; +import {App, InstalledPlugin, Plugin, PluginInstance, TAbstractFile, TFolder} from "obsidian"; +import {lastPathComponent} from "./utils"; const BookmarksPlugin_getBookmarks_methodName = 'getBookmarks' +const BookmarksPlugin_items_collectionName = 'items' + type Path = string // Only relevant types of bookmarked items considered here @@ -16,20 +19,23 @@ interface BookmarkWithPath { interface BookmarkedFile { type: 'file' path: Path - subpath?: string // Anchor within the file + subpath?: string // Anchor within the file (heading and/or block ref) title?: string + ctime: number } interface BookmarkedFolder { type: 'folder' path: Path title?: string + ctime: number } interface BookmarkedGroup { type: 'group' items: Array title?: string + ctime: number } export type BookmarkedItemPath = string @@ -37,6 +43,7 @@ export type BookmarkedItemPath = string export interface OrderedBookmarkedItem { file: boolean folder: boolean + group: boolean path: BookmarkedItemPath order: number } @@ -47,6 +54,8 @@ interface OrderedBookmarks { export interface Bookmarks_PluginInstance extends PluginInstance { [BookmarksPlugin_getBookmarks_methodName]: () => Array | undefined + [BookmarksPlugin_items_collectionName]: Array + saveData(): void } let bookmarksCache: OrderedBookmarks | undefined = undefined @@ -86,48 +95,55 @@ export const getBookmarksPlugin = (app?: App): Bookmarks_PluginInstance | undefi } } -type TraverseCallback = (item: BookmarkedItem, groupPath: string) => boolean | void +type TraverseCallback = (item: BookmarkedItem, parentsGroupsPath: string) => boolean | void const traverseBookmarksCollection = (items: Array, callback: TraverseCallback) => { - const recursiveTraversal = (collection: Array, groupPath: string) => { + const recursiveTraversal = (collection: Array, groupsPath: string) => { for (let idx = 0, collectionRef = collection; idx < collectionRef.length; idx++) { const item = collectionRef[idx]; - if (callback(item, groupPath)) return; - if ('group' === item.type) recursiveTraversal(item.items, `${groupPath}${groupPath ? '/' : ''}${item.title}`); + if (callback(item, groupsPath)) return; + if ('group' === item.type) recursiveTraversal(item.items, `${groupsPath}${groupsPath?'/':''}${item.title}`); } }; recursiveTraversal(items, ''); } -const getOrderedBookmarks = (plugin: Bookmarks_PluginInstance, bookmarksGroup?: string): OrderedBookmarks | undefined => { - const bookmarks: Array | undefined = plugin?.[BookmarksPlugin_getBookmarks_methodName]() +const getOrderedBookmarks = (plugin: Bookmarks_PluginInstance, bookmarksGroupName?: string): OrderedBookmarks | undefined => { + console.log(`Populating bookmarks cache with group scope ${bookmarksGroupName}`) + let bookmarks: Array | undefined = plugin?.[BookmarksPlugin_getBookmarks_methodName]() if (bookmarks) { - const orderedBookmarks: OrderedBookmarks = {} - let order: number = 0 - const groupNamePrefix: string = bookmarksGroup ? `${bookmarksGroup}/` : '' - const consumeItem = (item: BookmarkedItem, groupPath: string) => { - if (groupNamePrefix && !groupPath.startsWith(groupNamePrefix)) { - return - } - const isFile: boolean = item.type === 'file' - const isAnchor: boolean = isFile && !!(item as BookmarkedFile).subpath - const isFolder: boolean = item.type === 'folder' - if ((isFile && !isAnchor) || isFolder) { - const path = (item as BookmarkWithPath).path - // Consume only the first occurrence of a path in bookmarks, even if many duplicates can exist - const alreadyConsumed = orderedBookmarks[path] - if (!alreadyConsumed) { - orderedBookmarks[path] = { - path: path, - order: order++, - file: isFile, - folder: isFile + if (bookmarksGroupName) { + const bookmarksGroup: BookmarkedGroup|undefined = bookmarks.find( + (item) => item.type === 'group' && item.title === bookmarksGroupName) as BookmarkedGroup + bookmarks = bookmarksGroup ? bookmarksGroup.items : undefined + } + if (bookmarks) { + const orderedBookmarks: OrderedBookmarks = {} + let order: number = 0 + const consumeItem = (item: BookmarkedItem, parentGroupsPath: string) => { + const isFile: boolean = item.type === 'file' + const isAnchor: boolean = isFile && !!(item as BookmarkedFile).subpath + const isFolder: boolean = item.type === 'folder' + const isGroup: boolean = item.type === 'group' + if ((isFile && !isAnchor) || isFolder || isGroup) { + const pathOfGroup: string = `${parentGroupsPath}${parentGroupsPath?'/':''}${item.title}` + const path = isGroup ? pathOfGroup : (item as BookmarkWithPath).path + // Consume only the first occurrence of a path in bookmarks, even if many duplicates can exist + const alreadyConsumed = orderedBookmarks[path] + if (!alreadyConsumed) { + orderedBookmarks[path] = { + path: path, + order: order++, + file: isFile, + folder: isFile, + group: isGroup + } } } } + traverseBookmarksCollection(bookmarks, consumeItem) + return orderedBookmarks } - traverseBookmarksCollection(bookmarks, consumeItem) - return orderedBookmarks } } @@ -145,3 +161,57 @@ export const determineBookmarkOrder = (path: string, plugin: Bookmarks_PluginIns return (bookmarkedItemPosition !== undefined && bookmarkedItemPosition >= 0) ? (bookmarkedItemPosition + 1) : undefined } + +// EXPERIMENTAL - operates on internal structures of core Bookmarks plugin + +const createBookmarkFileEntry = (path: string): BookmarkedFile => { + return { type: "file", ctime: Date.now(), path: path } +} + +const createBookmarkGroupEntry = (title: string): BookmarkedGroup => { + return { type: "group", ctime: Date.now(), items: [], title: title } +} + +export const bookmarkFolderItem = (item: TAbstractFile, plugin: Bookmarks_PluginInstance, bookmarksGroup?: string) => { + bookmarkSiblings([item], plugin, bookmarksGroup) +} + +export const bookmarkSiblings = (siblings: Array, plugin: Bookmarks_PluginInstance, bookmarksGroup?: string) => { + let items = plugin[BookmarksPlugin_items_collectionName] + + if (siblings.length === 0) return // for sanity + + const parentPathComponents: Array = siblings[0].path.split('/')! + parentPathComponents.pop() + + if (bookmarksGroup) { + parentPathComponents.unshift(bookmarksGroup) + } + + parentPathComponents.forEach((pathSegment) => { + let group: BookmarkedGroup|undefined = items.find((it) => it.type === 'group' && it.title === pathSegment) as BookmarkedGroup + if (!group) { + group = createBookmarkGroupEntry(pathSegment) + items.push(group) + } + items = group.items + }) + + siblings.forEach((aSibling) => { + const siblingName = lastPathComponent(aSibling.path) + if (!items.find((it) => + ((it.type === 'folder' || it.type === 'file') && it.path === aSibling.path) || + (it.type === 'group' && it.title === siblingName))) { + const newEntry: BookmarkedItem = (aSibling instanceof TFolder) ? createBookmarkGroupEntry(siblingName) : createBookmarkFileEntry(aSibling.path) + items.push(newEntry) + } + }); +} + +export const saveDataAndUpdateBookmarkViews = (plugin: Bookmarks_PluginInstance, app: App) => { + plugin.saveData() + const bookmarksLeafs = app.workspace.getLeavesOfType('bookmarks') + bookmarksLeafs?.forEach((leaf) => { + (leaf.view as any)?.update?.() + }) +} diff --git a/src/utils/utils.ts b/src/utils/utils.ts index db8a783..e833cbf 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -6,3 +6,8 @@ export function isDefined(o: any): boolean { export function last(o: Array): T | undefined { return o?.length > 0 ? o[o.length - 1] : undefined } + +export function lastPathComponent(path: string): string { + const pathComponents = (path ?? '').split('/') + return pathComponents.pop()! +}