{"id":13411126,"url":"https://github.com/camspiers/snap","last_synced_at":"2026-01-27T20:06:17.601Z","repository":{"id":37383234,"uuid":"370236334","full_name":"camspiers/snap","owner":"camspiers","description":"A fast finder system for neovim.","archived":false,"fork":false,"pushed_at":"2024-07-03T17:01:44.000Z","size":523,"stargazers_count":479,"open_issues_count":26,"forks_count":16,"subscribers_count":9,"default_branch":"main","last_synced_at":"2024-07-31T20:45:02.548Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Fennel","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"unlicense","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/camspiers.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-05-24T05:20:23.000Z","updated_at":"2024-07-27T02:59:04.000Z","dependencies_parsed_at":"2024-01-03T03:29:26.728Z","dependency_job_id":"55d7e5a9-1d22-475f-a2b3-d38577a89988","html_url":"https://github.com/camspiers/snap","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/camspiers/snap","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/camspiers%2Fsnap","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/camspiers%2Fsnap/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/camspiers%2Fsnap/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/camspiers%2Fsnap/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/camspiers","download_url":"https://codeload.github.com/camspiers/snap/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/camspiers%2Fsnap/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28820352,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-27T18:44:20.126Z","status":"ssl_error","status_checked_at":"2026-01-27T18:44:09.161Z","response_time":168,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-30T20:01:11.587Z","updated_at":"2026-01-27T20:06:17.571Z","avatar_url":"https://github.com/camspiers.png","language":"Fennel","funding_links":[],"categories":["Fuzzy Finder","Fennel","Lua"],"sub_categories":["Markdown and LaTeX","Assembly"],"readme":"# Snap\n\nA fast finder system for neovim \u003e0.9.\n\n## Demo\n\nThe following shows finding files and grepping in the large `gcc` codebase.\n\n[Screencast from 2023-11-15 20-59-29.webm](https://github.com/camspiers/snap/assets/51294/80b844a2-5e92-47de-ae7e-2a78a5553b4f)\n\n## Installation\n\n### With lazy.nvim\n\n```lua\nrequire(\"lazy\").setup({\n  \"camspiers/snap\",\n  config = function ()\n    -- Basic example config\n    local snap = require\"snap\"\n    snap.maps{\n      { \"\u003cLeader\u003e\u003cLeader\u003e\", snap.config.file { producer = \"ripgrep.file\" } },\n    }\n  end\n})\n```\n\nor with `fzy` from `camspiers/luarocks`:\n\n```lua\nrequire(\"lazy\").setup({\n  {\n    \"camspiers/luarocks\",\n    opts = { rocks = { \"fzy\" } },\n  },\n  {\n    \"camspiers/snap\",\n    dependencies = { \"camspiers/luarocks\" },\n  }\n})\n```\n\n### With Packer\n\n```lua\nuse { 'camspiers/snap' }\n```\n\nor with `fzy`:\n\n```lua\nuse { 'camspiers/snap', rocks = {'fzy'}}\n```\n\n#### Semi-Optional Dependencies\n\nTo use the following `snap` components you need the specified dependencies, however not all components are needed, for example you should probably choose between `fzy` and `fzf` as your primary consumer.\n\n| Component            | Dependency                        |\n| -------------------- | --------------------------------- |\n| `consumer.fzy`       | `fzy` via luarocks                |\n| `consumer.fzf`       | `fzf` available on command line   |\n| `producer.ripgrep.*` | `rg` available on commmand line   |\n| `producer.fd.*`      | `fd` available on commmand line   |\n| `producer.git.file`  | `git` available on commmand line  |\n| `preview.*`          | `file` available on commmand line |\n\nThey are semi-optional because you can mix and match them depending on which technology you want to use.\n\n## Getting Started\n\nThere are three primary APIs to be aware of in order to set up your local nvim to use `snap`.\n\n### `snap.maps`\n\n`snap.maps` and `snap.map` will map a function to a particular keybinding. Any function can be registered (e.g your own lua functions), however usually you will register functions that result in a call to `snap.run`.\n\n### `snap.config`\n\n`snap.config` offers a terse API for creating functions that call `snap.run` with sensible configuration.\n\n### `snap.run`\n\nThough used directly infrequently, `snap.run` is the API to start a snap.\n\n### Registering Global Keymaps\n\nThe following illustrates some basic usage of the `snap.maps` and `snap.config` APIs, we generate a variety of functions and register them as normal mode mappings:\n\n```lua\nlocal snap = require'snap'\nsnap.maps {\n  {\"\u003cLeader\u003e\u003cLeader\u003e\", snap.config.file {producer = \"ripgrep.file\"}},\n  {\"\u003cLeader\u003efb\", snap.config.file {producer = \"vim.buffer\"}},\n  {\"\u003cLeader\u003efo\", snap.config.file {producer = \"vim.oldfile\"}},\n  {\"\u003cLeader\u003eff\", snap.config.vimgrep {}},\n}\n```\n\nThis gives a basic example, however see the [`snap.config`](#config-api) section for all options available.\n\n### Registering Global Keymaps With Defaults\n\nPerhaps you want some default config to apply to all your `snap.config.file` usages, to do so you can generate your own version of `snap.config.file` or `snap.config.vimgrep` with applied defaults:\n\n```lua\nlocal snap = require'snap'\nlocal file = snap.config.file:with {reverse = true, suffix = \"\u003e\u003e\", consumer = \"fzy\"}\nlocal vimgrep = snap.config.vimgrep:with {reverse = true, suffix = \"\u003e\u003e\", limit = 50000}\nsnap.maps {\n  {\"\u003cLeader\u003e\u003cLeader\u003e\", file {producer = \"ripgrep.file\"}},\n  {\"\u003cLeader\u003eff\", vimgrep {}},\n}\n```\n\n### Registering Global Keymaps With Registered Commands\n\nIf you want to also make your function available via the `:Snap myexamplefunction` API, you can pass an optional third parameter to `snap.map` or an optional third table value to each table to `snap.maps`.\n\n```lua\nlocal snap = require'snap'\nsnap.maps {\n  {\"\u003cLeader\u003e\u003cLeader\u003e\", snap.config.file {producer = \"ripgrep.file\"}, {command = \"mycommandname\"}}\n}\n```\n\n## Config API\n\n`snap.run` is designed to be a very general API used by composing different types of producers and consumers, instead of bundling defaults and configuration types into the general `snap.run` API, it is designed to be highly flexible and idempotent. So to ease the pain of creating your own functions that call `snap.run` with appropriate configuration, we instead provide `snap.config` for generating such functions with common configuration patterns.\n\n### `snap.config.file`\n\nThe full API:\n\n```typescript\n{\n  // One of either producer, try or combine are required\n\n  // A required producer either by string identifier or a function\n  producer: \"ripgrep.file\"\n    | \"fd.file\"\n    | \"vim.oldfile\"\n    | \"vim.buffer\"\n    | \"git.file\"\n    | Producer,\n\n  // A table of producers, the first that returns results is used\n  try: table\u003c\n    \"ripgrep.file\"\n    | \"fd.file\"\n    | \"vim.oldfile\"\n    | \"vim.buffer\"\n    | \"git.file\"\n    | Producer\n  \u003e,\n\n  // A table of producers, combines returns from each\n  combine: table\u003c\n    \"ripgrep.file\"\n    | \"fd.file\"\n    | \"vim.oldfile\"\n    | \"vim.buffer\"\n    | \"git.file\"\n    | Producer\n  \u003e,\n\n  // Optionals\n\n  // An optional prompt string (without suffix e.g. \"\u003e\")\n  prompt?: string,\n\n  // An optional suffix string e.g. \"\u003e\u003e\"\n  suffix?: string,\n\n  // An optional layout function, see layout API below\n  layout?: function,\n\n  // An optional table that passes args to producers that support it\n  args?: table\u003cstring\u003e,\n\n  // An optional boolean that configures producers that suppport it\n  hidden?: boolean,\n\n  // An optional boolean that when true places the input at the top\n  reverse?: boolean,\n\n  // An optional number that chanes the minimun screen column width the preview should display at\n  preview_min_width?: number,\n\n  // An optional boolean or function that returning true displays the preview and when false hides\n  preview?: boolean | function,\n\n  // An optional table of custom input buffer mappings, see mappings section below for options\n  mappings?: table\n\n  // An optional string, if cword then filter using current word, if selection then use selection\n  filter_with?: \"cword\" | \"selection\",\n\n  // An optional string or function use as initial filter\n  filter?: string | function\n}\n```\n\n### Examples\n\nThe following `snap.config.file` calls generate functions that run `snap.run` with various defaults.\n\nEach of these example functions generated would usually be passed to `snap.maps`, but you could also use them with any other mapping registration API, e.g. `which-key`.\n\n```lua\n-- Basic ripgrep file producer\nfile {producer = \"ripgrep.file\"}\n\n-- Ripgrep file producer with args\nfile {producer = \"ripgrep.file\", args = {'--hidden', '--iglob', '!.git/*'}}\n\n-- Git file producer with ripgrep fallback\nfile {try = {\"git.file\", \"ripgrep.file\"}}\n\n-- Basic file producer with previews off\nfile {producer = \"ripgrep.file\", preview = false}\n\n-- Basic buffer producer\nfile {producer = \"vim.buffer\"}\n\n-- Basic oldfile producer\nfile {producer = \"vim.oldfile\"}\n\n-- A customized prompt\nfile {producer = \"ripgrep.file\", prompt = \"MyFiles\"}\n\n-- A customized prompt suffix\nfile {producer = \"ripgrep.file\", suffix = \"\u003e\u003e\"}\n\n-- Display input at top\nfile {producer = \"ripgrep.file\", reverse = true}\n\n-- Custom layout function\nfile {producer = \"ripgrep.file\", layout = myCustomLayoutFunction}\n```\n\n## Recipes\n\n`snap` comes with inbuilt producers and consumers (see [How Snap Works](#how-snap-works) for what producers and consumers are) to enable easy creation of finders.\n\nThe following recipes illustrate direct usage of `snap.run` meaning calling the following examples will immediately run snap, but as illustrated above when registering a mapping you most often want to get a function that will invoke `snap.run` with a particular config, in that case the following examples can be replaced with invocations to `snap.config.file` to create the desired config.\n\n### Find Files\n\nUses built in `fzy` filter + score, and `ripgrep` for file finding.\n\n```lua\nsnap.run {\n  producer = snap.get'consumer.fzy'(snap.get'producer.ripgrep.file'),\n  select = snap.get'select.file'.select,\n  multiselect = snap.get'select.file'.multiselect,\n  views = {snap.get'preview.file'}\n}\n```\n\nor using `fzf`:\n\n```lua\nsnap.run {\n  producer = snap.get'consumer.fzf'(snap.get'producer.ripgrep.file'),\n  select = snap.get'select.file'.select,\n  multiselect = snap.get'select.file'.multiselect,\n  views = {snap.get'preview.file'}\n}\n```\n\n### Live Ripgrep\n\n```lua\nsnap.run {\n  producer = snap.get'producer.ripgrep.vimgrep',\n  select = snap.get'select.vimgrep'.select,\n  multiselect = snap.get'select.vimgrep'.multiselect,\n  views = {snap.get'preview.vimgrep'}\n}\n```\n\nOr given this can easily create the ability to ripgrep your entire filesystem with a result for every character, you can set a reasonable upper limit to 10,000 matches:\n\n```lua\nsnap.run {\n  producer = snap.get'consumer.limit'(10000, snap.get'producer.ripgrep.vimgrep'),\n  select = snap.get'select.vimgrep'.select,\n  multiselect = snap.get'select.vimgrep'.multiselect,\n  views = {snap.get'preview.vimgrep'}\n}\n```\n\n### Find Buffers\n\n```lua\nsnap.run {\n  producer = snap.get'consumer.fzy'(snap.get'producer.vim.buffer'),\n  select = snap.get'select.file'.select,\n  multiselect = snap.get'select.file'.multiselect,\n  views = {snap.get'preview.file'}\n}\n```\n\n### Find Old Files\n\n```lua\nsnap.run {\n  producer = snap.get'consumer.fzy'(snap.get'producer.vim.oldfile'),\n  select = snap.get'select.file'.select,\n  multiselect = snap.get'select.file'.multiselect,\n  views = {snap.get'preview.file'}\n}\n```\n\n### Find Git Files\n\n```lua\nsnap.run {\n  producer = snap.get'consumer.fzy'(snap.get'producer.git.file'),\n  select = snap.get'select.file'.select,\n  multiselect = snap.get'select.file'.multiselect,\n  views = {snap.get'preview.file'}\n}\n```\n\nor find git files with fallback to ripgrep:\n\n```lua\nsnap.run {\n  producer = snap.get'consumer.fzf'(\n    snap.get'consumer.try'(\n      snap.get'producer.git.file',\n      snap.get'producer.ripgrep.file'\n    ),\n  ),\n  select = snap.get'select.file'.select,\n  multiselect = snap.get'select.file'.multiselect,\n  views = {snap.get'preview.file'}\n}\n```\n\n### Find Help Tags\n\n```lua\nsnap.run {\n  prompt = \"Help\u003e\",\n  producer = snap.get'consumer.fzy'(snap.get'producer.vim.help'),\n  select = snap.get'select.help'.select,\n  views = {snap.get'preview.help'}\n}\n```\n\n### Find Buffer Marks\n\n```lua\nsnap.run {\n  prompt = \"Marks\u003e\",\n  producer = snap.get'consumer.fzy'(snap.get'producer.vim.marks'),\n  select = snap.get'select.vim.mark'.select,\n  views = {snap.get'preview.vim.mark'}\n}\n```\n\n### Find Global Marks\n\n```lua\nsnap.run {\n  prompt = \"Global Marks\u003e\",\n  producer = snap.get'consumer.fzy'(snap.get'producer.vim.globalmarks'),\n  select = snap.get'select.vim.mark'.select,\n  views = {snap.get'preview.vim.mark'}\n}\n```\n\n### Grep with FZF as optional next step\n\nThe following will run the vimgrep producer and upon `\u003cC-q\u003e` will run `fzf` on the filtered results.\n\n```lua\nsnap.run {\n  producer = snap.get'producer.ripgrep.vimgrep',\n  steps = {{\n    consumer = snap.get'consumer.fzf',\n    config = {prompt = \"FZF\u003e\"}\n  }},\n  select = snap.get'select.file'.select,\n  multiselect = snap.get'select.file'.multiselect,\n  views = {snap.get'preview.file'}\n}\n```\n\n### Search files in multiple paths\n\nThe following will combine results from multiple paths using a producer for each path:\n\n```lua\nsnap.run {\n  producer = snap.get'consumer.fzf'(\n    snap.get'consumer.combine'(\n      snap.get'producer.ripgrep.file'.args({}, \"/directory1\"),\n      snap.get'producer.ripgrep.file'.args({}, \"/directory2\"),\n      snap.get'producer.ripgrep.file'.args({}, \"/directory3\"),\n    ),\n  ),\n  select = snap.get'select.file'.select,\n  multiselect = snap.get'select.file'.multiselect,\n  views = {snap.get'preview.file'}\n}\n```\n\n### Key Bindings for Input Buffer\n\nThe following are what bindings are made for the input buffer while snap is open.\n\n#### Select\n\nWhen a single item is selected, calls the provided `select` function with the cursor result as the selection.\n\nWhen multiple items are selection, calls the provider `multiselect` function.\n\n- `\u003cCR\u003e`\n\nAlternatives:\n\n- `\u003cC-x\u003e` opens in new split\n- `\u003cC-v\u003e` opens in new vsplit\n- `\u003cC-t\u003e` opens in new tab\n\n#### Exit\n\nCloses `snap`\n\n- `\u003cEsc\u003e`\n- `\u003cC-c\u003e`\n\n#### Next\n\nMove cursor to the next selection.\n\n- `\u003cDown\u003e`\n- `\u003cC-n\u003e`\n- `\u003cC-j\u003e`\n\n#### Previous\n\nMove cursor to the previous selection.\n\n- `\u003cUp\u003e`\n- `\u003cC-p\u003e`\n- `\u003cC-k\u003e`\n\n#### Multiselect (enabled when `multiselect` is provided)\n\nAdd current cursor result to selection list.\n\n- `\u003cTab\u003e`\n\nRemove current cursor result from selection list.\n\n- `\u003cS-Tab\u003e`\n\nSelect all\n\n- `\u003cC-a\u003e`\n\n#### Results Page Down\n\nMoves the results cursor down a page.\n\n- `\u003cC-b\u003e`\n\n#### Results Page Up\n\nMoves the results cursor up a page.\n\n- `\u003cC-f\u003e`\n\n#### View Page Down\n\nMoves the cursor of the first view down a page (if more than one exists).\n\n- `\u003cC-d\u003e`\n\n#### View Page Up\n\nMoves the cursor of the first view up a page (if more than one exists).\n\n- `\u003cC-u\u003e`\n\n#### Toggle Views\n\nToggles the views on and off.\n\n- `\u003cC-h\u003e`\n\n### Customizing Default Input Buffer Mappings\n\nThe default mappings can be customized by providing a `mappings` key to your `snap.run` configs.\n\nThe following are the default mappings, each of which can be overridden:\n\n```lua\n{\n  [\"enter-split\"] = {\"\u003cC-x\u003e\"},\n  [\"enter-tab\"] = {\"\u003cC-t\u003e\"},\n  [\"enter-vsplit\"] = {\"\u003cC-v\u003e\"},\n  [\"next-item\"] = {\"\u003cC-n\u003e\"},\n  [\"next-page\"] = {\"\u003cC-f\u003e\"},\n  [\"prev-item\"] = {\"\u003cC-p\u003e\"},\n  [\"prev-page\"] = {\"\u003cC-b\u003e\"},\n  [\"select-all\"] = {\"\u003cC-a\u003e\"},\n  [\"view-page-down\"] = {\"\u003cC-d\u003e\"},\n  [\"view-page-up\"] = {\"\u003cC-u\u003e\"},\n  [\"view-toggle-hide\"] = {\"\u003cC-h\u003e\"},\n  enter = {\"\u003cCR\u003e\"},\n  exit = {\"\u003cEsc\u003e\", \"\u003cC-c\u003e\"},\n  next = {\"\u003cC-q\u003e\"},\n  select = {\"\u003cTab\u003e\"},\n  unselect = {\"\u003cS-Tab\u003e\"}\n}\n```\n\nExample:\n\n```lua\nsnap.run {\n  producer = snap.get'consumer.fzy'(snap.get'producer.ripgrep.file'),\n  select = snap.get'select.file'.select,\n  multiselect = snap.get'select.file'.multiselect,\n  views = {snap.get'preview.file'},\n  mappings = {\n    enter = {\"\u003cCR\u003e\", \"\u003cC-o\u003e\"}, -- my custom mapping\n  }\n}\n```\n\n## How Snap Works\n\n`snap` uses a non-blocking design to ensure the UI is always responsive to user input.\n\nTo achieve this it employs coroutines, and while that might be a little daunting, the following walk-through illustrates the primary concepts.\n\nOur example's goal is to run the `ls` command, filter the results in response to input, and print the selected value.\n\n### Producer\n\nA producers API looks like this:\n\n```typescript\ntype Producer = (request: Request) =\u003e yield\u003cYieldable\u003e;\n```\n\nThe producer is a function that takes a request and yields results (see below for the range of `Yieldable` types).\n\nIn the following `producer`, we run the `ls` command and progressively `yield` its output.\n\n```lua\nlocal snap = require'snap'\nlocal io = snap.get'common.io'\n\n-- Runs ls and yields lua tables containing each line\nlocal function producer (request)\n  -- Runs the slow-mode getcwd function\n  local cwd = snap.sync(vim.fn.getcwd)\n  -- Iterates ls commands output using snap.io.spawn\n  for data, err, kill in io.spawn(\"ls\", {}, cwd) do\n    -- If the filter updates while the command is still running\n    -- then we kill the process and yield nil\n    if request.canceled() then\n      kill()\n      coroutine.yield(nil)\n    -- If there is an error we yield nil\n    elseif (err ~= \"\") then\n      coroutine.yield(nil)\n    -- If the data is empty we yield an empty table\n    elseif (data == \"\") then\n      coroutine.yield({})\n    -- If there is data we split it by newline\n    else\n      coroutine.yield(vim.split(data, \"\\n\", true))\n    end\n  end\nend\n```\n\n### Consumer\n\nA consumers type looks like this:\n\n```typescript\ntype Consumer = (producer: Producer) =\u003e Producer;\n```\n\nA consumer is a function that takes a producer and returns a producer.\n\nAs our goal here is to filter, we iterate over our passed producer and only yield values that match `request.filter`.\n\n```lua\n-- Takes in a producer and returns a producer\nlocal function consumer (producer)\n  -- Return producer\n  return function (request)\n    -- Iterates over the producers results\n    for results in snap.consume(producer, request) do\n      -- If we have a table then we want to filter it\n      if type(results) == \"table\" then\n        -- Yield the filtered table\n        coroutine.yield(vim.tbl_filter(\n          function (value)\n            return string.find(value, request.filter, 0, true)\n          end,\n          results\n        ))\n      -- If we don't have a table we finish by yielding nil\n      else\n        coroutine.yield(nil)\n      end\n    end\n  end\nend\n```\n\n### Producer + Consumer\n\nThe following combines our above `consumer` and `producer`, itself creating a new producer, and passes this to `snap` to run:\n\n```lua\nsnap.run {\n  producer = consumer(producer),\n  select = print\n}\n```\n\nFrom the above we have seen the following distinct concepts of `snap`:\n\n- Producer + consumer pattern\n- Yielding a lua `table` of strings\n- Yielding `nil` to exit\n- Using `snap.io.spawn` iterate over the data of a process\n- Using `snap.sync` to run slow-mode nvim functions\n- Using `snap.consume` to consume another producer\n- Using the `request.filter` value\n- Using the `request.canceled()` signal to kill processes\n\n## API\n\n### Meta Result\n\nResults can be decorated with additional information (see `with_meta`), these results are represented by the `MetaResult` type.\n\n```typescript\n// A table that tostrings as result\n\ntype MetaResult = {\n  // The result string value\n  result: string;\n\n  // A metatable __tostring implementation\n  __tostring: (result: MetaResult) =\u003e string;\n\n  // More optional properties, e.g. score\n  ...\n};\n```\n\n### Yieldable\n\nCoroutines in `snap` can yield 4 different types, each with a distinct meaning outlined below.\n\n```typescript\ntype Yieldable = table\u003cstring\u003e | table\u003cMetaResult\u003e | function | nil;\n```\n\n#### Yielding `table\u003cstring\u003e`\n\nFor each `table\u003cstring\u003e` yielded (or returned as the last value of `producer`) from a `producer`, `snap` will accumulate the values of the table and display them in the results buffer.\n\n```lua\nlocal function producer(message)\n  coroutine.yield({\"Result 1\", \"Result 1\"})\n  -- the nvim UI can respond to input between these yields\n  coroutine.yield({\"Result 3\", \"Result 4\"})\nend\n```\n\nThis `producer` function results in a table of 4 values displayed, but given there are two yields, in between these yields `nvim` has an oppurtunity to process more input.\n\nOne can see how this functionality allows for results of spawned processes to progressively yield thier results while avoiding blocking user input, and enabling the cancelation of said spawned processes.\n\n#### Yielding `table\u003cMetaResult\u003e`\n\nResults at times need to be decorated with additional information, e.g. a sort score.\n\n`snap` makes use of tables (with an attached metatable implementing `__tostring`) to represent results with meta data.\n\nThe following shows how to add results with additional information. And because `snap` automatically sorts results with `score` meta data, the following with be ordered accordingly.\n\n```lua\nlocal function producer(message)\n  coroutine.yield({\n    snap.with_meta(\"Higher rank\", \"score\", 10),\n    snap.with_meta(\"Lower rank\", \"score\", 1),\n    snap.with_meta(\"Mid rank\", \"score\", 5)\n  })\nend\n```\n\n#### Yielding `function`\n\nGiven that `producer` is by design run when `fast-mode` is true. One needs an ability to at times get the result of a blocking `nvim` function, such as many of `nvim` basic functions, e.g. `vim.fn.getcwd`. As such `snap` provides the ability to `yield` a function, have its execution run with `vim.schedule` and its resulting value returned.\n\n```lua\nlocal function producer(message)\n  -- Yield a function to get its result\n  local cwd = snap.sync(vim.fn.getcwd)\n  -- Now we have the cwd we can do something with it\nend\n```\n\n#### Yielding `nil`\n\nYielding nil signals to `snap` that there are not more results, and the coroutine is dead. `snap` will finish processing the `coroutine` when nil is encounted.\n\n```lua\nlocal function producer(message)\n  coroutine.yield({\"Result 1\", \"Result 1\"})\n  coroutine.yield(nil)\n  -- Doesn't proces this, as coroutine is dead\n  coroutine.yield({\"Result 3\", \"Result 4\"})\nend\n```\n\n### Request\n\nThis is the request that is passed to a `producer`.\n\n```typescript\ntype Request = {\n  filter: string;\n  winnr: number;\n  canceled: () =\u003e boolean;\n};\n```\n\n### ViewRequest\n\nThis is the request that is passed to view producers.\n\n```typescript\ntype ViewRequest = {\n  selection: string;\n  bufnr: number;\n  winnr: number;\n  canceled: () =\u003e boolean;\n};\n```\n\n### Producer\n\n```typescript\ntype Producer = (request: Request) =\u003e yield\u003cYieldable\u003e;\n```\n\nThe full type of producer is actually:\n\n```typescript\ntype ProducerWithDefault = { default: Producer } | Producer;\n```\n\nBecause we support passing a table if it has a `default` field that is a producer. This enables producer modules to export a default producer, while also making orther related producers available, e.g. ones with additional configuration.\n\nSee: https://github.com/camspiers/snap/blob/main/fnl/snap/producer/ripgrep/file.fnl\n\n### Consumer\n\n```typescript\ntype Consumer = (producer: Producer) =\u003e Producer;\n```\n\n### ViewProducer\n\n```typescript\ntype ViewProducer = (request: ViewRequest) =\u003e yield\u003cfunction | nil\u003e;\n```\n\n### `snap.run`\n\n```typescript\n{\n  // Get the results to display\n  producer: Producer;\n\n  // Called on select\n  select: (selection: string) =\u003e nil;\n\n  // Optional prompt displayed to the user\n  prompt?: string;\n\n  // Optional function that enables multiselect\n  multiselect?: (selections: table\u003cstring\u003e) =\u003e nil;\n\n  // Optional function configuring the results window\n  layout?: () =\u003e {\n    width: number;\n    height: number;\n    row: number;\n    col: number;\n  };\n\n  // Optional initial filter\n  initial_filter?: string;\n\n  // Optional views\n  views?: table\u003cViewProducer\u003e\n};\n```\n\n## Advanced API (for developers)\n\n### `snap.meta_result`\n\nTurns a result into a meta result.\n\n```typescript\n(result: string | MetaResult) =\u003e MetaResult;\n```\n\n### `snap.with_meta`\n\nAdds a meta field to a result.\n\n```typescript\n(result: string | MetaResult, field: string, value: any) =\u003e MetaResult;\n```\n\n### `snap.has_meta`\n\nChecks if a result has a meta field.\n\n```typescript\n(result: string | MetaResult, field: string) =\u003e boolean;\n```\n\n### `snap.resume`\n\nResumes a passed coroutine while handling non-fast API requests.\n\nTODO\n\n### `snap.sync`\n\nYield a slow-mode function and get it's result.\n\n```typescript\n(fnc: () =\u003e T) =\u003e T;\n```\n\n### `snap.consume`\n\nConsumes a producer providing an iterator of its yielded results\n\n```typescript\n(producer: Producer, request: Request) =\u003e iterator\u003cYieldable\u003e;\n```\n\n### Layouts\n\n#### `snap.layouts.centered`\n\n#### `snap.layouts.bottom`\n\n#### `snap.layouts.top`\n\n### Producers\n\n#### `snap.producer.vim.buffer`\n\nProduces vim buffers.\n\n#### `snap.producer.vim.oldfiles`\n\nProduces vim oldfiles.\n\n#### `snap.producer.luv.file`\n\nLuv (`vim.loop`) based file producer.\n\n```\nNOTE: Requires no external dependencies.\n```\n\n#### `snap.producer.luv.directory`\n\nLuv (`vim.loop`) based directory producer.\n\n```\nNOTE: Requires no external dependencies.\n```\n\n#### `snap.producer.ripgrep.file`\n\nRipgrep based file producer.\n\n#### `snap.producer.ripgrep.vimgrep`\n\nRipgrep based grep producer in `vimgrep` format.\n\n#### `snap.producer.fd.file`\n\nFd based file producer.\n\n#### `snap.producer.fd.directory`\n\nFd based directory producer.\n\n#### `snap.producer.git.file`\n\nGit file producer.\n\n### Consumers\n\n#### `snap.consumer.cache`\n\nGeneral cache for producers whose values don't change in response to `request`.\n\n#### `snap.consumer.limit`\n\nGeneral limit, will stop consuming a producer when a specified limit is reached.\n\n#### `snap.consumer.fzy`\n\nThe workhorse consume for filtering producers that don't themselves filter.\n\nNOTE: Requests `fzy`, e.g. `use_rocks 'fzy'`\n\n#### `snap.consumer.fzy.filter`\n\nA component piece of fzy that only filters.\n\n#### `snap.consumer.fzy.score`\n\nA component piece of fzy that only attaches score meta data.\n\n#### `snap.consumer.fzy.positions`\n\nA component piece of fzy that only attaches position meta data.\n\n#### `snap.consumer.fzf`\n\nRuns filtering through fzf, only supports basic positions highlighting for now.\n\n#### `snap.consumer.try`\n\nAccepts and arbitrary number of producers and upon the first that yields results then use it and skip the rest:\n\n```lua\nsnap.get'consumer.try'(\n  snap.get'producer.git.file',\n  snap.get'producer.ripgrep.file'\n)\n```\n\n#### `snap.consumer.combine`\n\nAccepts and arbitrary number of producers and combines their results:\n\n```lua\nsnap.get'consumer.combine'(\n  snap.get'producer.ripgrep.file'.args({}, \"/directory1\"),\n  snap.get'producer.ripgrep.file'.args({}, \"/directory2\"),\n)\n```\n\n### Selectors\n\n#### `snap.select.file`\n\nOpens a file in a buffer in the last used window.\n\n```\nNOTE: Provides both `select` and `multiselect`.\n```\n\n#### `snap.select.vimgrep`\n\nIf a single file is selected then simply opens the file at appropriate position.\n\nIf multiple files are selected then it adds them to the quickfix list, and opens the first.\n\n```\nNOTE: Provides both `select` and `multiselect`.\n```\n\n#### `snap.select.cwd`\n\nChanges directory in response to selection.\n\n#### `snap.select.insert`\n\nInserts selection at cursor location.\n\n```\nNOTE: Only provides `select`.\n```\n\n### Previewers\n\n#### `snap.preview.file`\n\nCreates a basic file previewer.\n\n```\nNOTE: Experimental, and relies on `file` program in path.\n```\n\n# Contributing\n\nSnap is written in fennel, a language that compiles to Lua. See https://fennel-lang.org/\n\nTo install build dependencies:\n\n```bash\nmake deps\n```\n\nTo compile lua:\n\n```bash\nmake compile\n```\n\n# Roadmap\n\n- [x] Lua file producer\n- [x] Preview system\n- [x] More configurable layout system, including arbitrary windows\n- [x] Configurable loading screen\n- [x] FZF score/filter consumer\n- [ ] More producers for vim concepts\n- [ ] Lua filter consumer\n- [ ] Tests\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcamspiers%2Fsnap","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcamspiers%2Fsnap","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcamspiers%2Fsnap/lists"}