From f7ca01825234bd553c82db8e8be874f233ef619d Mon Sep 17 00:00:00 2001 From: MOHAMED MOHANA Date: Fri, 6 Feb 2026 20:06:34 +0300 Subject: [PATCH] docs: Add documentation for custom filter functions Addresses issue #6 - Document how to write your own filter function This commit adds comprehensive documentation for: - Custom filter function type and signature - Multiple examples (exact match, prefix match, FuzzyFind integration) - Custom sorting examples - Default fuzzy filter with custom parameters - Explanation of filter parameters (fuzziness, threshold) This helps users understand how to extend FloatingFilter's filtering capabilities beyond the default fuzzy matching implementation. --- README.md | 144 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 141 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e83e2dd..5d3aae9 100644 --- a/README.md +++ b/README.md @@ -14,13 +14,12 @@ The list uses the default `NSTableView` styles, so it'll look right at home on B ## Usage -This module strives for taking care of its components's memory wherever possible. This means you do not need to keep a reference to the window or its controller around. **Both will be freed when the action has been completed.** +This module strives for taking care of its components's memory wherever possible. This means you do not need to keep a reference to window or its controller around. **Both will be freed when action has been completed.** ### When all possible items are known This is useful if you want to filter e.g. a limited collection of files, like "Recent Files", or to display a list of known macros. - ```swift import FloatingFilter @@ -32,12 +31,151 @@ let items = [ Item(identifier: "custom-ID_123", title: "Show downloaded data") ] - FloatingFilterModule.showFilterWindow(items: items) { selectedItems in print("Selected:", selectedItems.map { $0.title }) } ``` +### Custom Filter Functions + +Since version 0.8.0, you can provide a custom filter function instead of using the built-in fuzzy matching. The default implementation performs fuzzy string matching, scoring, filtering below a threshold, and sorting. + +A custom filter function has the following type: + +```swift +typealias ItemFilter = (_ needle: String, _ haystack: [Item]) -> [Item] +``` + +- `needle`: The search string typed by the user +- `haystack`: The complete list of items to filter +- Returns: A filtered and sorted array of matching items + +#### Example 1: Exact Match Filter + +```swift +import FloatingFilter + +func exactMatchFilter(needle: String, haystack: [Item]) -> [Item] { + guard !needle.isEmpty else { return haystack } + + return haystack.filter { item in + item.title.localizedCaseInsensitiveContains(needle) + } +} + +let items = [ + Item(identifier: 1, title: "Create new widget"), + Item(identifier: 2, title: "Open last document"), + Item(identifier: 3, title: "Show downloaded data") +] + +FloatingFilterModule.showFilterWindow( + items: items, + filter: exactMatchFilter +) { selectedItems in + print("Selected:", selectedItems.map { $0.title }) +} +``` + +#### Example 2: Prefix Match Filter + +```swift +func prefixMatchFilter(needle: String, haystack: [Item]) -> [Item] { + guard !needle.isEmpty else { return haystack } + + let lowercaseNeedle = needle.localizedLowercase + return haystack.filter { item in + item.title.localizedLowercase.hasPrefix(lowercaseNeedle) + } +} + +FloatingFilterModule.showFilterWindow( + items: items, + filter: prefixMatchFilter +) { selectedItems in + print("Selected:", selectedItems.map { $0.title }) +} +``` + +#### Example 3: Custom Scoring with FuzzyFind + +If you want to use the [FuzzyFind](https://github.com/truizlop/FuzzyFind) library instead of the built-in fuzzy matcher: + +```swift +import FloatingFilter +import FuzzyFind + +func fuzzyFindFilter(needle: String, haystack: [Item]) -> [Item] { + guard !needle.isEmpty else { return haystack } + + let matcher = FuzzyFind(matcher: FuzzyMatcher.init) + return matcher.match(needle, in: haystack) + .sorted(by: { $0.score > $1.score }) + .map { $0.item } +} + +FloatingFilterModule.showFilterWindow( + items: items, + filter: fuzzyFindFilter +) { selectedItems in + print("Selected:", selectedItems.map { $0.title }) +} +``` + +#### Example 4: Filter with Custom Sorting + +```swift +func filterByDate(needle: String, haystack: [Item]) -> [Item] { + // Combine your custom filter logic with any sorting you need + let filtered = haystack.filter { item in + item.title.localizedCaseInsensitiveContains(needle) + } + + // Sort by identifier (assuming it's a timestamp) + return filtered.sorted { item1, item2 in + if let date1 = item1.identifier as? Int, + let date2 = item2.identifier as? Int { + return date1 > date2 // Newest first + } + return false + } +} + +FloatingFilterModule.showFilterWindow( + items: items, + filter: filterByDate +) { selectedItems in + print("Selected:", selectedItems.map { $0.title }) +} +``` + +#### Default Fuzzy Filter + +If you want to use the built-in fuzzy matching with custom parameters: + +```swift +let customFilter = FloatingFilterModule.defaultFuzzyFilter( + fuzziness: 0.2, // Lower = more strict matching + threshold: 0.5 // Higher = fewer matches shown +) + +FloatingFilterModule.showFilterWindow( + items: items, + filter: customFilter +) { selectedItems in + print("Selected:", selectedItems.map { $0.title }) +} +``` + +**Filter Parameters:** + +- `fuzziness` (default: 0.3): How "fuzzy" the matching should be + - Lower values (0.0-0.2): More strict, exact-ish matches + - Higher values (0.4-0.5): More permissive, allow typos +- `threshold` (default: 0.4): Minimum score required to include a match + - Lower values (0.2-0.3): More results shown + - Higher values (0.5-0.6): Fewer, higher-quality results + ## Installation