#60 - Simplified integration with obsidian-icon-folder plugin

- the plugin integration and matching part completed
- unit tests
This commit is contained in:
SebastianMC 2023-02-28 23:57:08 +01:00
parent 18faf70b5e
commit 01af14b174
6 changed files with 586 additions and 29 deletions

View File

@ -8,7 +8,8 @@ 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 StarredOnly,
IconFolderPlugin
} }
export enum CustomSortOrder { export enum CustomSortOrder {
@ -59,6 +60,7 @@ export interface CustomSortGroup {
matchFilenameWithExt?: boolean matchFilenameWithExt?: boolean
foldersOnly?: boolean foldersOnly?: boolean
withMetadataFieldName?: string // for 'with-metadata:' grouping withMetadataFieldName?: string // for 'with-metadata:' grouping
folderIconName?: string // for integration with obsidian-folder-icon community plugin
priority?: number priority?: number
combineWithIdx?: number combineWithIdx?: number
} }

View File

@ -12,6 +12,10 @@ import {
import {CustomSortGroupType, CustomSortOrder, CustomSortSpec, RegExpSpec} from './custom-sort-types'; import {CustomSortGroupType, CustomSortOrder, CustomSortSpec, RegExpSpec} from './custom-sort-types';
import {CompoundDashNumberNormalizerFn, CompoundDotRomanNumberNormalizerFn} from "./sorting-spec-processor"; import {CompoundDashNumberNormalizerFn, CompoundDotRomanNumberNormalizerFn} from "./sorting-spec-processor";
import {findStarredFile_pathParam, Starred_PluginInstance} from "../utils/StarredPluginSignature"; import {findStarredFile_pathParam, Starred_PluginInstance} from "../utils/StarredPluginSignature";
import {
ObsidianIconFolder_PluginInstance,
ObsidianIconFolderPlugin_Data
} from "../utils/ObsidianIconFolderPluginSignature";
const mockTFile = (basename: string, ext: string, size?: number, ctime?: number, mtime?: number): TFile => { const mockTFile = (basename: string, ext: string, size?: number, ctime?: number, mtime?: number): TFile => {
return { return {
@ -1067,6 +1071,458 @@ describe('determineSortingGroup', () => {
expect(starredPluginInstance.findStarredFile).toHaveBeenCalledTimes(2) expect(starredPluginInstance.findStarredFile).toHaveBeenCalledTimes(2)
}) })
}) })
describe('CustomSortGroupType.IconFolderPlugin', () => {
it('should not match file w/o icon', () => {
// given
const file: TFile = mockTFile('References', 'md', 111, MOCK_TIMESTAMP + 222, MOCK_TIMESTAMP + 333);
const sortSpec: CustomSortSpec = {
targetFoldersPaths: ['/'],
groups: [{
type: CustomSortGroupType.IconFolderPlugin
}]
}
const obsidianIconFolderPluginInstance: Partial<ObsidianIconFolder_PluginInstance> = {
getData: jest.fn( function(): ObsidianIconFolderPlugin_Data {
return {settings: {}} // The obsidian-folder-icon plugin keeps the settings there indeed ;-)
})
}
// when
const result = determineSortingGroup(file, sortSpec, {
iconFolderPluginInstance: obsidianIconFolderPluginInstance as ObsidianIconFolder_PluginInstance
})
// then
expect(result).toEqual({
groupIdx: 1, // The lastIdx+1, group not determined
isFolder: false,
sortString: "References.md",
ctimeNewest: MOCK_TIMESTAMP + 222,
ctimeOldest: MOCK_TIMESTAMP + 222,
mtime: MOCK_TIMESTAMP + 333,
path: 'Some parent folder/References.md'
});
expect(obsidianIconFolderPluginInstance.getData).toHaveBeenCalledTimes(1)
})
it('should not match file with icon of different name', () => {
// given
const file: TFile = mockTFile('References', 'md', 111, MOCK_TIMESTAMP + 222, MOCK_TIMESTAMP + 333);
const sortSpec: CustomSortSpec = {
targetFoldersPaths: ['/'],
groups: [{
type: CustomSortGroupType.IconFolderPlugin,
folderIconName: 'IncorrectIconName'
}]
}
const obsidianIconFolderPluginInstance: Partial<ObsidianIconFolder_PluginInstance> = {
getData: jest.fn( function(): ObsidianIconFolderPlugin_Data {
return {
settings: {}, // The obsidian-folder-icon plugin keeps the settings there indeed ;-)
'Some parent folder/References.md': 'CorrectIconName'
}
})
}
// when
const result = determineSortingGroup(file, sortSpec, {
iconFolderPluginInstance: obsidianIconFolderPluginInstance as ObsidianIconFolder_PluginInstance
})
// then
expect(result).toEqual({
groupIdx: 1, // The lastIdx+1, group not determined
isFolder: false,
sortString: "References.md",
ctimeNewest: MOCK_TIMESTAMP + 222,
ctimeOldest: MOCK_TIMESTAMP + 222,
mtime: MOCK_TIMESTAMP + 333,
path: 'Some parent folder/References.md'
});
expect(obsidianIconFolderPluginInstance.getData).toHaveBeenCalledTimes(1)
})
it('should match file with any icon', () => {
// given
const file: TFile = mockTFile('References', 'md', 111, MOCK_TIMESTAMP + 222, MOCK_TIMESTAMP + 333);
const sortSpec: CustomSortSpec = {
targetFoldersPaths: ['/'],
groups: [{
type: CustomSortGroupType.IconFolderPlugin
}]
}
const obsidianIconFolderPluginInstance: Partial<ObsidianIconFolder_PluginInstance> = {
getData: jest.fn( function(): ObsidianIconFolderPlugin_Data {
return {
settings: {}, // The obsidian-folder-icon plugin keeps the settings there indeed ;-)
'Some parent folder/References.md': 'Irrelevant icon name, only presence matters'
}
})
}
// when
const result = determineSortingGroup(file, sortSpec, {
iconFolderPluginInstance: obsidianIconFolderPluginInstance as ObsidianIconFolder_PluginInstance
})
// then
expect(result).toEqual({
groupIdx: 0,
isFolder: false,
sortString: "References.md",
ctimeNewest: MOCK_TIMESTAMP + 222,
ctimeOldest: MOCK_TIMESTAMP + 222,
mtime: MOCK_TIMESTAMP + 333,
path: 'Some parent folder/References.md'
});
expect(obsidianIconFolderPluginInstance.getData).toHaveBeenCalledTimes(1)
})
it('should match file with icon of expected name', () => {
// given
const file: TFile = mockTFile('References', 'md', 111, MOCK_TIMESTAMP + 222, MOCK_TIMESTAMP + 333);
const sortSpec: CustomSortSpec = {
targetFoldersPaths: ['/'],
groups: [{
type: CustomSortGroupType.IconFolderPlugin,
folderIconName: 'CorrectIconName'
}]
}
const obsidianIconFolderPluginInstance: Partial<ObsidianIconFolder_PluginInstance> = {
getData: jest.fn( function(): ObsidianIconFolderPlugin_Data {
return {
settings: {}, // The obsidian-folder-icon plugin keeps the settings there indeed ;-)
'Some parent folder/References.md': 'CorrectIconName'
}
})
}
// when
const result = determineSortingGroup(file, sortSpec, {
iconFolderPluginInstance: obsidianIconFolderPluginInstance as ObsidianIconFolder_PluginInstance
})
// then
expect(result).toEqual({
groupIdx: 0,
isFolder: false,
sortString: "References.md",
ctimeNewest: MOCK_TIMESTAMP + 222,
ctimeOldest: MOCK_TIMESTAMP + 222,
mtime: MOCK_TIMESTAMP + 333,
path: 'Some parent folder/References.md'
});
expect(obsidianIconFolderPluginInstance.getData).toHaveBeenCalledTimes(1)
})
it('should not match folder w/o icon', () => {
// given
const folder: TFolder = mockTFolder('TestEmptyFolder');
const sortSpec: CustomSortSpec = {
targetFoldersPaths: ['/'],
groups: [{
type: CustomSortGroupType.IconFolderPlugin
}]
}
const obsidianIconFolderPluginInstance: Partial<ObsidianIconFolder_PluginInstance> = {
getData: jest.fn( function(): ObsidianIconFolderPlugin_Data {
return {settings: {}} // The obsidian-folder-icon plugin keeps the settings there indeed ;-)
})
}
// when
const result = determineSortingGroup(folder, sortSpec, {
iconFolderPluginInstance: obsidianIconFolderPluginInstance as ObsidianIconFolder_PluginInstance
})
// then
expect(result).toEqual({
groupIdx: 1, // The lastIdx+1, group not determined
isFolder: true,
sortString: "TestEmptyFolder",
ctimeNewest: 0,
ctimeOldest: 0,
mtime: 0,
path: 'TestEmptyFolder',
folder: {
children: [],
isRoot: expect.any(Function),
name: "TestEmptyFolder",
parent: {},
path: "TestEmptyFolder",
vault: {}
}
});
expect(obsidianIconFolderPluginInstance.getData).toHaveBeenCalledTimes(1)
})
it('should match folder with any icon (icon specified by string alone)', () => {
// given
const folder: TFolder = mockTFolderWithChildren('TestEmptyFolder');
const sortSpec: CustomSortSpec = {
targetFoldersPaths: ['/'],
groups: [{
type: CustomSortGroupType.IconFolderPlugin
}]
}
const obsidianIconFolderPluginInstance: Partial<ObsidianIconFolder_PluginInstance> = {
getData: jest.fn( function(): ObsidianIconFolderPlugin_Data {
return {
settings: {}, // The obsidian-folder-icon plugin keeps the settings there indeed ;-)
'TestEmptyFolder': 'Irrelevant icon name, only presence matters'
}
})
}
// when
const result = determineSortingGroup(folder, sortSpec, {
iconFolderPluginInstance: obsidianIconFolderPluginInstance as ObsidianIconFolder_PluginInstance
})
// then
expect(result).toEqual({
groupIdx: 0,
isFolder: true,
sortString: "TestEmptyFolder",
ctimeNewest: 0,
ctimeOldest: 0,
mtime: 0,
path: 'TestEmptyFolder',
folder: {
children: expect.any(Array),
isRoot: expect.any(Function),
name: "TestEmptyFolder",
parent: {},
path: "TestEmptyFolder",
vault: {}
}
});
expect(obsidianIconFolderPluginInstance.getData).toHaveBeenCalledTimes(1)
})
it('should match folder with any icon (icon specified together with inheritance)', () => {
// given
const folder: TFolder = mockTFolderWithChildren('TestEmptyFolder');
const sortSpec: CustomSortSpec = {
targetFoldersPaths: ['/'],
groups: [{
type: CustomSortGroupType.IconFolderPlugin
}]
}
const obsidianIconFolderPluginInstance: Partial<ObsidianIconFolder_PluginInstance> = {
getData: jest.fn( function(): ObsidianIconFolderPlugin_Data {
return {
settings: {}, // The obsidian-folder-icon plugin keeps the settings there indeed ;-)
'TestEmptyFolder': {
iconName: 'ConfiguredIcon',
inheritanceIcon: 'ConfiguredInheritanceIcon'
}
}
})
}
// when
const result = determineSortingGroup(folder, sortSpec, {
iconFolderPluginInstance: obsidianIconFolderPluginInstance as ObsidianIconFolder_PluginInstance
})
// then
expect(result).toEqual({
groupIdx: 0,
isFolder: true,
sortString: "TestEmptyFolder",
ctimeNewest: 0,
ctimeOldest: 0,
mtime: 0,
path: 'TestEmptyFolder',
folder: {
children: expect.any(Array),
isRoot: expect.any(Function),
name: "TestEmptyFolder",
parent: {},
path: "TestEmptyFolder",
vault: {}
}
});
expect(obsidianIconFolderPluginInstance.getData).toHaveBeenCalledTimes(1)
})
it('should match folder with specified icon (icon specified by string alone)', () => {
// given
const folder: TFolder = mockTFolderWithChildren('TestEmptyFolder');
const sortSpec: CustomSortSpec = {
targetFoldersPaths: ['/'],
groups: [{
type: CustomSortGroupType.IconFolderPlugin,
folderIconName: 'ConfiguredIcon-by-string'
}]
}
const obsidianIconFolderPluginInstance: Partial<ObsidianIconFolder_PluginInstance> = {
getData: jest.fn( function(): ObsidianIconFolderPlugin_Data {
return {
settings: {}, // The obsidian-folder-icon plugin keeps the settings there indeed ;-)
'TestEmptyFolder': 'ConfiguredIcon-by-string'
}
})
}
// when
const result = determineSortingGroup(folder, sortSpec, {
iconFolderPluginInstance: obsidianIconFolderPluginInstance as ObsidianIconFolder_PluginInstance
})
// then
expect(result).toEqual({
groupIdx: 0,
isFolder: true,
sortString: "TestEmptyFolder",
ctimeNewest: 0,
ctimeOldest: 0,
mtime: 0,
path: 'TestEmptyFolder',
folder: {
children: expect.any(Array),
isRoot: expect.any(Function),
name: "TestEmptyFolder",
parent: {},
path: "TestEmptyFolder",
vault: {}
}
});
expect(obsidianIconFolderPluginInstance.getData).toHaveBeenCalledTimes(1)
})
it('should match folder with specified icon (icon specified together with inheritance)', () => {
// given
const folder: TFolder = mockTFolderWithChildren('TestEmptyFolder');
const sortSpec: CustomSortSpec = {
targetFoldersPaths: ['/'],
groups: [{
type: CustomSortGroupType.IconFolderPlugin,
folderIconName: 'ConfiguredIcon'
}]
}
const obsidianIconFolderPluginInstance: Partial<ObsidianIconFolder_PluginInstance> = {
getData: jest.fn( function(): ObsidianIconFolderPlugin_Data {
return {
settings: {}, // The obsidian-folder-icon plugin keeps the settings there indeed ;-)
'TestEmptyFolder': {
iconName: 'ConfiguredIcon',
inheritanceIcon: 'ConfiguredInheritanceIcon'
}
}
})
}
// when
const result = determineSortingGroup(folder, sortSpec, {
iconFolderPluginInstance: obsidianIconFolderPluginInstance as ObsidianIconFolder_PluginInstance
})
// then
expect(result).toEqual({
groupIdx: 0,
isFolder: true,
sortString: "TestEmptyFolder",
ctimeNewest: 0,
ctimeOldest: 0,
mtime: 0,
path: 'TestEmptyFolder',
folder: {
children: expect.any(Array),
isRoot: expect.any(Function),
name: "TestEmptyFolder",
parent: {},
path: "TestEmptyFolder",
vault: {}
}
});
expect(obsidianIconFolderPluginInstance.getData).toHaveBeenCalledTimes(1)
})
it('should not match folder with different icon (icon specified by string alone)', () => {
// given
const folder: TFolder = mockTFolderWithChildren('TestEmptyFolder');
const sortSpec: CustomSortSpec = {
targetFoldersPaths: ['/'],
groups: [{
type: CustomSortGroupType.IconFolderPlugin,
folderIconName: 'ConfiguredIcon-by-string'
}]
}
const obsidianIconFolderPluginInstance: Partial<ObsidianIconFolder_PluginInstance> = {
getData: jest.fn( function(): ObsidianIconFolderPlugin_Data {
return {
settings: {}, // The obsidian-folder-icon plugin keeps the settings there indeed ;-)
'TestEmptyFolder': 'AnotherConfiguredIcon-by-string'
}
})
}
// when
const result = determineSortingGroup(folder, sortSpec, {
iconFolderPluginInstance: obsidianIconFolderPluginInstance as ObsidianIconFolder_PluginInstance
})
// then
expect(result).toEqual({
groupIdx: 1, // lastIdx+1 - no match
isFolder: true,
sortString: "TestEmptyFolder",
ctimeNewest: 0,
ctimeOldest: 0,
mtime: 0,
path: 'TestEmptyFolder',
folder: {
children: expect.any(Array),
isRoot: expect.any(Function),
name: "TestEmptyFolder",
parent: {},
path: "TestEmptyFolder",
vault: {}
}
});
expect(obsidianIconFolderPluginInstance.getData).toHaveBeenCalledTimes(1)
})
it('should not match folder with different icon (icon specified together with inheritance)', () => {
// given
const folder: TFolder = mockTFolderWithChildren('TestEmptyFolder');
const sortSpec: CustomSortSpec = {
targetFoldersPaths: ['/'],
groups: [{
type: CustomSortGroupType.IconFolderPlugin,
folderIconName: 'ConfiguredIcon'
}]
}
const obsidianIconFolderPluginInstance: Partial<ObsidianIconFolder_PluginInstance> = {
getData: jest.fn( function(): ObsidianIconFolderPlugin_Data {
return {
settings: {}, // The obsidian-folder-icon plugin keeps the settings there indeed ;-)
'TestEmptyFolder': {
iconName: 'OtherConfiguredIcon',
inheritanceIcon: 'ConfiguredInheritanceIcon'
}
}
})
}
// when
const result = determineSortingGroup(folder, sortSpec, {
iconFolderPluginInstance: obsidianIconFolderPluginInstance as ObsidianIconFolder_PluginInstance
})
// then
expect(result).toEqual({
groupIdx: 1, // lastIdx+1 - no match
isFolder: true,
sortString: "TestEmptyFolder",
ctimeNewest: 0,
ctimeOldest: 0,
mtime: 0,
path: 'TestEmptyFolder',
folder: {
children: expect.any(Array),
isRoot: expect.any(Function),
name: "TestEmptyFolder",
parent: {},
path: "TestEmptyFolder",
vault: {}
}
});
expect(obsidianIconFolderPluginInstance.getData).toHaveBeenCalledTimes(1)
})
})
describe('when sort by metadata is involved', () => { describe('when sort by metadata is involved', () => {
it('should correctly read direct metadata from File item (order by metadata set on group) alph', () => { it('should correctly read direct metadata from File item (order by metadata set on group) alph', () => {
// given // given

View File

@ -1,5 +1,6 @@
import { import {
App, App,
CommunityPlugin,
FrontMatterCache, FrontMatterCache,
InstalledPlugin, InstalledPlugin,
requireApiVersion, requireApiVersion,
@ -8,9 +9,19 @@ import {
TFolder TFolder
} from 'obsidian'; } from 'obsidian';
import { import {
determineStarredStatusOf,
getStarredPlugin,
Starred_PluginInstance, Starred_PluginInstance,
StarredPlugin_findStarredFile_methodName StarredPlugin_findStarredFile_methodName
} from '../utils/StarredPluginSignature' } from '../utils/StarredPluginSignature';
import {
determineIconOf,
getIconFolderPlugin,
FolderIconObject,
ObsidianIconFolder_PluginInstance,
ObsidianIconFolderPlugin_Data,
ObsidianIconFolderPlugin_getData_methodName
} from '../utils/ObsidianIconFolderPluginSignature'
import { import {
CustomSortGroup, CustomSortGroup,
CustomSortGroupType, CustomSortGroupType,
@ -154,6 +165,7 @@ export const matchGroupRegex = (theRegex: RegExpSpec, nameForMatching: string):
export interface Context { export interface Context {
starredPluginInstance?: Starred_PluginInstance starredPluginInstance?: Starred_PluginInstance
iconFolderPluginInstance?: ObsidianIconFolder_PluginInstance
} }
export const determineSortingGroup = function (entry: TFile | TFolder, spec: CustomSortSpec, ctx?: Context): FolderItemForSorting { export const determineSortingGroup = function (entry: TFile | TFolder, spec: CustomSortSpec, ctx?: Context): FolderItemForSorting {
@ -253,17 +265,24 @@ export const determineSortingGroup = function (entry: TFile | TFolder, spec: Cus
break break
case CustomSortGroupType.StarredOnly: case CustomSortGroupType.StarredOnly:
if (ctx?.starredPluginInstance) { if (ctx?.starredPluginInstance) {
let starred: boolean let starred: boolean = determineStarredStatusOf(entry, aFile, ctx.starredPluginInstance)
if (aFile) {
starred = !!ctx.starredPluginInstance[StarredPlugin_findStarredFile_methodName]({path: entry.path})
} else { // aFolder
starred = determineStarredStatusOfFolder(entry as TFolder, ctx.starredPluginInstance)
}
if (starred) { if (starred) {
determined = true determined = true
} }
} }
break break
case CustomSortGroupType.IconFolderPlugin:
if(ctx?.iconFolderPluginInstance) {
let iconName: string | undefined = determineIconOf(entry, ctx.iconFolderPluginInstance)
if (iconName) {
if (group.folderIconName) {
determined = iconName === group.folderIconName
} else {
determined = true
}
}
}
break
case CustomSortGroupType.MatchAll: case CustomSortGroupType.MatchAll:
determined = true; determined = true;
break break
@ -389,25 +408,6 @@ export const determineDatesForFolder = (folder: TFolder, now: number): [Modified
return [mtimeOfFolder, ctimeNewestOfFolder, ctimeOldestOfFolder] return [mtimeOfFolder, ctimeNewestOfFolder, ctimeOldestOfFolder]
} }
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
}
}
}
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 determineFolderDatesIfNeeded = (folderItems: Array<FolderItemForSorting>, sortingSpec: CustomSortSpec) => { export const determineFolderDatesIfNeeded = (folderItems: Array<FolderItemForSorting>, sortingSpec: CustomSortSpec) => {
const Now: number = Date.now() const Now: number = Date.now()
folderItems.forEach((item) => { folderItems.forEach((item) => {
@ -427,6 +427,7 @@ export const folderSort = function (sortingSpec: CustomSortSpec, order: string[]
let fileExplorer = this.fileExplorer let fileExplorer = this.fileExplorer
sortingSpec._mCache = sortingSpec.plugin?.app.metadataCache sortingSpec._mCache = sortingSpec.plugin?.app.metadataCache
const starredPluginInstance: Starred_PluginInstance | undefined = getStarredPlugin(sortingSpec?.plugin?.app) const starredPluginInstance: Starred_PluginInstance | undefined = getStarredPlugin(sortingSpec?.plugin?.app)
const iconFolderPluginInstance: ObsidianIconFolder_PluginInstance | undefined = getIconFolderPlugin(sortingSpec?.plugin?.app)
const folderItems: Array<FolderItemForSorting> = (sortingSpec.itemsToHide ? const folderItems: Array<FolderItemForSorting> = (sortingSpec.itemsToHide ?
this.file.children.filter((entry: TFile | TFolder) => { this.file.children.filter((entry: TFile | TFolder) => {
@ -436,7 +437,8 @@ export const folderSort = function (sortingSpec: CustomSortSpec, order: string[]
this.file.children) this.file.children)
.map((entry: TFile | TFolder) => { .map((entry: TFile | TFolder) => {
const itemForSorting: FolderItemForSorting = determineSortingGroup(entry, sortingSpec, { const itemForSorting: FolderItemForSorting = determineSortingGroup(entry, sortingSpec, {
starredPluginInstance: starredPluginInstance starredPluginInstance: starredPluginInstance,
iconFolderPluginInstance: iconFolderPluginInstance
}) })
return itemForSorting return itemForSorting
}) })

17
src/types/types.d.ts vendored
View File

@ -12,7 +12,24 @@ declare module 'obsidian' {
id: string; id: string;
} }
export type CommunityPluginId = string
// undocumented internal interface - for experimental features
export interface CommunityPlugin {
manifest: {
id: CommunityPluginId
}
_loaded: boolean
}
// undocumented internal interface - for experimental features
export interface CommunityPlugins {
enabledPlugins: Set<CommunityPluginId>
plugins: {[key: CommunityPluginId]: CommunityPlugin}
}
export interface App { export interface App {
plugins: CommunityPlugins;
internalPlugins: InternalPlugins; // undocumented internal API - for experimental features internalPlugins: InternalPlugins; // undocumented internal API - for experimental features
viewRegistry: ViewRegistry; viewRegistry: ViewRegistry;
} }

View File

@ -0,0 +1,48 @@
import {App, CommunityPlugin, TAbstractFile, TFile, TFolder} from "obsidian";
import {Starred_PluginInstance} from "./StarredPluginSignature";
// For https://github.com/FlorianWoelki/obsidian-icon-folder
export const ObsidianIconFolderPlugin_getData_methodName = 'getData'
export interface FolderIconObject {
iconName: string | null;
inheritanceIcon: string;
}
export type ObsidianIconFolderPlugin_Data = Record<string, boolean | string | FolderIconObject | any>
export interface ObsidianIconFolder_PluginInstance extends CommunityPlugin {
[ObsidianIconFolderPlugin_getData_methodName]: () => ObsidianIconFolderPlugin_Data
}
// https://github.com/FlorianWoelki/obsidian-icon-folder/blob/fd9c7df1486744450cec3d7ee9cee2b34d008e56/manifest.json#L2
export const ObsidianIconFolderPluginId: string = 'obsidian-icon-folder'
export const getIconFolderPlugin = (app?: App): ObsidianIconFolder_PluginInstance | undefined => {
const iconFolderPlugin: CommunityPlugin | undefined = app?.plugins?.plugins?.[ObsidianIconFolderPluginId]
if (iconFolderPlugin && iconFolderPlugin._loaded && app?.plugins?.enabledPlugins?.has(ObsidianIconFolderPluginId)) {
const iconFolderPluginInstance: ObsidianIconFolder_PluginInstance = iconFolderPlugin as ObsidianIconFolder_PluginInstance
// defensive programming, in case the community plugin changes its internal APIs
if (typeof iconFolderPluginInstance?.[ObsidianIconFolderPlugin_getData_methodName] === 'function') {
return iconFolderPluginInstance
}
}
}
// Intentionally partial and simplified, only detect icons configured directly,
// ignoring any icon inheritance or regexp-based applied icons
export const determineIconOf = (entry: TAbstractFile, iconFolderPluginInstance: ObsidianIconFolder_PluginInstance): string | undefined => {
const iconsData: ObsidianIconFolderPlugin_Data | undefined = iconFolderPluginInstance[ObsidianIconFolderPlugin_getData_methodName]()
const entryForPath: any = iconsData?.[entry.path]
// Icons configured directly
if (typeof entryForPath === 'string') {
return entryForPath
} else if (typeof (entryForPath as FolderIconObject)?.iconName === 'string') {
return (entryForPath as FolderIconObject)?.iconName ?? undefined
} else {
return undefined
}
}

View File

@ -1,4 +1,4 @@
import {PluginInstance, TFile} from "obsidian"; import {App, InstalledPlugin, PluginInstance, TAbstractFile, TFile, TFolder} from "obsidian";
export const StarredPlugin_findStarredFile_methodName = 'findStarredFile' export const StarredPlugin_findStarredFile_methodName = 'findStarredFile'
@ -9,3 +9,35 @@ export interface findStarredFile_pathParam {
export interface Starred_PluginInstance extends PluginInstance { export interface Starred_PluginInstance extends PluginInstance {
[StarredPlugin_findStarredFile_methodName]: (filePath: findStarredFile_pathParam) => TFile | null [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)
}
}