diff --git a/README.md b/README.md index 8585a8c..5cdf255 100644 --- a/README.md +++ b/README.md @@ -16,11 +16,30 @@ colorscheme) -Fall is a fuzzy finder for Vim and Neovim, implemented in [Denops], and stands -for **"Filter All."** +Fall is a powerful, flexible fuzzy finder framework for Vim and Neovim, +implemented in [Denops], and stands for **"Filter All."** It provides an +extensible architecture that allows users to search, filter, and act on various +data sources including files, buffers, lines, help tags, and more through a +unified interface. + +## Key Features + +- **Extensible Architecture**: Modular design allows custom sources, matchers, + sorters, renderers, and previewers +- **Interactive Interface**: Quick help via F1, real-time filtering, and action + selection +- **Multiple Processing Stages**: Sophisticated pipeline with collection, + matching, sorting, rendering, and preview stages +- **Session Management**: Save and resume picker sessions with `:FallSession` + and `:FallResume` +- **Submatch Capabilities**: Refine searches with multiple filter criteria +- **Performance Optimized**: Asynchronous processing with intelligent scheduling + and chunking +- **Rich Customization**: Configure behavior through TypeScript extensions and + Vim script settings See [Features](https://github.com/vim-fall/fall.vim/wiki/Features) for more -information about Fall's features. +detailed information. > [!WARNING] > @@ -62,16 +81,44 @@ following arguments: Fall {source} {source_args}... ``` -For example, if you'd like to use the `file` source, you can use the following: +### Common Examples -``` -Fall file -``` +```vim +" Find files in current directory +:Fall file -Or use the `line` source with `README.md` as an argument: +" Find files in specific directory +:Fall file /path/to/directory -``` -Fall line README.md +" Search file contents with grep +:Fall grep + +" Search in git repository +:Fall git-grep + +" Search with ripgrep (faster) +:Fall rg + +" Filter lines in current buffer +:Fall line + +" Filter lines in specific file +:Fall line README.md + +" Switch between buffers +:Fall buffer + +" Browse help tags +:Fall help + +" Browse command history +:Fall history + +" Resume previous picker +:FallResume + +" Manage picker sessions +:FallSession ``` ### Mappings @@ -103,14 +150,28 @@ Visit the [Customization](https://github.com/vim-fall/fall.vim/wiki/Customization) page on the GitHub Wiki for more information. +## Architecture + +Fall follows a modular architecture with these core components: + +- **Sources**: Data providers (files, buffers, grep results, etc.) +- **Matchers**: Filter algorithms (fuzzy, substring, regex) +- **Sorters**: Result ordering strategies (alphabetical, length, score) +- **Renderers**: Display formatters (with optional NerdFont icons) +- **Previewers**: Content preview generators +- **Actions**: Operations on selected items (open, edit, split, etc.) + +Components communicate through a well-defined pipeline, allowing for extensive +customization and extension. + ## Related Projects -| Repository | Package | Description | -| ------------------------------------------------------------------------- | ----------------------------------------------------- | ------------------------------------------------ | -| [vim-fall/deno-fall-core](https://github.com/vim-fall/deno-fall-core) | [`@vim-fall/core`](https://jsr.io/@vim-fall/core) | Core types for Fall. Not meant for external use. | -| [vim-fall/deno-fall-custom](https://github.com/vim-fall/deno-fall-custom) | [`@vim-fall/custom`](https://jsr.io/@vim-fall/custom) | Library to customize Fall. | -| [vim-fall/deno-fall-std](https://github.com/vim-fall/deno-fall-std) | [`@vim-fall/std`](https://jsr.io/@vim-fall/std) | Standard library for using Fall. | -| [vim-fall/deno-fall-extra](https://github.com/vim-fall/deno-fall-extra) | [`@vim-fall/extra`](https://jsr.io/@vim-fall/extra) | Extra library for using Fall. | +| Repository | Package | Description | +| ------------------------------------------------------------------------- | ----------------------------------------------------- | --------------------------------------------- | +| [vim-fall/deno-fall-core](https://github.com/vim-fall/deno-fall-core) | [`@vim-fall/core`](https://jsr.io/@vim-fall/core) | Core types and interfaces for Fall extensions | +| [vim-fall/deno-fall-custom](https://github.com/vim-fall/deno-fall-custom) | [`@vim-fall/custom`](https://jsr.io/@vim-fall/custom) | Customization utilities and helpers | +| [vim-fall/deno-fall-std](https://github.com/vim-fall/deno-fall-std) | [`@vim-fall/std`](https://jsr.io/@vim-fall/std) | Standard built-in components | +| [vim-fall/deno-fall-extra](https://github.com/vim-fall/deno-fall-extra) | [`@vim-fall/extra`](https://jsr.io/@vim-fall/extra) | Additional sources and extensions | ## Similar Projects diff --git a/denops/fall/component/help.ts b/denops/fall/component/help.ts index f52ae24..32d10f9 100644 --- a/denops/fall/component/help.ts +++ b/denops/fall/component/help.ts @@ -1,3 +1,20 @@ +/** + * @module component/help + * + * Help component for the vim-fall picker UI. + * + * This module provides the HelpComponent class which displays interactive help + * information to users. It supports: + * + * - Multi-page help content with navigation + * - Syntax highlighting through decorations + * - Dynamic content generation based on current mappings + * - Toggle visibility with F1 key + * + * The help component provides contextual assistance, showing available + * keyboard shortcuts and commands for the current picker. + */ + import type { Denops } from "jsr:@denops/std@^7.3.2"; import type { Decoration } from "jsr:@denops/std@^7.3.2/buffer"; import * as mapping from "jsr:@denops/std@^7.3.2/mapping"; diff --git a/denops/fall/component/input.ts b/denops/fall/component/input.ts index bc80746..923167e 100644 --- a/denops/fall/component/input.ts +++ b/denops/fall/component/input.ts @@ -1,3 +1,13 @@ +/** + * @module component/input + * + * Input component for the vim-fall picker UI. + * + * This module provides the InputComponent class which manages the user input area + * of the picker. It displays the current query, cursor position, collection/processing + * status, and provides visual feedback through spinners and status indicators. + */ + import type { Denops } from "jsr:@denops/std@^7.3.2"; import * as fn from "jsr:@denops/std@^7.3.2/function"; import * as buffer from "jsr:@denops/std@^7.3.2/buffer"; diff --git a/denops/fall/component/list.ts b/denops/fall/component/list.ts index da509e4..8b4eac1 100644 --- a/denops/fall/component/list.ts +++ b/denops/fall/component/list.ts @@ -1,3 +1,20 @@ +/** + * @module component/list + * + * List component for the vim-fall picker UI. + * + * This module provides the ListComponent class which manages the main list area + * of the picker where filtered and rendered items are displayed. It handles: + * + * - Rendering items with syntax highlighting + * - Managing item selection with visual indicators + * - Executing commands on the list buffer + * - Handling decorations for match highlighting + * + * The list component works closely with the render processor to display + * items in a scrollable, interactive list format. + */ + import type { Denops } from "jsr:@denops/std@^7.3.2"; import type { Decoration } from "jsr:@denops/std@^7.3.2/buffer"; import { batch } from "jsr:@denops/std@^7.3.2/batch"; diff --git a/denops/fall/component/preview.ts b/denops/fall/component/preview.ts index 343de89..66d6e03 100644 --- a/denops/fall/component/preview.ts +++ b/denops/fall/component/preview.ts @@ -1,3 +1,20 @@ +/** + * @module component/preview + * + * Preview component for the vim-fall picker UI. + * + * This module provides the PreviewComponent class which displays previews of + * selected items in the picker. It supports: + * + * - Content preview with optional syntax highlighting + * - Cursor positioning at specific lines/columns + * - File type detection for proper highlighting + * - Command execution within the preview context + * + * The preview component enhances the user experience by showing item details + * before selection, making it easier to identify the desired item. + */ + import type { Denops } from "jsr:@denops/std@^7.3.2"; import * as fn from "jsr:@denops/std@^7.3.2/function"; import * as buffer from "jsr:@denops/std@^7.3.2/buffer"; diff --git a/denops/fall/custom.ts b/denops/fall/custom.ts index 7af1788..96e6c97 100644 --- a/denops/fall/custom.ts +++ b/denops/fall/custom.ts @@ -1,3 +1,22 @@ +/** + * @module custom + * + * Custom configuration management for vim-fall. + * + * This module handles loading, managing, and reloading user customizations + * for vim-fall. It provides APIs for: + * + * - Loading user custom configuration files + * - Managing global settings (theme, coordinator) + * - Registering custom pickers + * - Configuring action pickers + * - Editing and reloading configurations + * + * The custom system allows users to define their own pickers, customize + * existing ones, and configure the overall appearance and behavior of + * vim-fall through a TypeScript configuration file. + */ + import type { Denops } from "jsr:@denops/std@^7.3.2"; import * as buffer from "jsr:@denops/std@^7.3.2/buffer"; import * as vars from "jsr:@denops/std@^7.3.2/variable"; @@ -53,7 +72,22 @@ let actionPickerParams = { ...defaultActionPickerParams }; const pickerParamsMap = new Map(); /** - * Edit user custom + * Opens the user custom configuration file for editing. + * + * This function: + * - Creates the custom file from a template if it doesn't exist + * - Opens the file in a new buffer + * - Sets up auto-reload on save + * + * @param denops - The Denops instance + * @param options - Buffer open options (split, vsplit, etc.) + * @returns A promise that resolves when the file is opened + * + * @example + * ```typescript + * // Open custom file in a vertical split + * await editUserCustom(denops, { mods: "vertical" }); + * ``` */ export async function editUserCustom( denops: Denops, @@ -86,7 +120,28 @@ export async function editUserCustom( } /** - * Load user custom from the g:fall_config_path. + * Loads the user custom configuration from the path specified in g:fall_custom_path. + * + * This function: + * - Loads the custom TypeScript module + * - Executes its main function with the configuration context + * - Falls back to default configuration on error + * - Emits User:FallCustomLoaded autocmd on success + * + * @param denops - The Denops instance + * @param options - Loading options + * @param options.reload - Force reload even if already loaded + * @param options.verbose - Show loading messages + * @returns A promise that resolves when loading is complete + * + * @example + * ```typescript + * // Initial load + * await loadUserCustom(denops); + * + * // Force reload with verbose output + * await loadUserCustom(denops, { reload: true, verbose: true }); + * ``` */ export function loadUserCustom( denops: Denops, @@ -144,7 +199,26 @@ export function loadUserCustom( } /** - * Recache user custom by running `deno cache --reload` command. + * Recaches the user custom file and its dependencies. + * + * This function runs `deno cache --reload` on the custom file to: + * - Download and update all dependencies + * - Recompile TypeScript code + * - Clear the module cache + * + * After recaching, Vim must be restarted for changes to take effect. + * + * @param denops - The Denops instance + * @param options - Recache options + * @param options.verbose - Show cache progress + * @param options.signal - AbortSignal to cancel the operation + * @returns A promise that resolves when recaching is complete + * + * @example + * ```typescript + * // Recache with progress output + * await recacheUserCustom(denops, { verbose: true }); + * ``` */ export async function recacheUserCustom( denops: Denops, @@ -199,14 +273,32 @@ export async function recacheUserCustom( } /** - * Get global custom. + * Gets the current global settings. + * + * @returns The current setting configuration including theme and coordinator + * + * @example + * ```typescript + * const settings = getSetting(); + * console.log("Current theme:", settings.theme); + * ``` */ export function getSetting(): Readonly { return setting; } /** - * Get action picker params. + * Gets the current action picker parameters. + * + * Action pickers are used for selecting actions to perform on items. + * + * @returns The current action picker configuration + * + * @example + * ```typescript + * const params = getActionPickerParams(); + * console.log("Action picker matchers:", params.matchers); + * ``` */ export function getActionPickerParams(): Readonly< ActionPickerParams @@ -215,7 +307,18 @@ export function getActionPickerParams(): Readonly< } /** - * Get item picker params. + * Gets the parameters for a specific picker by name. + * + * @param name - The name of the picker + * @returns The picker parameters if found, undefined otherwise + * + * @example + * ```typescript + * const filePickerParams = getPickerParams("file"); + * if (filePickerParams) { + * console.log("File picker source:", filePickerParams.source); + * } + * ``` */ export function getPickerParams( name: string, @@ -228,18 +331,42 @@ export function getPickerParams( } /** - * List item picker names. + * Lists all registered picker names. + * + * @returns An array of all registered picker names + * + * @example + * ```typescript + * const pickers = listPickerNames(); + * console.log("Available pickers:", pickers); + * // Output: ["file", "grep", "buffer", ...] + * ``` */ export function listPickerNames(): readonly string[] { return Array.from(pickerParamsMap.keys()); } +/** + * Resets all custom configurations to their defaults. + * This is called before loading/reloading custom configurations. + */ function reset(): void { setting = { ...defaultSetting }; actionPickerParams = { ...defaultActionPickerParams }; pickerParamsMap.clear(); } +/** + * Builds the context object passed to custom configuration files. + * + * This context provides APIs for: + * - Refining global settings + * - Configuring action pickers + * - Defining new pickers from sources or curators + * + * @param denops - The Denops instance + * @returns The configuration context object + */ function buildContext(denops: Denops): { denops: Denops; refineSetting: ReturnType; @@ -266,6 +393,13 @@ function buildContext(denops: Denops): { }; } +/** + * Gets the URL of the user custom file from g:fall_custom_path. + * + * @param denops - The Denops instance + * @returns The file URL of the custom configuration + * @throws Error if g:fall_custom_path is not set or invalid + */ async function getUserCustomUrl(denops: Denops): Promise { try { const path = await vars.g.get(denops, "fall_custom_path") as string; @@ -277,6 +411,12 @@ async function getUserCustomUrl(denops: Denops): Promise { } } +/** + * Validates a picker name to ensure it's valid and not already used. + * + * @param name - The picker name to validate + * @throws ExpectedError if the name is invalid or already exists + */ function validatePickerName(name: string): void { if (pickerParamsMap.has(name)) { throw new ExpectedError(`Picker '${name}' is already defined.`); @@ -286,6 +426,12 @@ function validatePickerName(name: string): void { } } +/** + * Validates action names to ensure they don't use reserved prefixes. + * + * @param actions - The actions object to validate + * @throws ExpectedError if any action name starts with '@' + */ function validateActions(actions: Record): void { Object.keys(actions).forEach((name) => { if (name.startsWith("@")) { diff --git a/denops/fall/main.ts b/denops/fall/main.ts index 80a774c..c44eb53 100644 --- a/denops/fall/main.ts +++ b/denops/fall/main.ts @@ -1,3 +1,17 @@ +/** + * @module main + * + * Main entry point for the vim-fall Denops plugin. + * + * This module serves as the primary initialization point for vim-fall, a fuzzy finder + * framework for Vim/Neovim built with Denops. It coordinates the initialization of + * all major subsystems including custom configurations, event handling, picker + * functionality, and submatch features. + * + * The module imports and executes initialization functions for each subsystem in a + * specific order to ensure proper setup of the plugin's functionality. + */ + import "./lib/polyfill.ts"; import type { Entrypoint } from "jsr:@denops/std@^7.3.2"; @@ -7,6 +21,26 @@ import { main as mainEvent } from "./main/event.ts"; import { main as mainPicker } from "./main/picker.ts"; import { main as mainSubmatch } from "./main/submatch.ts"; +/** + * Main entry point function for the vim-fall plugin. + * + * This function is called by Denops when the plugin is loaded. It initializes + * all the plugin's subsystems in the following order: + * + * 1. Custom configurations - Loads user-defined settings and customizations + * 2. Event system - Sets up the internal event handling mechanism + * 3. Picker system - Initializes the core picker functionality + * 4. Submatch system - Sets up submatch highlighting capabilities + * + * @param denops - The Denops instance provided by the Denops plugin system + * @returns A promise that resolves when all subsystems are initialized + * + * @example + * ```typescript + * // This function is automatically called by Denops + * // No manual invocation is required + * ``` + */ export const main: Entrypoint = async (denops) => { await mainCustom(denops); await mainEvent(denops); diff --git a/denops/fall/picker.ts b/denops/fall/picker.ts index a541d5b..520f4be 100644 --- a/denops/fall/picker.ts +++ b/denops/fall/picker.ts @@ -1,3 +1,26 @@ +/** + * @module picker + * + * Core picker implementation for vim-fall. + * + * This module provides the main `Picker` class which orchestrates the entire fuzzy finding + * experience. It manages the lifecycle of a picker session including: + * + * - User input handling through the input component + * - Item collection from sources + * - Filtering items through matchers + * - Sorting filtered results + * - Rendering items in the list component + * - Previewing selected items + * - Managing user interactions and events + * + * The picker follows a pipeline architecture where items flow through: + * Source -> Collector -> Matcher -> Sorter -> Renderer -> Preview + * + * Each stage can be customized with different implementations to create + * various types of pickers (file finder, grep, buffer list, etc.). + */ + import type { Denops } from "jsr:@denops/std@^7.3.2"; import * as opt from "jsr:@denops/std@^7.3.2/option"; import * as autocmd from "jsr:@denops/std@^7.3.2/autocmd"; @@ -44,52 +67,159 @@ const SORTER_ICON = "🆂 "; const RENDERER_ICON = "🆁 "; const PREVIEWER_ICON = "🅿 "; +/** + * Callback function type for reserved operations that need to be executed + * asynchronously during the picker's main loop. + */ type ReservedCallback = ( denops: Denops, options: { signal?: AbortSignal }, ) => void | Promise; +/** + * Configuration parameters for creating a new Picker instance. + * + * @template T - The type of detail data associated with each item + */ export type PickerParams = { + /** Unique name identifier for the picker */ name: string; + + /** Visual theme configuration for the picker UI */ theme: Theme; + + /** Layout coordinator that manages component positioning */ coordinator: Coordinator; + + /** Source that provides items to the picker */ source: Source; + + /** Array of matchers for filtering items (at least one required) */ matchers: readonly [Matcher, ...Matcher[]]; + + /** Optional array of sorters for ordering filtered items */ sorters?: readonly Sorter[]; + + /** Optional array of renderers for displaying items */ renderers?: readonly Renderer[]; + + /** Optional array of previewers for showing item details */ previewers?: readonly Previewer[]; + + /** Z-index for layering picker windows (default: 50) */ zindex?: number; + + /** Optional context to restore previous picker state */ context?: PickerContext; }; +/** + * Result returned when a picker session completes. + * + * @template T - The type of detail data associated with each item + */ export type PickerResult = { + /** The action name if an action was invoked (e.g., "edit", "split") */ readonly action?: string; + + /** The final query string entered by the user */ readonly query: string; + + /** The currently selected item, if any */ readonly item: Readonly> | undefined; + + /** Array of multi-selected items, if any were selected */ readonly selectedItems: Readonly>[] | undefined; + + /** All items that passed the current filter */ readonly filteredItems: Readonly>[]; }; +/** + * Optional configuration for Picker behavior. + */ export type PickerOptions = { + /** Interval in milliseconds for the scheduler loop (default: 10) */ schedulerInterval?: number; previewDebounceDelay?: number; }; +/** + * Context state of a picker that can be saved and restored. + * Useful for implementing picker resume functionality. + * + * @template T - The type of detail data associated with each item + */ export type PickerContext = { + /** The current query string */ readonly query: string; + + /** Set of selected item IDs */ readonly selection: Set; + + /** All collected items from the source */ readonly collectedItems: readonly IdItem[]; + + /** Items that passed the current filter */ readonly filteredItems: readonly IdItem[]; + + /** Current cursor position in the list */ readonly cursor: number; + + /** Current scroll offset in the list */ readonly offset: number; + + /** Index of the active matcher */ readonly matcherIndex: number; + + /** Index of the active sorter */ readonly sorterIndex: number; + + /** Index of the active renderer */ readonly rendererIndex: number; + + /** Index of the active previewer (if any) */ readonly previewerIndex?: number; }; +/** + * Main Picker class that orchestrates the fuzzy finding experience. + * + * The Picker manages the entire lifecycle of a fuzzy finding session, including: + * - Creating and managing UI components (input, list, preview, help) + * - Processing items through the collection -> match -> sort -> render pipeline + * - Handling user interactions and keyboard events + * - Managing component layout and resizing + * + * @template T - The type of detail data associated with each item + * + * @example + * ```typescript + * // Create a file picker + * const picker = new Picker({ + * name: "files", + * theme: MODERN_THEME, + * coordinator: modern(), + * source: file(), + * matchers: [fzf()], + * sorters: [alphabetical()], + * renderers: [nerdfont()], + * previewers: [file()], + * }); + * + * // Open and start the picker + * await using _ = await picker.open(denops, { signal }); + * const result = await picker.start(denops, { args: [] }); + * + * if (result?.item) { + * console.log("Selected:", result.item.value); + * } + * ``` + */ export class Picker implements AsyncDisposable { + /** Number of z-index levels allocated for picker components */ static readonly ZINDEX_ALLOCATION = 4; + readonly #stack = new AsyncDisposableStack(); readonly #schedulerInterval: number; readonly #previewDebounceDelay: number; @@ -112,6 +242,12 @@ export class Picker implements AsyncDisposable { readonly #previewerIcon: string; #selection: Set; + /** + * Creates a new Picker instance. + * + * @param params - Configuration parameters for the picker + * @param options - Optional behavior configuration + */ constructor(params: PickerParams, options: PickerOptions = {}) { this.#schedulerInterval = options.schedulerInterval ?? SCHEDULER_INTERVAL; this.#previewDebounceDelay = options.previewDebounceDelay ?? @@ -198,6 +334,14 @@ export class Picker implements AsyncDisposable { ); } + /** + * Gets the current context state of the picker. + * + * This context can be used to save and restore the picker state, + * enabling features like picker resume. + * + * @returns The current picker context + */ get context(): PickerContext { return { query: this.#inputComponent.cmdline, @@ -239,6 +383,27 @@ export class Picker implements AsyncDisposable { return `${mi} ${si} ${ri} ${pi}`.trim(); } + /** + * Opens the picker UI components and prepares them for interaction. + * + * This method: + * - Creates and positions all UI windows (input, list, preview) + * - Sets up window resize handlers + * - Emits picker enter/leave events + * + * The returned AsyncDisposable should be used with `await using` to ensure + * proper cleanup when the picker closes. + * + * @param denops - The Denops instance + * @param options - Options including an optional AbortSignal + * @returns An AsyncDisposable for cleanup + * + * @example + * ```typescript + * await using _ = await picker.open(denops, { signal }); + * // Picker is now open and ready for use + * ``` + */ async open( denops: Denops, { signal }: { signal?: AbortSignal }, @@ -338,6 +503,29 @@ export class Picker implements AsyncDisposable { return stack.move(); } + /** + * Starts the picker interaction loop. + * + * This method: + * - Begins collecting items from the source + * - Starts the main event loop to handle user input + * - Processes items through the pipeline (match, sort, render) + * - Returns when the user accepts or cancels + * + * @param denops - The Denops instance + * @param params - Parameters including source arguments + * @param options - Options including an optional AbortSignal + * @returns The picker result if accepted, undefined if cancelled + * + * @example + * ```typescript + * const result = await picker.start(denops, { args: ["--hidden"] }); + * if (result) { + * console.log("Selected:", result.item?.value); + * console.log("Action:", result.action); + * } + * ``` + */ async start( denops: Denops, { args }: { args: readonly string[] }, @@ -426,6 +614,12 @@ export class Picker implements AsyncDisposable { }; } + /** + * Handles item selection at the specified cursor position. + * + * @param cursor - The cursor position or "$" for last item + * @param method - Selection method: "on" to select, "off" to deselect, "toggle" to toggle + */ #select( cursor?: number | "$", method: "on" | "off" | "toggle" = "toggle", @@ -459,6 +653,11 @@ export class Picker implements AsyncDisposable { } } + /** + * Handles selection of all filtered items. + * + * @param method - Selection method: "on" to select all, "off" to deselect all, "toggle" to toggle all + */ #selectAll( method: "on" | "off" | "toggle" = "toggle", ): void { @@ -485,6 +684,20 @@ export class Picker implements AsyncDisposable { } } + /** + * Central event handler for all picker events. + * + * This method processes events from: + * - User input (keyboard/commands) + * - Component updates + * - Processor state changes + * + * Events may trigger immediate actions or reserve callbacks for + * asynchronous processing in the next scheduler tick. + * + * @param event - The event to handle + * @param handlers - Accept and reserve callback handlers + */ #handleEvent(event: Event, { accept, reserve, reservePreviewDebounced }: { accept: (name: string) => Promise; reserve: (callback: ReservedCallback) => void; @@ -808,11 +1021,23 @@ export class Picker implements AsyncDisposable { } } + /** + * Async disposal method for cleaning up picker resources. + * + * This is called automatically when using `await using` syntax. + * It ensures all components and processors are properly disposed. + */ [Symbol.asyncDispose]() { return this.#stack[Symbol.asyncDispose](); } } +/** + * Gets the current screen size from Vim. + * + * @param denops - The Denops instance + * @returns The screen dimensions + */ async function getScreenSize(denops: Denops): Promise { const [width, height] = await collect(denops, (denops) => [ opt.columns.get(denops), diff --git a/denops/fall/processor/collect.ts b/denops/fall/processor/collect.ts index 1ae3652..8b02649 100644 --- a/denops/fall/processor/collect.ts +++ b/denops/fall/processor/collect.ts @@ -1,3 +1,21 @@ +/** + * @module processor/collect + * + * Collection processor for vim-fall. + * + * This module provides the CollectProcessor class which manages the collection + * of items from a source. It handles: + * + * - Asynchronous item collection with progress updates + * - Chunking for performance optimization + * - Item deduplication and ID assignment + * - Pause/resume functionality + * - Threshold limiting to prevent memory issues + * + * The processor emits events during collection to update the UI and coordinate + * with other components. + */ + import type { Denops } from "jsr:@denops/std@^7.3.2"; import { take } from "jsr:@core/iterutil@^0.9.0/async/take"; import { map } from "jsr:@core/iterutil@^0.9.0/map"; @@ -8,17 +26,60 @@ import { Chunker } from "../lib/chunker.ts"; import { UniqueOrderedList } from "../lib/unique_ordered_list.ts"; import { dispatch } from "../event.ts"; +/** Default maximum number of items to collect */ const THRESHOLD = 100000; + +/** Default number of items to process in each chunk */ const CHUNK_SIZE = 1000; + +/** Default interval in milliseconds between chunk updates */ const CHUNK_INTERVAL = 100; +/** + * Configuration options for the CollectProcessor. + * + * @template T - The type of detail data associated with each item + */ export type CollectProcessorOptions = { + /** Initial items to populate the processor with (useful for resume) */ initialItems?: readonly IdItem[]; + + /** Maximum number of items to collect (default: 100000) */ threshold?: number; + + /** Number of items to process before emitting an update (default: 1000) */ chunkSize?: number; + + /** Maximum time in ms between updates regardless of chunk size (default: 100) */ chunkInterval?: number; }; +/** + * Processor responsible for collecting items from a source. + * + * The CollectProcessor manages the asynchronous collection of items from a source, + * handling chunking, deduplication, and progress updates. It can be paused and + * resumed, making it suitable for long-running collection operations. + * + * @template T - The type of detail data associated with each item + * + * @example + * ```typescript + * const processor = new CollectProcessor(fileSource, { + * threshold: 50000, + * chunkSize: 500, + * }); + * + * // Start collecting + * processor.start(denops, { args: ["--hidden"] }); + * + * // Access collected items + * console.log(processor.items.length); + * + * // Pause if needed + * processor.pause(); + * ``` + */ export class CollectProcessor implements Disposable { readonly #controller: AbortController = new AbortController(); readonly #items: UniqueOrderedList>; @@ -28,6 +89,12 @@ export class CollectProcessor implements Disposable { #processing?: Promise; #paused?: PromiseWithResolvers; + /** + * Creates a new CollectProcessor. + * + * @param source - The source to collect items from + * @param options - Configuration options + */ constructor( readonly source: Source, options: CollectProcessorOptions = {}, @@ -45,10 +112,20 @@ export class CollectProcessor implements Disposable { ); } + /** + * Gets the currently collected items. + * + * @returns An array of collected items with assigned IDs + */ get items(): readonly IdItem[] { return this.#items.items; } + /** + * Validates that the processor is not disposed. + * + * @throws Error if the processor is disposed + */ #validateAvailability(): void { try { this.#controller.signal.throwIfAborted(); @@ -60,6 +137,26 @@ export class CollectProcessor implements Disposable { } } + /** + * Starts or resumes the collection process. + * + * If collection is already in progress and paused, this will resume it. + * Otherwise, it starts a new collection process. + * + * The method emits the following events: + * - `collect-processor-started`: When collection begins + * - `collect-processor-updated`: When new items are collected + * - `collect-processor-succeeded`: When collection completes successfully + * - `collect-processor-failed`: If an error occurs + * + * @param denops - The Denops instance + * @param params - Parameters to pass to the source's collect method + * + * @example + * ```typescript + * processor.start(denops, { args: ["--hidden", "--no-ignore"] }); + * ``` + */ start( denops: Denops, params: CollectParams, @@ -107,6 +204,20 @@ export class CollectProcessor implements Disposable { }); } + /** + * Pauses the collection process. + * + * The collection can be resumed by calling `start()` again. + * This is useful for temporarily stopping collection to free up + * resources or when the user navigates away. + * + * @example + * ```typescript + * processor.pause(); + * // Later... + * processor.start(denops, params); // Resumes from where it left off + * ``` + */ pause(): void { this.#validateAvailability(); if (!this.#processing) { @@ -118,6 +229,9 @@ export class CollectProcessor implements Disposable { }); } + /** + * Resumes a paused collection process. + */ #resume(): void { if (!this.#paused) { return; @@ -126,6 +240,12 @@ export class CollectProcessor implements Disposable { this.#paused = undefined; } + /** + * Disposes of the processor and cancels any ongoing collection. + * + * This method is called automatically when the processor is no longer needed. + * It aborts the collection process and cleans up resources. + */ [Symbol.dispose](): void { try { this.#controller.abort(null); diff --git a/denops/fall/processor/match.ts b/denops/fall/processor/match.ts index 1b201b7..3d34df5 100644 --- a/denops/fall/processor/match.ts +++ b/denops/fall/processor/match.ts @@ -1,3 +1,21 @@ +/** + * @module processor/match + * + * Matching processor for vim-fall. + * + * This module provides the MatchProcessor class which filters items based on + * user queries using configurable matchers. It supports: + * + * - Multiple matcher implementations (fuzzy, substring, regex, etc.) + * - Incremental matching for performance optimization + * - Asynchronous processing with chunking + * - Query caching to avoid redundant processing + * - Matcher switching during runtime + * + * The processor transforms collected items into filtered items that match + * the current query, emitting events to coordinate with other components. + */ + import type { Denops } from "jsr:@denops/std@^7.3.2"; import { delay } from "jsr:@std/async@^1.0.0/delay"; import { take } from "jsr:@core/iterutil@^0.9.0/async/take"; @@ -8,22 +26,75 @@ import { Chunker } from "../lib/chunker.ts"; import { ItemBelt } from "../lib/item_belt.ts"; import { dispatch } from "../event.ts"; +/** Default delay interval between processing cycles */ const INTERVAL = 0; + +/** Default maximum number of items to process */ const THRESHOLD = 100000; + +/** Default number of items to process in each chunk */ const CHUNK_SIZE = 1000; + +/** Default interval in milliseconds between chunk updates */ const CHUNK_INTERVAL = 100; +/** + * Configuration options for the MatchProcessor. + * + * @template T - The type of detail data associated with each item + */ export type MatchProcessorOptions = { + /** Initial filtered items (useful for resume) */ initialItems?: readonly IdItem[]; + + /** Initial query string */ initialQuery?: string; + + /** Initial matcher index */ initialIndex?: number; + + /** Delay between processing cycles in ms (default: 0) */ interval?: number; + + /** Maximum items to process (default: 100000) */ threshold?: number; + + /** Items per chunk (default: 1000) */ chunkSize?: number; + + /** Max time between chunk updates in ms (default: 100) */ chunkInterval?: number; + + /** Enable incremental matching mode for better performance */ incremental?: boolean; }; +/** + * Processor responsible for filtering items based on user queries. + * + * The MatchProcessor applies matchers to filter collected items according to + * the current query. It supports multiple matchers that can be switched + * dynamically, and provides both standard and incremental matching modes. + * + * @template T - The type of detail data associated with each item + * + * @example + * ```typescript + * const processor = new MatchProcessor([fzf(), substring()], { + * incremental: true, + * chunkSize: 500, + * }); + * + * // Start matching + * processor.start(denops, { + * items: collectedItems, + * query: "search term", + * }); + * + * // Switch matcher + * processor.matcherIndex = 1; + * ``` + */ export class MatchProcessor implements Disposable { readonly matchers: ItemBelt>; readonly #interval: number; @@ -57,18 +128,34 @@ export class MatchProcessor implements Disposable { return this.matchers.current!; } + /** + * Gets the currently filtered items. + * + * @returns Array of items that match the current query + */ get items(): IdItem[] { return this.#items; } + /** + * Gets the total number of available matchers. + */ get matcherCount(): number { return this.matchers.count; } + /** + * Gets the current matcher index. + */ get matcherIndex(): number { return this.matchers.index; } + /** + * Sets the current matcher index. + * + * @param index - The matcher index or "$" for the last matcher + */ set matcherIndex(index: number | "$") { if (index === "$") { index = this.matchers.count; @@ -87,6 +174,34 @@ export class MatchProcessor implements Disposable { } } + /** + * Starts the matching process. + * + * This method filters the provided items based on the query using the + * current matcher. It handles: + * - Query caching to avoid redundant processing + * - Incremental matching when enabled + * - Asynchronous processing with progress updates + * + * The method emits: + * - `match-processor-started`: When matching begins + * - `match-processor-updated`: When new matches are found + * - `match-processor-succeeded`: When matching completes + * - `match-processor-failed`: If an error occurs + * + * @param denops - The Denops instance + * @param params - Items to filter and the query string + * @param options - Optional configuration + * @param options.restart - Force restart even if processing + * + * @example + * ```typescript + * processor.start(denops, { + * items: collectedItems, + * query: "search term", + * }); + * ``` + */ start( denops: Denops, { items, query }: MatchParams, @@ -158,6 +273,12 @@ export class MatchProcessor implements Disposable { }); } + /** + * Disposes of the processor and cancels any ongoing matching. + * + * This method is called automatically when the processor is no longer needed. + * It aborts the matching process and cleans up resources. + */ [Symbol.dispose](): void { try { this.#controller.abort(null); diff --git a/denops/fall/processor/preview.ts b/denops/fall/processor/preview.ts index 82e1a68..8253c09 100644 --- a/denops/fall/processor/preview.ts +++ b/denops/fall/processor/preview.ts @@ -1,3 +1,20 @@ +/** + * @module processor/preview + * + * Preview processor for vim-fall. + * + * This module provides the PreviewProcessor class which generates preview + * content for selected items. It supports: + * + * - Multiple previewer implementations (file, buffer, help, etc.) + * - Asynchronous preview generation + * - Dynamic previewer switching + * - Cancellation of long-running preview operations + * + * The processor works with the preview component to display item details + * before the user makes a selection. + */ + import type { Denops } from "jsr:@denops/std@^7.3.2"; import type { Detail, PreviewItem } from "jsr:@vim-fall/core@^0.3.0/item"; import type { @@ -8,10 +25,40 @@ import type { import { ItemBelt } from "../lib/item_belt.ts"; import { dispatch } from "../event.ts"; +/** + * Configuration options for the PreviewProcessor. + */ export type PreviewProcessorOptions = { + /** Initial previewer index to use */ initialIndex?: number; }; +/** + * Processor responsible for generating preview content for items. + * + * The PreviewProcessor uses configured previewers to generate preview + * content for the currently selected item. Preview generation is + * asynchronous and can be cancelled if the user navigates away. + * + * @template T - The type of detail data associated with each item + * + * @example + * ```typescript + * const processor = new PreviewProcessor([ + * file(), + * buffer(), + * help(), + * ]); + * + * // Start preview generation + * processor.start(denops, { item: selectedItem }); + * + * // Access preview content + * if (processor.item) { + * console.log("Preview:", processor.item.content); + * } + * ``` + */ export class PreviewProcessor implements Disposable { readonly #controller: AbortController = new AbortController(); readonly previewers: ItemBelt>; @@ -32,14 +79,25 @@ export class PreviewProcessor implements Disposable { return this.previewers.current; } + /** + * Gets the total number of available previewers. + */ get previewerCount(): number { return this.previewers.count; } + /** + * Gets the current previewer index. + */ get previewerIndex(): number { return this.previewers.index; } + /** + * Sets the current previewer index. + * + * @param index - The previewer index or "$" for the last previewer + */ set previewerIndex(index: number | "$") { if (index === "$") { index = this.previewers.count; @@ -47,6 +105,11 @@ export class PreviewProcessor implements Disposable { this.previewers.index = index; } + /** + * Gets the generated preview item. + * + * @returns The preview content or undefined if no preview is available + */ get item(): PreviewItem | undefined { return this.#item; } @@ -62,6 +125,30 @@ export class PreviewProcessor implements Disposable { } } + /** + * Starts the preview generation process. + * + * This method generates preview content for the provided item using + * the current previewer. If no item is provided, the preview is cleared. + * Preview generation is asynchronous and can be cancelled. + * + * The method emits: + * - `preview-processor-started`: When preview generation begins + * - `preview-processor-succeeded`: When preview is ready + * - `preview-processor-failed`: If an error occurs + * + * @param denops - The Denops instance + * @param params - Object containing the item to preview + * + * @example + * ```typescript + * // Generate preview for an item + * processor.start(denops, { item: selectedItem }); + * + * // Clear preview + * processor.start(denops, { item: undefined }); + * ``` + */ start(denops: Denops, { item }: PreviewParams): void { this.#validateAvailability(); if (this.#processing) { @@ -99,6 +186,12 @@ export class PreviewProcessor implements Disposable { }); } + /** + * Disposes of the processor and cancels any ongoing preview generation. + * + * This method is called automatically when the processor is no longer needed. + * It aborts the preview process and cleans up resources. + */ [Symbol.dispose](): void { try { this.#controller.abort(null); diff --git a/denops/fall/processor/render.ts b/denops/fall/processor/render.ts index 65583f1..6b05aca 100644 --- a/denops/fall/processor/render.ts +++ b/denops/fall/processor/render.ts @@ -95,6 +95,11 @@ export class RenderProcessor implements Disposable { this.#cursor = cursor; } + /** + * Gets the current scroll offset. + * + * @returns 0-based offset of the visible window + */ get offset(): number { return this.#offset; } @@ -109,10 +114,20 @@ export class RenderProcessor implements Disposable { ); } + /** + * Gets the current list height. + */ get height(): number { return this.#height; } + /** + * Sets the list height. + * + * Adjusts the offset if necessary to maintain cursor visibility. + * + * @param height - The new list height + */ set height(height: number) { this.#height = height; this.#adjustOffset(); @@ -138,6 +153,26 @@ export class RenderProcessor implements Disposable { } } + /** + * Starts the rendering process. + * + * This method renders items for display, applying the current renderer + * to format items with appropriate decorations. Only items within the + * visible window are rendered for performance. + * + * The method emits: + * - `render-processor-started`: When rendering begins + * - `render-processor-succeeded`: When rendering completes + * - `render-processor-failed`: If an error occurs + * + * @param denops - The Denops instance + * @param params - Object containing the items to render + * + * @example + * ```typescript + * processor.start(denops, { items: sortedItems }); + * ``` + */ start( denops: Denops, { items }: { items: readonly Readonly>[] }, diff --git a/denops/fall/processor/sort.ts b/denops/fall/processor/sort.ts index 566a4ed..bf6da31 100644 --- a/denops/fall/processor/sort.ts +++ b/denops/fall/processor/sort.ts @@ -1,3 +1,20 @@ +/** + * @module processor/sort + * + * Sorting processor for vim-fall. + * + * This module provides the SortProcessor class which orders filtered items + * using configurable sorters. It supports: + * + * - Multiple sorter implementations (alphabetical, length, score-based) + * - Dynamic sorter switching during runtime + * - In-place sorting for efficiency + * - Asynchronous processing with proper cancellation + * + * The processor transforms filtered items into sorted items, maintaining + * the pipeline flow from matcher to renderer. + */ + import type { Denops } from "jsr:@denops/std@^7.3.2"; import type { Detail, IdItem } from "jsr:@vim-fall/core@^0.3.0/item"; import type { Sorter } from "jsr:@vim-fall/core@^0.3.0/sorter"; @@ -5,10 +22,39 @@ import type { Sorter } from "jsr:@vim-fall/core@^0.3.0/sorter"; import { ItemBelt } from "../lib/item_belt.ts"; import { dispatch } from "../event.ts"; +/** + * Configuration options for the SortProcessor. + */ export type SortProcessorOptions = { + /** Initial sorter index to use */ initialIndex?: number; }; +/** + * Processor responsible for sorting filtered items. + * + * The SortProcessor applies sorters to order items after they've been + * filtered by the matcher. Sorting is performed in-place on the item + * array for efficiency. Multiple sorters can be configured and switched + * between dynamically. + * + * @template T - The type of detail data associated with each item + * + * @example + * ```typescript + * const processor = new SortProcessor([ + * alphabetical(), + * byLength(), + * byScore(), + * ]); + * + * // Start sorting + * processor.start(denops, { items: filteredItems }); + * + * // Switch sorter + * processor.sorterIndex = 1; + * ``` + */ export class SortProcessor implements Disposable { readonly #controller: AbortController = new AbortController(); readonly sorters: ItemBelt>; @@ -29,14 +75,25 @@ export class SortProcessor implements Disposable { return this.sorters.current; } + /** + * Gets the total number of available sorters. + */ get sorterCount(): number { return this.sorters.count; } + /** + * Gets the current sorter index. + */ get sorterIndex(): number { return this.sorters.index; } + /** + * Sets the current sorter index. + * + * @param index - The sorter index or "$" for the last sorter + */ set sorterIndex(index: number | "$") { if (index === "$") { index = this.sorters.count; @@ -44,6 +101,11 @@ export class SortProcessor implements Disposable { this.sorters.index = index; } + /** + * Gets the sorted items. + * + * @returns The items after sorting has been applied + */ get items(): readonly IdItem[] { return this.#items; } @@ -59,6 +121,26 @@ export class SortProcessor implements Disposable { } } + /** + * Starts the sorting process. + * + * This method sorts the provided items using the current sorter. + * Sorting is performed in-place on the items array for efficiency. + * If no sorter is configured, items are passed through unchanged. + * + * The method emits: + * - `sort-processor-started`: When sorting begins + * - `sort-processor-succeeded`: When sorting completes + * - `sort-processor-failed`: If an error occurs + * + * @param denops - The Denops instance + * @param params - Object containing the items to sort + * + * @example + * ```typescript + * processor.start(denops, { items: filteredItems }); + * ``` + */ start(denops: Denops, { items }: { items: readonly IdItem[] }): void { this.#validateAvailability(); if (this.#processing) { @@ -96,6 +178,12 @@ export class SortProcessor implements Disposable { }); } + /** + * Disposes of the processor and cancels any ongoing sorting. + * + * This method is called automatically when the processor is no longer needed. + * It aborts the sorting process and cleans up resources. + */ [Symbol.dispose](): void { try { this.#controller.abort(null); diff --git a/doc/fall.txt b/doc/fall.txt index b97eeed..ea4751e 100644 --- a/doc/fall.txt +++ b/doc/fall.txt @@ -17,15 +17,21 @@ MODULE |fall-module| ============================================================================= INTRODUCTION *fall-introduction* -Fall (*fall.vim*) is a flexible Fuzzy Finder for Vim/Neovim. +Fall (*fall.vim*) is a powerful, flexible fuzzy finder framework for Vim and +Neovim. The name stands for "Filter All" and it provides an extensible +architecture that allows users to search, filter, and act on various data +sources through a unified interface. Key features include: - - Quick help via key. - - Support for multiple matchers, sorters, renderers, and previewers. - - Submatch capabilities for refined filtering. - - Action selector for invoking various actions. - - Extensible through TypeScript customization. + - Extensible architecture with modular components + - Quick help via key + - Support for multiple matchers, sorters, renderers, and previewers + - Submatch capabilities for refined filtering + - Action selector for invoking various actions + - Session management for resuming previous searches + - Asynchronous processing with performance optimization + - Rich customization through TypeScript and Vim script ============================================================================= REQUIREMENTS *fall-requirements* @@ -70,58 +76,94 @@ Sources are defined in "custom.ts" via |:FallCustom|. In default "custom.ts", the following sources are defined: Name Description~ -"grep" Filter files by its content using grep. -"git-grep" Filter files by its content using git-grep. -"rg" Filter files by its content using ripgrep (rg). -"file" Filter files in the directory. -"file:all" Filter files in the directory without static filters. -"line" Filter lines in the buffer. -"buffer" Filter buffers. -"help" Filter help tags. -"quickfix" Filter |quickfix| list. -"oldfiles" Filter |:oldfiles|. -"history" Filter command history. +"grep" Filter files by content using grep command +"git-grep" Filter files by content using git-grep (git repositories only) +"rg" Filter files by content using ripgrep (faster alternative) +"file" Filter files in the directory (respects gitignore) +"file:all" Filter all files in the directory (includes hidden/ignored) +"line" Filter lines in the current buffer or specified file +"buffer" Filter and switch between open buffers +"help" Filter and browse Vim help tags +"quickfix" Filter items in the |quickfix| list +"oldfiles" Filter recently edited files from |:oldfiles| +"history" Filter and execute from command history + +Each source can accept additional arguments. For example: +> + " Search in a specific directory + :Fall file ~/projects + + " Search specific file types with ripgrep + :Fall rg --type=python + + " Filter lines in a specific file + :Fall line /path/to/file.txt +< Users can define additional sources in "custom.ts" to suit their needs. See |fall-customization| for customization details. -Within the picker window, the following default mappings are applied. Note -that these may vary based on configuration, and can be confirmed via the -help prompt. +Within the picker window, the following default mappings are applied. These +can be confirmed via the help prompt. +Navigation~ +Key Description~ + Move cursor to the first item + Move cursor to the last item + Move cursor to the previous item + Move cursor to the next item + Scroll up by 'scroll' lines + Scroll down by 'scroll' lines + Scroll list content left + Scroll list content right + Scroll left by 'scroll' lines + Scroll right by 'scroll' lines + +Selection~ +Key Description~ + Toggle selection of the current item + Select all visible items + Select current item and move to next + Move to previous item and select it + +Preview Navigation~ +Key Description~ + Move to the first line of preview + Move to the last line of preview + Move to the previous line of preview + Move to the next line of preview + Scroll preview left + Scroll preview right + Scroll preview left by 'scroll' lines + Scroll preview right by 'scroll' lines + +Actions and Modes~ Key Description~ - Move cursor to the first item. - Move cursor to the last item. - Move cursor to the previous item. - Move cursor to the next item. - Scroll up by 'scroll' lines. - Scroll down by 'scroll' lines. - Scroll list content left. - Scroll list content right. - Scroll left by 'scroll' lines. - Scroll right by 'scroll' lines. - Select the current item. - Select all items. - Select the current item and move to the next item. - Move to the previous item and select it. - Move cursor to the first line of preview. - Move cursor to the last line of preview. - Move cursor to the previous line of preview. - Move cursor to the next line of preview. - Scroll preview left. - Scroll preview right. - Scroll preview left by 'scroll' lines. - Scroll preview right by 'scroll' lines. - Open the action selector. - Open or close help window. - Switch to the next matcher. - Switch to the next sorter. - Switch to the next renderer. - Switch to the next previewer. + Execute default action on selected item(s) + Open the action selector + Toggle help window + Cycle through available matchers + Cycle through available sorters + Cycle through available renderers + Cycle through available previewers Uses can define additional mappings via |FallPickerEnter| autocmd. See |fall-configuration| for details. +Session Management~ + +Fall provides session management commands to save and resume picker states: + + *:FallSession* +:FallSession Open the session manager to view and resume saved + sessions. Sessions preserve the picker state including + query, selections, and cursor position. + + *:FallResume* +:FallResume [{name}] + Resume the last picker session or a specific named session. + If {name} is provided, resume that specific session. + ============================================================================= CONFIGURATION *fall-configuration* @@ -729,64 +771,212 @@ Example usage: < Preview filetype is determined by the previewer; use |FallPreviewRendered|. +============================================================================= +ARCHITECTURE *fall-architecture* + +Fall employs a modular pipeline architecture where data flows through distinct +processing stages. Each stage is handled by specialized components that can be +independently customized or replaced. + +Processing Pipeline~ +> + Source -> Collector -> Matcher -> Sorter -> Renderer -> Display + | + v + Previewer +< +The pipeline operates asynchronously with intelligent scheduling to maintain +responsive user interaction while processing large datasets. + +Component Communication~ + +Components communicate through well-defined interfaces using TypeScript types +from the @vim-fall/core package. This ensures type safety and enables +third-party extensions. + +Performance Optimizations~ + +- Incremental processing: Only changed data is reprocessed +- Chunked operations: Large datasets are processed in chunks +- Debounced input: User input is debounced to reduce processing +- Lazy evaluation: Preview generation only occurs for visible items + ============================================================================= MODULE *fall-module* -Fall decomposes the functionality of a Fuzzy Finder into the following -modules: +Fall decomposes fuzzy finder functionality into the following modular +components. Each module type can have multiple implementations that users +can switch between or customize. *fall-module-coordinator* Coordinator~ - A module that determines the style and layout of the picker. Opens - the picker window. + Manages the overall layout and window positioning of the picker + interface. Coordinators determine how the input, list, and preview + components are arranged on screen. + + Built-in coordinators: + - modern: Floating windows with customizable dimensions + - compact: Compact layout with minimal spacing + - separate: Separate windows for each component *fall-module-theme* Theme~ - A theme structure used to define the style of the picker. It is passed - to the |fall-module-coordinator|. + Defines visual styling including borders, colors, and icons. Themes + work with coordinators to create cohesive visual experiences. + + Built-in themes: + - MODERN_THEME: Clean, modern appearance with rounded borders + - ASCII_THEME: ASCII art style borders + - DOUBLE_THEME: Double-line borders + - SINGLE_THEME: Single-line borders *fall-module-source* Source~ - Retrieves data from arbitrary sources and generates an - "AsyncIterableIterator" of items. This module is applied to the - internal collect processor. + Provides data from various sources like files, buffers, or external + commands. Sources generate items asynchronously, allowing Fall to + handle large datasets efficiently. + + Interface: Returns AsyncIterableIterator + Examples: file, buffer, line, grep, git-grep, rg *fall-module-matcher* Matcher~ - Filters the items received from the |fall-module-source| based on user - input and generates an "AsyncIterableIterator" of filtered items. It - is applied to the internal match processor. + Filters items based on user input using various algorithms. Matchers + can be incremental (reusing previous results) or non-incremental. + + Interface: (items, query) -> AsyncIterableIterator + Examples: fzf (fuzzy matching) *fall-module-curator* Curator~ - Combines |fall-module-source| and |fall-module-matcher|, performing - data retrieval and filtering based on user input. Mainly used for - live-grep like feature. It is transformed into |fall-module-source| - with |fall-module-matcher| internally. + A specialized component that combines source and matcher functionality + for scenarios where filtering happens at the source level (e.g., + live-grep). This enables more efficient processing by filtering + data before it enters the pipeline. + + Use cases: Live grep, database queries, API searches *fall-module-sorter* Sorter~ - Sorts items in-place received from the |fall-module-matcher| or - |fall-module-curator|. It is applied to the internal sort processor. + Orders filtered items using various strategies. Multiple sorters + can be chained to create complex sorting behaviors. + + Interface: (items) -> void (sorts in-place) + Examples: noop (no sorting), lexical (alphabetical) *fall-module-renderer* Renderer~ - Processes sorted items in-place from the |fall-module-sorter| for user - display. It is applied to the internal render processor. + Transforms items into displayable format with syntax highlighting, + icons, and formatting. Renderers can add visual enhancements without + modifying the underlying data. + + Interface: (items) -> void (modifies display properties) + Examples: nerdfont (adds icons), smartPath (intelligent path display) *fall-module-previewer* Previewer~ - Generates content for previewing the currently selected item. It is - applied to the internal preview processor. + Generates preview content for the currently selected item. Previewers + can display file contents, documentation, or any relevant information. + + Interface: (item) -> PreviewContent + Examples: file (with syntax highlighting), buffer, helptag *fall-module-action* Action~ - Executes actions on the selected target item. + Defines operations that can be performed on selected items. Actions + are invoked through the action selector or default key bindings. + + Interface: (denops, { selectedItems, item }) -> Promise + Examples: open, edit, split, vsplit, tabedit, cd, yank *fall-module-refiner* Refiner~ - Applies to |fall-module-source| or |fall-module-curator| to refine and - process generated items. + Post-processes items from sources or curators to add metadata, + transform data, or filter results. Refiners enable source composition + and data enrichment. + + Use cases: Adding file stats, filtering by type, data transformation + +============================================================================= +TROUBLESHOOTING *fall-troubleshooting* + +Common Issues~ + +1. Icons not displaying correctly + - Ensure your terminal uses a Nerd Font + - Disable nerdfont renderer in custom.ts if Nerd Fonts unavailable + - Check terminal encoding (should be UTF-8) + +2. Picker not opening or crashing + - Verify Deno 2.x is installed: `:echo denops#server#status()` + - Check Denops server: `:DenopsHealth` + - Review error messages: `:messages` + +3. Slow performance + - Use ripgrep (rg) instead of grep for file content search + - Limit search scope with more specific paths + - Adjust chunk size in custom configuration + - Disable preview for large files + +4. Custom source not working + - Ensure source returns proper item structure + - Check Deno console for runtime errors + - Review TypeScript syntax errors in the editor + +5. Key mappings not working + - Confirm no conflicts: `:verbose cmap ` + - Check if inside picker window during mapping + - Verify autocmd is triggered: `:autocmd User FallPickerEnter` + +Debugging~ + +Enable verbose logging: +>vim + let g:denops#debug = 1 +< +View Denops server output: +>vim + :DenopsServer +< +============================================================================= +EXAMPLES *fall-examples* +Quick Start Configuration~ +>vim + " Basic key mappings + nnoremap ff :Fall file + nnoremap fg :Fall grep + nnoremap fb :Fall buffer + nnoremap fh :Fall help + nnoremap fr :FallResume +< +Custom Source Example~ +>typescript + // In custom.ts + import { defineSource } from "jsr:@vim-fall/std/source"; + + const mySource = defineSource(async function* () { + yield { id: 1, value: "Item 1", detail: {} }; + yield { id: 2, value: "Item 2", detail: {} }; + }); + + definePickerFromSource("mysource", mySource, { + matchers: [fzf()], + actions: { open: builtin.action.echo }, + defaultAction: "open", + }); +< +Integration with vim-qfreplace~ +>vim + function! s:fall_qfreplace() abort + " Send grep results to quickfix + cnoremap call fall#action('quickfix') + endfunction + + augroup fall_qfreplace + autocmd! + autocmd User FallPickerEnter:grep,git-grep,rg call s:fall_qfreplace() + augroup END +< ============================================================================= vim:tw=78:fo=tcq2mM:ts=8:ft=help:norl