Skip to content

Commit

Permalink
Update ExplorerSort feature
Browse files Browse the repository at this point in the history
  • Loading branch information
snezhig committed Oct 27, 2024
1 parent 4fa8e84 commit 6e55e74
Show file tree
Hide file tree
Showing 8 changed files with 51 additions and 73 deletions.
5 changes: 5 additions & 0 deletions docs/Processor.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,18 @@ A value returned by your function will be used as shown title

Works same as [Function](#function), but the argument is the object instead of string;
Object your function will give an argument:

```typescript
// Object that will be given into FunctionV2 processor as an "obj" argument
import {TFile} from "obsidian";

export type FunctionV2ObjArg = {
// New file title
title: string;
// Path to file
path: string;
// Obisian's TFile
file: TFile | null;
};
```

Expand Down
2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"id": "obsidian-front-matter-title-plugin",
"name": "Front Matter Title",
"version": "3.11.0",
"version": "3.12.0",
"minAppVersion": "1.7.4",
"description": "Lets you define a title in frontmatter to be displayed as the filename for explorer, graph, search and etc.",
"author": "Snezhig",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "obsidian-front-matter-title",
"version": "3.11.0",
"version": "3.12.0",
"description": "This is a sample plugin for Obsidian (https://obsidian.md)",
"main": "main.js",
"scripts": {
Expand Down
14 changes: 9 additions & 5 deletions src/Feature/Explorer/ExplorerManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,19 @@ export default class ExplorerManager extends AbstractManager {
this.setState(State.Disabled);
}

private tryEnable(): void {
this.sort?.start();
this.setState(State.Enabled);
this.logger.log("Manager enabled");
}

private subscribe(): void {
const ref = this.dispatcher.addListener({
name: "active:leaf:change",
cb: () => {
if (this.isEnabled() && this.getExplorerView()) {
this.logger.log("Catch explorer view");
this.setState(State.Enabled);
this.tryEnable();
this.doRefresh().catch(this.logger.log);
this.dispatcher.removeListener(ref);
} else if (!this.isEnabled()) {
Expand All @@ -79,21 +85,19 @@ export default class ExplorerManager extends AbstractManager {
}
},
});
this.setState(State.WaitForActiveLeaf);
}

protected doEnable() {
if (!this.facade.isInternalPluginEnabled(Plugins.FileExplorer)) {
this.logger.info(`internal plugin ${Plugins.FileExplorer} is not enabled`);
return;
}
this.sort?.start();
if (!this.getExplorerView()) {
this.logger.log("Could not get view. Subscribe and wait for active leaf");
this.subscribe();
this.setState(State.WaitForActiveLeaf);
} else {
this.logger.log("Manager enabled");
this.setState(State.Enabled);
this.tryEnable();
}
}

Expand Down
51 changes: 18 additions & 33 deletions src/Feature/Explorer/ExplorerSort.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,15 @@ import { Feature, Leaves } from "@src/Enum";
import ExplorerViewUndefined from "@src/Feature/Explorer/ExplorerViewUndefined";
import EventDispatcherInterface from "@src/Components/EventDispatcher/Interfaces/EventDispatcherInterface";
import ListenerRef from "@src/Components/EventDispatcher/Interfaces/ListenerRef";
import { ResolverInterface } from "../../Resolver/Interfaces";
import { ResolverInterface } from "@src/Resolver/Interfaces";
import FeatureService from "../FeatureService";
import { DelayerInterface } from "@src/Components/Delayer/Delayer";
import { FunctionReplacerFactory } from "../../../config/inversify.factory.types";
import { FunctionReplacerFactory } from "@config/inversify.factory.types";

@injectable()
export default class ExplorerSort {
private view: TFileExplorerView;
private started = false;
private replacer: FunctionReplacer<TFileExplorerItem, "sort", ExplorerSort>;
private replacer: FunctionReplacer<TFileExplorerView, "getSortedFolderItems", ExplorerSort>;
private readonly cb: (e: { id: string; result?: boolean }) => void;
private refs: [ListenerRef<"manager:refresh">?, ListenerRef<"manager:update">?] = [];
private resolver: ResolverInterface;
Expand All @@ -33,10 +32,12 @@ export default class ExplorerSort {
private dispatcher: EventDispatcherInterface<AppEvents>,
@inject(SI["feature:service"])
service: FeatureService,
@inject(SI.delayer)
private readonly delayer: DelayerInterface,
@inject(SI["factory:replacer"])
private readonly replacerFactory: FunctionReplacerFactory<TFileExplorerItem, "sort", ExplorerSort>
private readonly replacerFactory: FunctionReplacerFactory<
TFileExplorerView,
"getSortedFolderItems",
ExplorerSort
>
) {
this.resolver = service.createResolver(Feature.Explorer);
const trigger = debounce(this.onManagerUpdate.bind(this), 1000);
Expand Down Expand Up @@ -105,20 +106,15 @@ export default class ExplorerSort {
}
return;
}
const item = this.getFolderItem();
if (!item) {
this.logger.log("Folder item not found. Try again in 1000 ms");
this.delayer.delay(this.tryToReplaceOriginalSort.bind(this), 1000);
return;
}

this.logger.log("Init replacer");

this.replacer = this.replacerFactory(
Object.getPrototypeOf(item),
"sort",
Object.getPrototypeOf(this.view),
"getSortedFolderItems",
this,
function (args, defaultArgs, vanilla) {
args.sort(this, vanilla);
(feature, defaultArgs, vanilla) => {
return feature.getSortedFolderItems(defaultArgs?.[0]) ?? vanilla.call(this);
}
);
this.replacer.enable();
Expand All @@ -132,21 +128,19 @@ export default class ExplorerSort {
return order === "alphabetical";
}

private sort(item: TFileExplorerItem, vanilla: () => void) {
private getSortedFolderItems(folder: TFolder): TFileExplorerItem[] | null {
const sortOrder = this.view.sortOrder;

if (!this.isSortSupported(sortOrder)) {
this.logger.log(`Sort is ${sortOrder}. Skipped.`);
vanilla.call(item);
return;
}
if (!(item.file instanceof TFolder)) {
if (!(folder instanceof TFolder)) {
this.logger.log("File is not TFolder. Why? Skipped.");
vanilla.call(item);
return;
}
this.logger.log("Sort by feature");
const children = item.file.children.slice();

const children = folder.children.slice();
const result = [];
children.sort((a, b) => {
const i = a instanceof TFolder;
Expand All @@ -169,15 +163,6 @@ export default class ExplorerSort {
const f = this.view.fileItems[child.path];
f && result.push(f);
}

item.vChildren.setChildren(result);
}

private getFolderItem(): TFileExplorerItem {
for (const item of Object.values(this.view.fileItems)) {
if (item.file instanceof TFolder) {
return item;
}
}
return result;
}
}
2 changes: 2 additions & 0 deletions src/obsidian-ex.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ declare module "obsidian" {
sortOrder: string;
requestSort: () => void;

getSortedFolderItems: (TFolder) => TFileExplorerItem[];

getDisplayText(): string;

getViewType(): string;
Expand Down
45 changes: 13 additions & 32 deletions test/unit/Feature/Explorer/ExplorerSort.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import EventDispatcherInterface, {
import { AppEvents } from "@src/Types";
import FeatureService from "@src/Feature/FeatureService";
import { ResolverInterface } from "@src/Resolver/Interfaces";
import { DelayerInterface } from "@src/Components/Delayer/Delayer";
import LoggerInterface from "@src/Components/Debug/LoggerInterface";
import { FunctionReplacerFactory } from "@config/inversify.factory.types";
import FunctionReplacer from "../../../../src/Utils/FunctionReplacer";
Expand All @@ -21,10 +20,11 @@ let callback: Callback<AppEvents[keyof AppEvents]>;
let dispatcher: MockProxy<EventDispatcherInterface<any>>;
let refs: [any?, any?];
let featureService: MockProxy<FeatureService>;
let delayer: MockProxy<DelayerInterface>;
let sort: ExplorerSort;
let view: MockProxy<TFileExplorerView>;
let replacerFactory: jest.Mock<ReturnType<FunctionReplacerFactory<TFileExplorerItem, "sort", ExplorerSort>>>;
let replacerFactory: jest.Mock<
ReturnType<FunctionReplacerFactory<TFileExplorerView, "getSortedFolderItems", ExplorerSort>>
>;

beforeEach(() => {
refs = [];
Expand All @@ -38,12 +38,11 @@ beforeEach(() => {
});
featureService = mock<FeatureService>();
featureService.createResolver.mockReturnValue(mock<ResolverInterface>());
delayer = mock<DelayerInterface>();
view = mock<TFileExplorerView>();
replacerFactory = jest.fn();
// @ts-ignore
view.requestSort = jest.fn();
sort = new ExplorerSort(mock<LoggerInterface>(), facade, dispatcher, featureService, delayer, replacerFactory);
sort = new ExplorerSort(mock<LoggerInterface>(), facade, dispatcher, featureService, replacerFactory);
});

describe("ExplorerSort", () => {
Expand All @@ -58,43 +57,25 @@ describe("ExplorerSort", () => {
facade.getViewsOfType.mockReturnValue([view]);
});

test("should add listener after enabled", () => {
sort.start();
expect(sort.isStarted()).toBeTruthy();
expect(dispatcher.addListener).toHaveBeenCalledWith({ name: "manager:update", cb: expect.anything() });
expect(dispatcher.addListener).toHaveBeenCalledWith({ name: "manager:refresh", cb: expect.anything() });
expect(dispatcher.addListener).toHaveBeenCalledTimes(2);
});
//
test("should delay replace because these is not item", () => {
let fn: Function;
//copy delayed function to call it manually
delayer.delay.mockImplementation(f => {
fn = f;
return 0;
});
//enable sort to trigger internal "tryToReplaceOriginalSort"
sort.start();
expect(delayer.delay).toHaveBeenCalledTimes(1);
//Call delayed function
fn();
expect(delayer.delay).toHaveBeenCalledTimes(2);
expect(replacerFactory).not.toBeCalled();
});

describe("with explorer item", () => {
let replacer: FunctionReplacer<TFileExplorerItem, "sort", ExplorerSort>;
let replacer: FunctionReplacer<TFileExplorerView, "getSortedFolderItems", ExplorerSort>;
let item: TFileExplorerItem;
beforeEach(() => {
item = mock<TFileExplorerItem>();
item.file = new TFolder();
replacer = mock<FunctionReplacer<TFileExplorerItem, "sort", ExplorerSort>>();
replacer = mock<FunctionReplacer<TFileExplorerView, "getSortedFolderItems", ExplorerSort>>();
replacerFactory.mockImplementationOnce(() => replacer);
});

afterEach(() => {
expect(dispatcher.addListener).toHaveBeenCalledWith({ name: "manager:update", cb: expect.anything() });
expect(dispatcher.addListener).toHaveBeenCalledWith({ name: "manager:refresh", cb: expect.anything() });
expect(dispatcher.addListener).toHaveBeenCalledTimes(2);
});

test("Should create function replacer without delay, because there is folder item", () => {
view.fileItems["mock"] = item;
sort.start();
expect(delayer.delay).not.toHaveBeenCalled();
expect(replacerFactory).toBeCalled();
expect(replacer.enable).toBeCalled();
});
Expand Down
3 changes: 2 additions & 1 deletion versions.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,6 @@
"3.8.4": "0.16.3",
"3.9.0": "1.5.3",
"3.10.0": "1.5.3",
"3.11.0": "1.7.4"
"3.11.0": "1.7.4",
"3.12.0": "1.7.4"
}

0 comments on commit 6e55e74

Please sign in to comment.