Merge branch '171-poc-metadata-value-extractors-idea' into nov-2024-refactoring-regroup-sorting-structures
# Conflicts: # manifest.json # src/test/unit/sorting-spec-processor.spec.ts
This commit is contained in:
commit
6a47c51182
15
package.json
15
package.json
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "obsidian-custom-sort",
|
"name": "obsidian-custom-sort",
|
||||||
"version": "2.1.15",
|
"version": "3.0.0",
|
||||||
"description": "Custom Sort plugin for Obsidian (https://obsidian.md)",
|
"description": "Custom Sort plugin for Obsidian (https://obsidian.md)",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -24,12 +24,11 @@
|
||||||
"builtin-modules": "3.3.0",
|
"builtin-modules": "3.3.0",
|
||||||
"esbuild": "0.17.3",
|
"esbuild": "0.17.3",
|
||||||
"eslint": "^8.29.0",
|
"eslint": "^8.29.0",
|
||||||
"jest": "^28.1.1",
|
"jest": "^29.7.0",
|
||||||
"monkey-around": "^2.3.0",
|
"monkey-around": "^3.0.0",
|
||||||
"obsidian": "^0.15.4",
|
"obsidian": "^1.7.2",
|
||||||
"obsidian-1.4.11": "npm:obsidian@1.4.11",
|
"ts-jest": "^29.2.5",
|
||||||
"ts-jest": "^28.0.5",
|
"tslib": "2.8.1",
|
||||||
"tslib": "2.4.0",
|
"typescript": "5.7.2"
|
||||||
"typescript": "4.7.4"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ export enum CustomSortGroupType {
|
||||||
ExactSuffix,
|
ExactSuffix,
|
||||||
ExactHeadAndTail, // Like W...n or Un...ed, which is shorter variant of typing the entire title
|
ExactHeadAndTail, // Like W...n or Un...ed, which is shorter variant of typing the entire title
|
||||||
HasMetadataField, // Notes (or folder's notes) containing a specific metadata field
|
HasMetadataField, // Notes (or folder's notes) containing a specific metadata field
|
||||||
StarredOnly,
|
|
||||||
BookmarkedOnly,
|
BookmarkedOnly,
|
||||||
HasIcon
|
HasIcon
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,6 @@ export interface HasSortingTypes {
|
||||||
|
|
||||||
export interface HasGroupingTypes {
|
export interface HasGroupingTypes {
|
||||||
byBookmarks: number
|
byBookmarks: number
|
||||||
byStarred: number
|
|
||||||
byIcon: number
|
byIcon: number
|
||||||
total: number
|
total: number
|
||||||
}
|
}
|
||||||
|
@ -31,10 +30,6 @@ export const checkByBookmark = (has: HasSortingOrGrouping, order?: CustomSortOrd
|
||||||
(order === CustomSortOrder.byBookmarkOrder || order === CustomSortOrder.byBookmarkOrderReverse) && has.sorting.byBookmarks++;
|
(order === CustomSortOrder.byBookmarkOrder || order === CustomSortOrder.byBookmarkOrderReverse) && has.sorting.byBookmarks++;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const checkByStarred = (has: HasSortingOrGrouping, order?: CustomSortOrder, groupType?: CustomSortGroupType ) => {
|
|
||||||
groupType === CustomSortGroupType.StarredOnly && has.grouping.byStarred++;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const checkByIcon = (has: HasSortingOrGrouping, order?: CustomSortOrder, groupType?: CustomSortGroupType ) => {
|
export const checkByIcon = (has: HasSortingOrGrouping, order?: CustomSortOrder, groupType?: CustomSortGroupType ) => {
|
||||||
groupType === CustomSortGroupType.HasIcon && has.grouping.byIcon++;
|
groupType === CustomSortGroupType.HasIcon && has.grouping.byIcon++;
|
||||||
}
|
}
|
||||||
|
@ -45,7 +40,6 @@ export const checkStandardObsidian = (has: HasSortingOrGrouping, order?: CustomS
|
||||||
|
|
||||||
export const doCheck = (has: HasSortingOrGrouping, order?: CustomSortOrder, groupType?: CustomSortGroupType) => {
|
export const doCheck = (has: HasSortingOrGrouping, order?: CustomSortOrder, groupType?: CustomSortGroupType) => {
|
||||||
checkByBookmark(has, order, groupType)
|
checkByBookmark(has, order, groupType)
|
||||||
checkByStarred(has, order, groupType)
|
|
||||||
checkByIcon(has, order, groupType)
|
checkByIcon(has, order, groupType)
|
||||||
checkStandardObsidian(has, order, groupType)
|
checkStandardObsidian(has, order, groupType)
|
||||||
|
|
||||||
|
@ -56,7 +50,7 @@ export const doCheck = (has: HasSortingOrGrouping, order?: CustomSortOrder, grou
|
||||||
export const collectSortingAndGroupingTypes = (sortSpec?: CustomSortSpec|null): HasSortingOrGrouping => {
|
export const collectSortingAndGroupingTypes = (sortSpec?: CustomSortSpec|null): HasSortingOrGrouping => {
|
||||||
const has: HasSortingOrGrouping = {
|
const has: HasSortingOrGrouping = {
|
||||||
grouping: {
|
grouping: {
|
||||||
byIcon: 0, byStarred: 0, byBookmarks: 0, total: 0
|
byIcon: 0, byBookmarks: 0, total: 0
|
||||||
},
|
},
|
||||||
sorting: {
|
sorting: {
|
||||||
byBookmarks: 0, standardObsidian: 0, total: 0
|
byBookmarks: 0, standardObsidian: 0, total: 0
|
||||||
|
|
|
@ -1,17 +1,11 @@
|
||||||
import {
|
import {
|
||||||
FrontMatterCache,
|
FrontMatterCache,
|
||||||
MetadataCache,
|
MetadataCache,
|
||||||
Plugin,
|
|
||||||
requireApiVersion,
|
|
||||||
TAbstractFile,
|
TAbstractFile,
|
||||||
TFile,
|
TFile,
|
||||||
TFolder,
|
TFolder,
|
||||||
Vault
|
Vault
|
||||||
} from 'obsidian';
|
} from 'obsidian';
|
||||||
import {
|
|
||||||
determineStarredStatusOf,
|
|
||||||
Starred_PluginInstance
|
|
||||||
} from '../utils/StarredPluginSignature';
|
|
||||||
import {
|
import {
|
||||||
determineIconOf,
|
determineIconOf,
|
||||||
ObsidianIconFolder_PluginInstance
|
ObsidianIconFolder_PluginInstance
|
||||||
|
@ -42,7 +36,6 @@ export interface ProcessingContext {
|
||||||
// For internal transient use
|
// For internal transient use
|
||||||
plugin?: CustomSortPluginAPI // 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
|
|
||||||
bookmarksPluginInstance?: BookmarksPluginInterface,
|
bookmarksPluginInstance?: BookmarksPluginInterface,
|
||||||
iconFolderPluginInstance?: ObsidianIconFolder_PluginInstance
|
iconFolderPluginInstance?: ObsidianIconFolder_PluginInstance
|
||||||
}
|
}
|
||||||
|
@ -498,14 +491,6 @@ export const determineSortingGroup = function (entry: TFile | TFolder, spec: Cus
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case CustomSortGroupType.StarredOnly:
|
|
||||||
if (ctx?.starredPluginInstance) {
|
|
||||||
const starred: boolean = determineStarredStatusOf(entry, aFile, ctx.starredPluginInstance)
|
|
||||||
if (starred) {
|
|
||||||
determined = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break
|
|
||||||
case CustomSortGroupType.BookmarkedOnly:
|
case CustomSortGroupType.BookmarkedOnly:
|
||||||
if (ctx?.bookmarksPluginInstance) {
|
if (ctx?.bookmarksPluginInstance) {
|
||||||
const bookmarkOrder: number | undefined = ctx?.bookmarksPluginInstance.determineBookmarkOrder(entry.path)
|
const bookmarkOrder: number | undefined = ctx?.bookmarksPluginInstance.determineBookmarkOrder(entry.path)
|
||||||
|
@ -751,28 +736,8 @@ export const determineBookmarksOrderIfNeeded = (folderItems: Array<FolderItemFor
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function is a replacement for the Obsidian File Explorer function sort(...) up to Obsidian 1.6.0
|
export const getSortedFolderItems = function (sortedFolder: TFolder, sortingSpec: CustomSortSpec, ctx: ProcessingContext) {
|
||||||
// when a major refactoring of sorting mechanics happened
|
const sortOrder = this.sortOrder // this is bound to FileExplorer Obsidian component
|
||||||
export const folderSort_vUpTo_1_6_0 = function (sortingSpec: CustomSortSpec, ctx: ProcessingContext) {
|
|
||||||
|
|
||||||
const fileExplorerView = this.fileExplorer ?? this.view // this.view replaces the former since 1.5.4 insider build
|
|
||||||
const folderUnderSort = this.file as TFolder
|
|
||||||
const sortOrder = this.sortOrder
|
|
||||||
const allFileItemsCollection = fileExplorerView.fileItems
|
|
||||||
|
|
||||||
const items = folderSortCore(folderUnderSort, sortOrder, sortingSpec, allFileItemsCollection, ctx)
|
|
||||||
|
|
||||||
if (requireApiVersion && requireApiVersion("0.15.0")) {
|
|
||||||
this.vChildren.setChildren(items);
|
|
||||||
} else {
|
|
||||||
this.children = items;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This function is a replacement for the Obsidian File Explorer function getSortedFolderItems(...)
|
|
||||||
// which first appeared in Obsidian 1.6.0 and simplified a bit the plugin integration point
|
|
||||||
export const getSortedFolderItems_vFrom_1_6_0 = function (sortedFolder: TFolder, sortingSpec: CustomSortSpec, ctx: ProcessingContext) {
|
|
||||||
const sortOrder = this.sortOrder
|
|
||||||
const allFileItemsCollection = this.fileItems
|
const allFileItemsCollection = this.fileItems
|
||||||
return folderSortCore(sortedFolder, sortOrder, sortingSpec, allFileItemsCollection, ctx)
|
return folderSortCore(sortedFolder, sortOrder, sortingSpec, allFileItemsCollection, ctx)
|
||||||
}
|
}
|
||||||
|
|
|
@ -271,8 +271,6 @@ const MetadataFieldIndicatorLexeme: string = 'with-metadata:'
|
||||||
|
|
||||||
const BookmarkedItemIndicatorLexeme: string = 'bookmarked:'
|
const BookmarkedItemIndicatorLexeme: string = 'bookmarked:'
|
||||||
|
|
||||||
const StarredItemsIndicatorLexeme: string = 'starred:'
|
|
||||||
|
|
||||||
const IconIndicatorLexeme: string = 'with-icon:'
|
const IconIndicatorLexeme: string = 'with-icon:'
|
||||||
|
|
||||||
const CommentPrefix: string = '//'
|
const CommentPrefix: string = '//'
|
||||||
|
@ -1720,13 +1718,6 @@ export class SortingSpecProcessor {
|
||||||
foldersOnly: spec.foldersOnly,
|
foldersOnly: spec.foldersOnly,
|
||||||
matchFilenameWithExt: spec.matchFilenameWithExt
|
matchFilenameWithExt: spec.matchFilenameWithExt
|
||||||
}
|
}
|
||||||
} else if (theOnly.startsWith(StarredItemsIndicatorLexeme)) {
|
|
||||||
return {
|
|
||||||
type: CustomSortGroupType.StarredOnly,
|
|
||||||
filesOnly: spec.filesOnly,
|
|
||||||
foldersOnly: spec.foldersOnly,
|
|
||||||
matchFilenameWithExt: spec.matchFilenameWithExt
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// For non-three dots single text line assume exact match group
|
// For non-three dots single text line assume exact match group
|
||||||
return {
|
return {
|
||||||
|
|
62
src/main.ts
62
src/main.ts
|
@ -1,19 +1,12 @@
|
||||||
import {
|
import {
|
||||||
apiVersion,
|
|
||||||
App,
|
|
||||||
FileExplorerView,
|
FileExplorerView,
|
||||||
Menu,
|
Menu,
|
||||||
MenuItem,
|
MenuItem,
|
||||||
MetadataCache,
|
MetadataCache,
|
||||||
normalizePath,
|
|
||||||
Notice,
|
Notice,
|
||||||
Platform,
|
Platform,
|
||||||
Plugin,
|
Plugin,
|
||||||
PluginSettingTab,
|
|
||||||
requireApiVersion,
|
|
||||||
sanitizeHTMLToDom,
|
|
||||||
setIcon,
|
setIcon,
|
||||||
Setting,
|
|
||||||
TAbstractFile,
|
TAbstractFile,
|
||||||
TFile,
|
TFile,
|
||||||
TFolder,
|
TFolder,
|
||||||
|
@ -21,8 +14,7 @@ import {
|
||||||
} from 'obsidian';
|
} from 'obsidian';
|
||||||
import {around} from 'monkey-around';
|
import {around} from 'monkey-around';
|
||||||
import {
|
import {
|
||||||
folderSort_vUpTo_1_6_0,
|
getSortedFolderItems,
|
||||||
getSortedFolderItems_vFrom_1_6_0,
|
|
||||||
ObsidianStandardDefaultSortingName,
|
ObsidianStandardDefaultSortingName,
|
||||||
ProcessingContext,
|
ProcessingContext,
|
||||||
sortFolderItemsForBookmarking
|
sortFolderItemsForBookmarking
|
||||||
|
@ -44,7 +36,6 @@ import {
|
||||||
ICON_SORT_SUSPENDED_GENERAL_ERROR,
|
ICON_SORT_SUSPENDED_GENERAL_ERROR,
|
||||||
ICON_SORT_SUSPENDED_SYNTAX_ERROR
|
ICON_SORT_SUSPENDED_SYNTAX_ERROR
|
||||||
} from "./custom-sort/icons";
|
} from "./custom-sort/icons";
|
||||||
import {getStarredPlugin} from "./utils/StarredPluginSignature";
|
|
||||||
import {
|
import {
|
||||||
BookmarksPluginInterface,
|
BookmarksPluginInterface,
|
||||||
getBookmarksPlugin,
|
getBookmarksPlugin,
|
||||||
|
@ -124,7 +115,7 @@ export default class CustomSortPlugin
|
||||||
if (failed) return
|
if (failed) return
|
||||||
if (file instanceof TFile) {
|
if (file instanceof TFile) {
|
||||||
const aFile: TFile = file as TFile
|
const aFile: TFile = file as TFile
|
||||||
const parent: TFolder = aFile.parent
|
const parent: TFolder = aFile.parent!
|
||||||
// Read sorting spec from three sources of equal priority:
|
// Read sorting spec from three sources of equal priority:
|
||||||
// - files with designated predefined name
|
// - files with designated predefined name
|
||||||
// - files with the same name as parent folders (aka folder notes), e.g.: References/References.md
|
// - files with the same name as parent folders (aka folder notes), e.g.: References/References.md
|
||||||
|
@ -179,16 +170,9 @@ export default class CustomSortPlugin
|
||||||
checkFileExplorerIsAvailableAndPatchable(logWarning: boolean = true): FileExplorerView | undefined {
|
checkFileExplorerIsAvailableAndPatchable(logWarning: boolean = true): FileExplorerView | undefined {
|
||||||
let fileExplorerView: FileExplorerView | undefined = this.getFileExplorer()
|
let fileExplorerView: FileExplorerView | undefined = this.getFileExplorer()
|
||||||
if (fileExplorerView && typeof fileExplorerView.requestSort === 'function') {
|
if (fileExplorerView && typeof fileExplorerView.requestSort === 'function') {
|
||||||
// The plugin integration points changed with Obsidian 1.6.0 hence the patchability-check should also be Obsidian version aware
|
|
||||||
if (requireApiVersion && requireApiVersion("1.6.0")) {
|
|
||||||
if (typeof fileExplorerView.getSortedFolderItems === 'function') {
|
if (typeof fileExplorerView.getSortedFolderItems === 'function') {
|
||||||
return fileExplorerView
|
return fileExplorerView
|
||||||
}
|
}
|
||||||
} else { // Obsidian versions prior to 1.6.0
|
|
||||||
if (typeof fileExplorerView.createFolderDom === 'function') {
|
|
||||||
return fileExplorerView
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Various scenarios when File Explorer was turned off (e.g. by some other plugin)
|
// Various scenarios when File Explorer was turned off (e.g. by some other plugin)
|
||||||
if (logWarning) {
|
if (logWarning) {
|
||||||
|
@ -396,7 +380,7 @@ export default class CustomSortPlugin
|
||||||
item.onClick(() => {
|
item.onClick(() => {
|
||||||
const bookmarksPlugin = getBookmarksPlugin(plugin.app, plugin.settings.bookmarksGroupToConsumeAsOrderingReference)
|
const bookmarksPlugin = getBookmarksPlugin(plugin.app, plugin.settings.bookmarksGroupToConsumeAsOrderingReference)
|
||||||
if (bookmarksPlugin) {
|
if (bookmarksPlugin) {
|
||||||
const orderedChildren: Array<TAbstractFile> = plugin.orderedFolderItemsForBookmarking(file.parent, bookmarksPlugin)
|
const orderedChildren: Array<TAbstractFile> = plugin.orderedFolderItemsForBookmarking(file.parent!, bookmarksPlugin)
|
||||||
bookmarksPlugin.bookmarkSiblings(orderedChildren)
|
bookmarksPlugin.bookmarkSiblings(orderedChildren)
|
||||||
bookmarksPlugin.saveDataAndUpdateBookmarkViews(true)
|
bookmarksPlugin.saveDataAndUpdateBookmarkViews(true)
|
||||||
}
|
}
|
||||||
|
@ -409,7 +393,7 @@ export default class CustomSortPlugin
|
||||||
item.onClick(() => {
|
item.onClick(() => {
|
||||||
const bookmarksPlugin = getBookmarksPlugin(plugin.app, plugin.settings.bookmarksGroupToConsumeAsOrderingReference)
|
const bookmarksPlugin = getBookmarksPlugin(plugin.app, plugin.settings.bookmarksGroupToConsumeAsOrderingReference)
|
||||||
if (bookmarksPlugin) {
|
if (bookmarksPlugin) {
|
||||||
const orderedChildren: Array<TAbstractFile> = file.parent.children.map((entry: TFile | TFolder) => entry)
|
const orderedChildren: Array<TAbstractFile> = file.parent!.children.map((entry: TFile | TFolder) => entry)
|
||||||
bookmarksPlugin.unbookmarkSiblings(orderedChildren)
|
bookmarksPlugin.unbookmarkSiblings(orderedChildren)
|
||||||
bookmarksPlugin.saveDataAndUpdateBookmarkViews(true)
|
bookmarksPlugin.saveDataAndUpdateBookmarkViews(true)
|
||||||
}
|
}
|
||||||
|
@ -486,7 +470,6 @@ export default class CustomSortPlugin
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
if (requireApiVersion('1.4.11')) {
|
|
||||||
this.registerEvent(
|
this.registerEvent(
|
||||||
// "files-menu" event was exposed in 1.4.11
|
// "files-menu" event was exposed in 1.4.11
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
@ -523,7 +506,6 @@ export default class CustomSortPlugin
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
this.registerEvent(
|
this.registerEvent(
|
||||||
this.app.vault.on("rename", (file: TAbstractFile, oldPath: string) => {
|
this.app.vault.on("rename", (file: TAbstractFile, oldPath: string) => {
|
||||||
|
@ -583,7 +565,6 @@ export default class CustomSortPlugin
|
||||||
createProcessingContextForSorting(has: HasSortingOrGrouping): ProcessingContext {
|
createProcessingContextForSorting(has: HasSortingOrGrouping): ProcessingContext {
|
||||||
const ctx: ProcessingContext = {
|
const ctx: ProcessingContext = {
|
||||||
_mCache: this.app.metadataCache,
|
_mCache: this.app.metadataCache,
|
||||||
starredPluginInstance: has.grouping.byStarred ? getStarredPlugin(this.app) : undefined,
|
|
||||||
bookmarksPluginInstance: has.grouping.byBookmarks || has.sorting.byBookmarks ? getBookmarksPlugin(this.app, this.settings.bookmarksGroupToConsumeAsOrderingReference, false, true) : undefined,
|
bookmarksPluginInstance: has.grouping.byBookmarks || has.sorting.byBookmarks ? getBookmarksPlugin(this.app, this.settings.bookmarksGroupToConsumeAsOrderingReference, false, true) : undefined,
|
||||||
iconFolderPluginInstance: has.grouping.byIcon ? getIconFolderPlugin(this.app) : undefined,
|
iconFolderPluginInstance: has.grouping.byIcon ? getIconFolderPlugin(this.app) : undefined,
|
||||||
plugin: this
|
plugin: this
|
||||||
|
@ -635,8 +616,6 @@ export default class CustomSortPlugin
|
||||||
// That's why not showing and not logging error message here
|
// That's why not showing and not logging error message here
|
||||||
patchableFileExplorer = patchableFileExplorer ?? this.checkFileExplorerIsAvailableAndPatchable(false)
|
patchableFileExplorer = patchableFileExplorer ?? this.checkFileExplorerIsAvailableAndPatchable(false)
|
||||||
if (patchableFileExplorer) {
|
if (patchableFileExplorer) {
|
||||||
if (requireApiVersion && requireApiVersion("1.6.0")) {
|
|
||||||
// Starting from Obsidian 1.6.0 the sorting mechanics has been significantly refactored internally in Obsidian
|
|
||||||
const uninstallerOfFolderSortFunctionWrapper: MonkeyAroundUninstaller = around(patchableFileExplorer.constructor.prototype, {
|
const uninstallerOfFolderSortFunctionWrapper: MonkeyAroundUninstaller = around(patchableFileExplorer.constructor.prototype, {
|
||||||
getSortedFolderItems(old: any) {
|
getSortedFolderItems(old: any) {
|
||||||
return function (...args: any[]) {
|
return function (...args: any[]) {
|
||||||
|
@ -651,7 +630,7 @@ export default class CustomSortPlugin
|
||||||
const sortingData = plugin.determineAndPrepareSortingDataForFolder(folder)
|
const sortingData = plugin.determineAndPrepareSortingDataForFolder(folder)
|
||||||
|
|
||||||
if (sortingData.sortSpec) {
|
if (sortingData.sortSpec) {
|
||||||
return getSortedFolderItems_vFrom_1_6_0.call(this, folder, sortingData.sortSpec, plugin.createProcessingContextForSorting(sortingData.sortingAndGroupingStats))
|
return getSortedFolderItems.call(this, folder, sortingData.sortSpec, plugin.createProcessingContextForSorting(sortingData.sortingAndGroupingStats))
|
||||||
} else {
|
} else {
|
||||||
return old.call(this, ...args);
|
return old.call(this, ...args);
|
||||||
}
|
}
|
||||||
|
@ -660,35 +639,6 @@ export default class CustomSortPlugin
|
||||||
})
|
})
|
||||||
this.register(requestStandardObsidianSortAfter(uninstallerOfFolderSortFunctionWrapper))
|
this.register(requestStandardObsidianSortAfter(uninstallerOfFolderSortFunctionWrapper))
|
||||||
return true
|
return true
|
||||||
} else {
|
|
||||||
// Up to Obsidian 1.6.0
|
|
||||||
// @ts-ignore
|
|
||||||
let tmpFolder = new TFolder(Vault, "");
|
|
||||||
let Folder = patchableFileExplorer.createFolderDom(tmpFolder).constructor;
|
|
||||||
const uninstallerOfFolderSortFunctionWrapper: MonkeyAroundUninstaller = around(Folder.prototype, {
|
|
||||||
sort(old: any) {
|
|
||||||
return function (...args: any[]) {
|
|
||||||
// quick check for plugin status
|
|
||||||
if (plugin.settings.suspended) {
|
|
||||||
return old.call(this, ...args);
|
|
||||||
}
|
|
||||||
|
|
||||||
plugin.resetIconInaccurateStateToEnabled()
|
|
||||||
|
|
||||||
const folder: TFolder = this.file
|
|
||||||
const sortingData = plugin.determineAndPrepareSortingDataForFolder(folder)
|
|
||||||
|
|
||||||
if (sortingData.sortSpec) {
|
|
||||||
return folderSort_vUpTo_1_6_0.call(this, sortingData.sortSpec, plugin.createProcessingContextForSorting(sortingData.sortingAndGroupingStats));
|
|
||||||
} else {
|
|
||||||
return old.call(this, ...args);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
})
|
|
||||||
this.register(requestStandardObsidianSortAfter(uninstallerOfFolderSortFunctionWrapper))
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -732,7 +682,7 @@ export default class CustomSortPlugin
|
||||||
const data: any = await this.loadData() || {}
|
const data: any = await this.loadData() || {}
|
||||||
const isFreshInstall: boolean = Object.keys(data).length === 0
|
const isFreshInstall: boolean = Object.keys(data).length === 0
|
||||||
this.settings = Object.assign({}, DEFAULT_SETTINGS, data);
|
this.settings = Object.assign({}, DEFAULT_SETTINGS, data);
|
||||||
if (requireApiVersion('1.2.0') && isFreshInstall) {
|
if (isFreshInstall) {
|
||||||
this.settings = Object.assign(this.settings, DEFAULT_SETTING_FOR_1_2_0_UP)
|
this.settings = Object.assign(this.settings, DEFAULT_SETTING_FOR_1_2_0_UP)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@ const getHas = (gTotal?: NM, gBkmrk?: NM, gStar?: NM, gIcon?: NM, sTot?: NM, sBk
|
||||||
grouping: {
|
grouping: {
|
||||||
total: gTotal ||0,
|
total: gTotal ||0,
|
||||||
byBookmarks: gBkmrk ||0,
|
byBookmarks: gBkmrk ||0,
|
||||||
byStarred: gStar ||0,
|
|
||||||
byIcon: gIcon ||0
|
byIcon: gIcon ||0
|
||||||
},
|
},
|
||||||
sorting: {
|
sorting: {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import {
|
import {
|
||||||
CachedMetadata,
|
CachedMetadata,
|
||||||
MetadataCache,
|
MetadataCache,
|
||||||
Pos,
|
Pos, TAbstractFile,
|
||||||
TFile,
|
TFile,
|
||||||
TFolder,
|
TFolder,
|
||||||
Vault
|
Vault
|
||||||
|
@ -36,9 +36,8 @@ import {
|
||||||
CompoundDotRomanNumberNormalizerFn
|
CompoundDotRomanNumberNormalizerFn
|
||||||
} from "../../custom-sort/sorting-spec-processor";
|
} from "../../custom-sort/sorting-spec-processor";
|
||||||
import {
|
import {
|
||||||
findStarredFile_pathParam,
|
BookmarksPluginInterface
|
||||||
Starred_PluginInstance
|
} from "../../utils/BookmarksCorePluginSignature";
|
||||||
} from "../../utils/StarredPluginSignature";
|
|
||||||
import {
|
import {
|
||||||
ObsidianIconFolder_PluginInstance,
|
ObsidianIconFolder_PluginInstance,
|
||||||
ObsidianIconFolderPlugin_Data
|
ObsidianIconFolderPlugin_Data
|
||||||
|
@ -937,25 +936,25 @@ describe('determineSortingGroup', () => {
|
||||||
} as FolderItemForSorting);
|
} as FolderItemForSorting);
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
describe('CustomSortGroupType.StarredOnly', () => {
|
describe('CustomSortGroupType.BookmarkedOnly', () => {
|
||||||
it('should not match not starred file', () => {
|
it('should not match not bookmarked file', () => {
|
||||||
// given
|
// given
|
||||||
const file: TFile = mockTFile('References', 'md', 111, MOCK_TIMESTAMP + 222, MOCK_TIMESTAMP + 333);
|
const file: TFile = mockTFile('References', 'md', 111, MOCK_TIMESTAMP + 222, MOCK_TIMESTAMP + 333);
|
||||||
const sortSpec: CustomSortSpec = {
|
const sortSpec: CustomSortSpec = {
|
||||||
targetFoldersPaths: ['/'],
|
targetFoldersPaths: ['/'],
|
||||||
groups: [{
|
groups: [{
|
||||||
type: CustomSortGroupType.StarredOnly
|
type: CustomSortGroupType.BookmarkedOnly
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
const starredPluginInstance: Partial<Starred_PluginInstance> = {
|
const bookmarksPluginInstance: Partial<BookmarksPluginInterface> = {
|
||||||
findStarredFile: jest.fn( function(filePath: findStarredFile_pathParam): TFile | null {
|
determineBookmarkOrder: jest.fn( function(path: string): number | undefined {
|
||||||
return null
|
return undefined
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// when
|
// when
|
||||||
const result = determineSortingGroup(file, sortSpec, {
|
const result = determineSortingGroup(file, sortSpec, {
|
||||||
starredPluginInstance: starredPluginInstance as Starred_PluginInstance
|
bookmarksPluginInstance: bookmarksPluginInstance as BookmarksPluginInterface
|
||||||
} as ProcessingContext)
|
} as ProcessingContext)
|
||||||
|
|
||||||
// then
|
// then
|
||||||
|
@ -968,30 +967,32 @@ describe('determineSortingGroup', () => {
|
||||||
mtime: MOCK_TIMESTAMP + 333,
|
mtime: MOCK_TIMESTAMP + 333,
|
||||||
path: 'Some parent folder/References.md'
|
path: 'Some parent folder/References.md'
|
||||||
});
|
});
|
||||||
expect(starredPluginInstance.findStarredFile).toHaveBeenCalledTimes(1)
|
expect(bookmarksPluginInstance.determineBookmarkOrder).toHaveBeenCalledTimes(1)
|
||||||
})
|
})
|
||||||
it('should match starred file', () => {
|
it('should match bookmarked file', () => {
|
||||||
// given
|
// given
|
||||||
const file: TFile = mockTFile('References', 'md', 111, MOCK_TIMESTAMP + 222, MOCK_TIMESTAMP + 333);
|
const file: TFile = mockTFile('References', 'md', 111, MOCK_TIMESTAMP + 222, MOCK_TIMESTAMP + 333);
|
||||||
const sortSpec: CustomSortSpec = {
|
const sortSpec: CustomSortSpec = {
|
||||||
targetFoldersPaths: ['/'],
|
targetFoldersPaths: ['/'],
|
||||||
groups: [{
|
groups: [{
|
||||||
type: CustomSortGroupType.StarredOnly
|
type: CustomSortGroupType.BookmarkedOnly
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
const starredPluginInstance: Partial<Starred_PluginInstance> = {
|
const BOOKMARK_ORDER = 123
|
||||||
findStarredFile: jest.fn( function(filePath: findStarredFile_pathParam): TFile | null {
|
const bookmarksPluginInstance: Partial<BookmarksPluginInterface> = {
|
||||||
return filePath.path === 'Some parent folder/References.md' ? file : null
|
determineBookmarkOrder: jest.fn( function(path: string): number | undefined {
|
||||||
|
return path === 'Some parent folder/References.md' ? BOOKMARK_ORDER : undefined
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// when
|
// when
|
||||||
const result = determineSortingGroup(file, sortSpec, {
|
const result = determineSortingGroup(file, sortSpec, {
|
||||||
starredPluginInstance: starredPluginInstance as Starred_PluginInstance
|
bookmarksPluginInstance: bookmarksPluginInstance as BookmarksPluginInterface
|
||||||
} as ProcessingContext)
|
} as ProcessingContext)
|
||||||
|
|
||||||
// then
|
// then
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
|
bookmarkedIdx: BOOKMARK_ORDER,
|
||||||
groupIdx: 0,
|
groupIdx: 0,
|
||||||
isFolder: false,
|
isFolder: false,
|
||||||
sortString: "References",
|
sortString: "References",
|
||||||
|
@ -1000,131 +1001,7 @@ describe('determineSortingGroup', () => {
|
||||||
mtime: MOCK_TIMESTAMP + 333,
|
mtime: MOCK_TIMESTAMP + 333,
|
||||||
path: 'Some parent folder/References.md'
|
path: 'Some parent folder/References.md'
|
||||||
});
|
});
|
||||||
expect(starredPluginInstance.findStarredFile).toHaveBeenCalledTimes(1)
|
expect(bookmarksPluginInstance.determineBookmarkOrder).toHaveBeenCalledTimes(1)
|
||||||
})
|
|
||||||
it('should not match empty folder', () => {
|
|
||||||
// given
|
|
||||||
const folder: TFolder = mockTFolder('TestEmptyFolder');
|
|
||||||
const sortSpec: CustomSortSpec = {
|
|
||||||
targetFoldersPaths: ['/'],
|
|
||||||
groups: [{
|
|
||||||
type: CustomSortGroupType.StarredOnly
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
const starredPluginInstance: Partial<Starred_PluginInstance> = {
|
|
||||||
findStarredFile: jest.fn( function(filePath: findStarredFile_pathParam): TFile | null {
|
|
||||||
return filePath.path === 'Some parent folder/References.md' ? {} as TFile : null
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// when
|
|
||||||
const result = determineSortingGroup(folder, sortSpec, {
|
|
||||||
starredPluginInstance: starredPluginInstance as Starred_PluginInstance
|
|
||||||
} as ProcessingContext)
|
|
||||||
|
|
||||||
// then
|
|
||||||
expect(result).toEqual({
|
|
||||||
groupIdx: 1, // The lastIdx+1, group not determined
|
|
||||||
isFolder: true,
|
|
||||||
sortString: "TestEmptyFolder",
|
|
||||||
sortStringWithExt: "TestEmptyFolder",
|
|
||||||
|
|
||||||
ctime: 0,
|
|
||||||
mtime: 0,
|
|
||||||
path: 'TestEmptyFolder',
|
|
||||||
folder: {
|
|
||||||
children: [],
|
|
||||||
isRoot: expect.any(Function),
|
|
||||||
name: "TestEmptyFolder",
|
|
||||||
parent: {},
|
|
||||||
path: "TestEmptyFolder",
|
|
||||||
vault: {}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
expect(starredPluginInstance.findStarredFile).not.toHaveBeenCalled()
|
|
||||||
})
|
|
||||||
it('should not match folder w/o starred items', () => {
|
|
||||||
// given
|
|
||||||
const folder: TFolder = mockTFolderWithChildren('TestEmptyFolder');
|
|
||||||
const sortSpec: CustomSortSpec = {
|
|
||||||
targetFoldersPaths: ['/'],
|
|
||||||
groups: [{
|
|
||||||
type: CustomSortGroupType.StarredOnly
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
const starredPluginInstance: Partial<Starred_PluginInstance> = {
|
|
||||||
findStarredFile: jest.fn( function(filePath: findStarredFile_pathParam): TFile | null {
|
|
||||||
return filePath.path === 'Some parent folder/References.md' ? {} as TFile : null
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// when
|
|
||||||
const result = determineSortingGroup(folder, sortSpec, {
|
|
||||||
starredPluginInstance: starredPluginInstance as Starred_PluginInstance
|
|
||||||
} as ProcessingContext)
|
|
||||||
|
|
||||||
// then
|
|
||||||
expect(result).toEqual({
|
|
||||||
groupIdx: 1, // The lastIdx+1, group not determined
|
|
||||||
isFolder: true,
|
|
||||||
sortString: "TestEmptyFolder",
|
|
||||||
sortStringWithExt: "TestEmptyFolder",
|
|
||||||
|
|
||||||
ctime: 0,
|
|
||||||
mtime: 0,
|
|
||||||
path: 'TestEmptyFolder',
|
|
||||||
folder: {
|
|
||||||
children: expect.any(Array),
|
|
||||||
isRoot: expect.any(Function),
|
|
||||||
name: "TestEmptyFolder",
|
|
||||||
parent: {},
|
|
||||||
path: "TestEmptyFolder",
|
|
||||||
vault: {}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
expect(starredPluginInstance.findStarredFile).toHaveBeenCalledTimes(folder.children.filter(f => (f as any).isRoot === undefined).length)
|
|
||||||
})
|
|
||||||
it('should match folder with one starred item', () => {
|
|
||||||
// given
|
|
||||||
const folder: TFolder = mockTFolderWithChildren('TestEmptyFolder');
|
|
||||||
const sortSpec: CustomSortSpec = {
|
|
||||||
targetFoldersPaths: ['/'],
|
|
||||||
groups: [{
|
|
||||||
type: CustomSortGroupType.StarredOnly
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
const starredPluginInstance: Partial<Starred_PluginInstance> = {
|
|
||||||
findStarredFile: jest.fn(function (filePath: findStarredFile_pathParam): TFile | null {
|
|
||||||
return filePath.path === 'Some parent folder/Child file 2 created as newest, not modified at all.md' ? {} as TFile : null
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// when
|
|
||||||
const result = determineSortingGroup(folder, sortSpec, {
|
|
||||||
starredPluginInstance: starredPluginInstance as Starred_PluginInstance
|
|
||||||
} as ProcessingContext)
|
|
||||||
|
|
||||||
// then
|
|
||||||
expect(result).toEqual({
|
|
||||||
groupIdx: 0,
|
|
||||||
isFolder: true,
|
|
||||||
sortString: "TestEmptyFolder",
|
|
||||||
sortStringWithExt: "TestEmptyFolder",
|
|
||||||
|
|
||||||
ctime: 0,
|
|
||||||
mtime: 0,
|
|
||||||
path: 'TestEmptyFolder',
|
|
||||||
folder: {
|
|
||||||
children: expect.any(Array),
|
|
||||||
isRoot: expect.any(Function),
|
|
||||||
name: "TestEmptyFolder",
|
|
||||||
parent: {},
|
|
||||||
path: "TestEmptyFolder",
|
|
||||||
vault: {}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// assume optimized checking of starred items -> first match ends the check
|
|
||||||
expect(starredPluginInstance.findStarredFile).toHaveBeenCalledTimes(2)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
describe('CustomSortGroupType.HasIcon', () => {
|
describe('CustomSortGroupType.HasIcon', () => {
|
||||||
|
|
|
@ -42,9 +42,9 @@ with-metadata: Pages
|
||||||
> a-z by-metadata:
|
> a-z by-metadata:
|
||||||
/: with-icon:
|
/: with-icon:
|
||||||
with-icon: RiClock24
|
with-icon: RiClock24
|
||||||
starred:
|
bookmarked:
|
||||||
/:files starred:
|
/:files bookmarked:
|
||||||
/folders starred:
|
/folders bookmarked:
|
||||||
|
|
||||||
:::: folder of bookmarks
|
:::: folder of bookmarks
|
||||||
< by-bookmarks-order
|
< by-bookmarks-order
|
||||||
|
@ -107,9 +107,9 @@ target-folder: tricky folder 2
|
||||||
> a-z by-metadata:
|
> a-z by-metadata:
|
||||||
/:files with-icon:
|
/:files with-icon:
|
||||||
/folders:files with-icon: RiClock24
|
/folders:files with-icon: RiClock24
|
||||||
/folders:files starred:
|
/folders:files bookmarked:
|
||||||
/:files starred:
|
/:files bookmarked:
|
||||||
/folders starred:
|
/folders bookmarked:
|
||||||
|
|
||||||
target-folder: folder of bookmarks
|
target-folder: folder of bookmarks
|
||||||
order-asc: by-bookmarks-order
|
order-asc: by-bookmarks-order
|
||||||
|
@ -204,12 +204,12 @@ const expectedSortSpecsExampleA: { [key: string]: CustomSortSpec } = {
|
||||||
type: CustomSortGroupType.HasIcon,
|
type: CustomSortGroupType.HasIcon,
|
||||||
iconName: 'RiClock24'
|
iconName: 'RiClock24'
|
||||||
}, {
|
}, {
|
||||||
type: CustomSortGroupType.StarredOnly
|
type: CustomSortGroupType.BookmarkedOnly
|
||||||
}, {
|
}, {
|
||||||
type: CustomSortGroupType.StarredOnly,
|
type: CustomSortGroupType.BookmarkedOnly,
|
||||||
filesOnly: true
|
filesOnly: true
|
||||||
}, {
|
}, {
|
||||||
type: CustomSortGroupType.StarredOnly,
|
type: CustomSortGroupType.BookmarkedOnly,
|
||||||
foldersOnly: true
|
foldersOnly: true
|
||||||
}, {
|
}, {
|
||||||
type: CustomSortGroupType.Outsiders
|
type: CustomSortGroupType.Outsiders
|
||||||
|
|
|
@ -50,7 +50,6 @@ declare module 'obsidian' {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FileExplorerView extends View {
|
export interface FileExplorerView extends View {
|
||||||
createFolderDom(folder: TFolder): FileExplorerFolder;
|
|
||||||
getSortedFolderItems(sortedFolder: TFolder): any[];
|
getSortedFolderItems(sortedFolder: TFolder): any[];
|
||||||
|
|
||||||
requestSort(): void;
|
requestSort(): void;
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
import {App, InstalledPlugin, PluginInstance, TAbstractFile, TFile, TFolder} from "obsidian";
|
|
||||||
|
|
||||||
export const StarredPlugin_findStarredFile_methodName = 'findStarredFile'
|
|
||||||
|
|
||||||
export interface findStarredFile_pathParam {
|
|
||||||
path: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Starred_PluginInstance extends PluginInstance {
|
|
||||||
[StarredPlugin_findStarredFile_methodName]: (filePath: findStarredFile_pathParam) => TFile | null
|
|
||||||
}
|
|
||||||
|
|
||||||
export const StarredCorePluginId: string = 'starred'
|
|
||||||
|
|
||||||
export const getStarredPlugin = (app: App): Starred_PluginInstance | undefined => {
|
|
||||||
const starredPlugin: InstalledPlugin | undefined = app?.internalPlugins?.getPluginById(StarredCorePluginId)
|
|
||||||
if (starredPlugin && starredPlugin.enabled && starredPlugin.instance) {
|
|
||||||
const starredPluginInstance: Starred_PluginInstance = starredPlugin.instance as Starred_PluginInstance
|
|
||||||
// defensive programming, in case Obsidian changes its internal APIs
|
|
||||||
if (typeof starredPluginInstance?.[StarredPlugin_findStarredFile_methodName] === 'function') {
|
|
||||||
return starredPluginInstance
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const isFolder = (entry: TAbstractFile) => {
|
|
||||||
// The plain obvious 'entry instanceof TFolder' doesn't work inside Jest unit tests, hence a workaround below
|
|
||||||
return !!((entry as any).isRoot);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const determineStarredStatusOfFolder = (folder: TFolder, starredPluginInstance: Starred_PluginInstance): boolean => {
|
|
||||||
return folder.children.some((folderItem) => {
|
|
||||||
return !isFolder(folderItem) && starredPluginInstance[StarredPlugin_findStarredFile_methodName]({path: folderItem.path})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export const determineStarredStatusOf = (entry: TFile | TFolder, aFile: boolean, starredPluginInstance: Starred_PluginInstance) => {
|
|
||||||
if (aFile) {
|
|
||||||
return !!starredPluginInstance[StarredPlugin_findStarredFile_methodName]({path: entry.path})
|
|
||||||
} else { // aFolder
|
|
||||||
return determineStarredStatusOfFolder(entry as TFolder, starredPluginInstance)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -48,5 +48,6 @@
|
||||||
"2.1.12": "0.16.2",
|
"2.1.12": "0.16.2",
|
||||||
"2.1.13": "0.16.2",
|
"2.1.13": "0.16.2",
|
||||||
"2.1.14": "0.16.2",
|
"2.1.14": "0.16.2",
|
||||||
"2.1.15": "0.16.2"
|
"2.1.15": "0.16.2",
|
||||||
|
"3.0.0": "1.7.2"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue