Skip to content

Commit

Permalink
feat: quick filters implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
darlal committed Apr 29, 2023
1 parent 337d756 commit bfbc7dc
Show file tree
Hide file tree
Showing 22 changed files with 1,225 additions and 260 deletions.
21 changes: 12 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,13 @@ Find what you're looking for faster, using Quick Switcher++. An [Obsidian.md](ht
## Features

* [Find files by headings instead of filename](#search-headings-instead-of-filename)
* [Configuration](#search-headings-configuration)
* [Navigate to symbols (Canvas Nodes, Headings, Hashtags, Links, Embeddings) in your notes](#symbol-navigation-canvas-nodes-headings-hashtags-links-embeddings)
* [Configuration](#symbol-navigation-configuration)
* [Navigate between open editors, and side panels](#navigate-between-open-editors)
* [Configuration](#editor-navigation-configuration)
* [Switch between configured Workspaces quickly](#switch-workspaces)
* [Configuration](#workspace-configuration)
* [Navigate between your Starred notes](#navigate-starred-notes)
* [Configuration](#starred-configuration)
* [Run commands](#run-commands)
* [Configuration](#command-configuration)
* [Navigate to related items](#related-items)
* [Configuration](#related-items-configuration)
* [Run Obsidian commands](#run-commands)
* [Navigate to related files](#related-items)
* [Quick Filters to narrow down your search results](#quick-filters)
* [General Settings](#general-settings)
* [Global Commands for Hotkeys/Shortcuts](#global-commands-for-hotkeys)

Expand Down Expand Up @@ -183,6 +177,15 @@ When the Related Items command is triggered for a selected input suggestion/file
| Show related item types | Specify which relation types are enabled to be displayed in the result list. | `backlink`<br />`disk-location`<br />`outgoing-link` |
| Exclude open files | **Enabled**: related files which are already open in an editor will not be displayed in the list.<br />**Disabled**: All related files will be displayed in the list. | disabled |

## Quick Filters

Quick Filters enable you to quickly narrow down the types of items that appear in your search results without having to change your query. Each type of results will have a hotkey assigned that can be used to toggle (show/hide) that type from the result list. When active, only results that match the Quick Filter type will be displayed, multiple Quick Filters can be active at the same time.

In the demo below, `Quick Switcher++: Open Symbols for the active editor` global command is triggered for the active file. Notice towards the bottom of the Switcher the hotkeys assigned to each result type. The `headings` Quick Filter is triggered using the `Ctrl+Alt+1` hotkey, this restricts the result list to only display Heading results. Multiple Quick Filters are activated using their corresponding hotkeys, and all Quick Filters can be quickly toggle off using `Ctrl+Alt+0`.

![quick filters gif](https://raw.githubusercontent.com/darlal/obsidian-switcher-plus/master/demo/quick-filters.gif)


## General Settings

| Setting | Description | Default |
Expand Down
Binary file added demo/quick-filters.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 17 additions & 1 deletion src/Handlers/__tests__/handler.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Facet, FacetSettingsData } from './../../types/sharedTypes';
import {
App,
Editor,
Expand Down Expand Up @@ -2071,7 +2072,7 @@ describe('Handler', () => {

it('should log any errors to the console while trying to create a new file', async () => {
const filename = chance.word();
const errorMsg = 'Unit test error';
const errorMsg = 'createFile Unit test error';
const rejectedPromise = Promise.reject(errorMsg);
const consoleLogSpy = jest.spyOn(console, 'log').mockReturnValueOnce();

Expand Down Expand Up @@ -2101,4 +2102,19 @@ describe('Handler', () => {
consoleLogSpy.mockRestore();
});
});

describe('activateFacet', () => {
test('withshouldResetActiveFacets disabled, it should save changes to active facet status', () => {
const finalValue = true;
const mockFacet = mock<Facet>({ isActive: false });
mockSettings.quickFilters = mock<FacetSettingsData>({
shouldResetActiveFacets: false,
});

sut.activateFacet([mockFacet], finalValue);

expect(mockFacet.isActive).toBe(finalValue);
expect(mockSettings.save).toHaveBeenCalledWith();
});
});
});
18 changes: 14 additions & 4 deletions src/Handlers/__tests__/symbolHandler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -973,7 +973,12 @@ describe('symbolHandler', () => {
const results: SymbolInfo[] = [];
mockVault.cachedRead.mockResolvedValueOnce(fileContentWithCallout);

await sut.addCalloutsFromSource(mockFile, calloutSectionCache, results);
await sut.addCalloutsFromSource(
mockFile,
calloutSectionCache,
results,
new Set<string>(),
);

expect(mockVault.cachedRead).toHaveBeenCalledWith(mockFile);
expect(results).toHaveLength(calloutSectionCache.length);
Expand All @@ -986,7 +991,12 @@ describe('symbolHandler', () => {

mockVault.cachedRead.mockRejectedValueOnce(errorMsg);

await sut.addCalloutsFromSource(mockFile, calloutSectionCache, []);
await sut.addCalloutsFromSource(
mockFile,
calloutSectionCache,
[],
new Set<string>(),
);

expect(consoleLogSpy).toHaveBeenCalledWith(expectedMsg, errorMsg);
expect(mockVault.cachedRead).toHaveBeenCalledWith(mockFile);
Expand All @@ -1004,7 +1014,7 @@ describe('symbolHandler', () => {
const canvasNodes = (JSON.parse(fileContent) as CanvasData).nodes;
mockVault.cachedRead.mockResolvedValueOnce(fileContent);

await sut.addCanvasSymbolsFromSource(mockFile, results);
await sut.addCanvasSymbolsFromSource(mockFile, results, new Set<string>());

expect(mockVault.cachedRead).toHaveBeenCalledWith(mockFile);
expect(results).toHaveLength(canvasNodes.length);
Expand All @@ -1017,7 +1027,7 @@ describe('symbolHandler', () => {

mockVault.cachedRead.mockRejectedValueOnce(errorMsg);

await sut.addCanvasSymbolsFromSource(mockFile, []);
await sut.addCanvasSymbolsFromSource(mockFile, [], new Set<string>());

expect(consoleLogSpy).toHaveBeenCalledWith(expectedMsg, errorMsg);
expect(mockVault.cachedRead).toHaveBeenCalledWith(mockFile);
Expand Down
34 changes: 34 additions & 0 deletions src/Handlers/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
} from 'obsidian';
import {
AnySuggestion,
Facet,
MatchType,
Mode,
PathDisplayFormat,
Expand All @@ -46,6 +47,7 @@ import {
} from 'src/utils';

export abstract class Handler<T> {
facets: Facet[];
get commandString(): string {
return null;
}
Expand All @@ -68,6 +70,38 @@ export abstract class Handler<T> {
/* noop */
}

getFacets(mode: Mode): Facet[] {
if (!this.facets) {
this.facets = this.settings.quickFilters.facetList?.filter((v) => v.mode === mode);
}

return this.facets ?? [];
}
getAvailableFacets(inputInfo: InputInfo): Facet[] {
return this.getFacets(inputInfo.mode).filter((v) => v.isAvailable);
}

activateFacet(facets: Facet[], isActive: boolean): void {
facets.forEach((v) => (v.isActive = isActive));

if (!this.settings.quickFilters.shouldResetActiveFacets) {
this.settings.save();
}
}

getActiveFacetIds(inputInfo: InputInfo): Set<string> {
const facetIds = this.getAvailableFacets(inputInfo)
.filter((v) => v.isActive)
.map((v) => v.id);

return new Set(facetIds);
}

isFacetedWith(activeFacetIds: Set<string>, facetId: string): boolean {
const hasActiveFacets = !!activeFacetIds.size;
return (hasActiveFacets && activeFacetIds.has(facetId)) || !hasActiveFacets;
}

getEditorInfo(leaf: WorkspaceLeaf): SourceInfo {
const { excludeViewTypes } = this.settings;
let file: TFile = null;
Expand Down
44 changes: 27 additions & 17 deletions src/Handlers/relatedItemsHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export class RelatedItemsHandler extends Handler<

const { hasSearchTerm } = inputInfo.searchQuery;
const cmd = inputInfo.parsedCommand(Mode.RelatedItemsList) as SourcedParsedCommand;
const items = this.getItems(cmd.source);
const items = this.getItems(cmd.source, inputInfo);

items.forEach((item) => {
const sugg = this.searchAndCreateSuggestion(inputInfo, item);
Expand Down Expand Up @@ -169,29 +169,39 @@ export class RelatedItemsHandler extends Handler<
: this.createSuggestion(currentWorkspaceEnvList, item, result);
}

getItems(sourceInfo: SourceInfo): RelatedItemsInfo[] {
getItems(sourceInfo: SourceInfo, inputInfo: InputInfo): RelatedItemsInfo[] {
const relatedItems: RelatedItemsInfo[] = [];
const { metadataCache } = this.app;
const { enabledRelatedItems } = this.settings;
const { file, suggestion } = sourceInfo;
const enabledRelatedItems = new Set(this.settings.enabledRelatedItems);
const activeFacetIds = this.getActiveFacetIds(inputInfo);

enabledRelatedItems.forEach((relationType) => {
if (relationType === RelationType.Backlink) {
let targetPath = file?.path;
let linkMap = metadataCache.resolvedLinks;
const shouldIncludeRelation = (relationType: RelationType) => {
return (
enabledRelatedItems.has(relationType) &&
this.isFacetedWith(activeFacetIds, relationType)
);
};

if (isUnresolvedSuggestion(suggestion)) {
targetPath = suggestion.linktext;
linkMap = metadataCache.unresolvedLinks;
}
if (shouldIncludeRelation(RelationType.Backlink)) {
let targetPath = file?.path;
let linkMap = metadataCache.resolvedLinks;

this.addBacklinks(targetPath, linkMap, relatedItems);
} else if (relationType === RelationType.DiskLocation) {
this.addRelatedDiskFiles(file, relatedItems);
} else {
this.addOutgoingLinks(file, relatedItems);
if (isUnresolvedSuggestion(suggestion)) {
targetPath = suggestion.linktext;
linkMap = metadataCache.unresolvedLinks;
}
});

this.addBacklinks(targetPath, linkMap, relatedItems);
}

if (shouldIncludeRelation(RelationType.DiskLocation)) {
this.addRelatedDiskFiles(file, relatedItems);
}

if (shouldIncludeRelation(RelationType.OutgoingLink)) {
this.addOutgoingLinks(file, relatedItems);
}

return relatedItems;
}
Expand Down
Loading

0 comments on commit bfbc7dc

Please sign in to comment.