{"id":19027655,"url":"https://github.com/danielefongo/microscope","last_synced_at":"2025-04-23T14:47:57.136Z","repository":{"id":91572149,"uuid":"602545692","full_name":"danielefongo/microscope","owner":"danielefongo","description":"A micro and highly composable finder for Neovim with no dependencies.","archived":false,"fork":false,"pushed_at":"2025-03-23T10:14:40.000Z","size":276,"stargazers_count":5,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-18T00:57:04.859Z","etag":null,"topics":["composable","extensible","fuzzy","fuzzy-search","minimal","neovim","neovim-plugin"],"latest_commit_sha":null,"homepage":"","language":"Lua","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/danielefongo.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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}},"created_at":"2023-02-16T12:49:04.000Z","updated_at":"2025-03-23T10:14:44.000Z","dependencies_parsed_at":null,"dependency_job_id":"94a90760-5f6d-47c5-8984-646a962c6ce6","html_url":"https://github.com/danielefongo/microscope","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielefongo%2Fmicroscope","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielefongo%2Fmicroscope/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielefongo%2Fmicroscope/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielefongo%2Fmicroscope/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/danielefongo","download_url":"https://codeload.github.com/danielefongo/microscope/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250456833,"owners_count":21433722,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["composable","extensible","fuzzy","fuzzy-search","minimal","neovim","neovim-plugin"],"created_at":"2024-11-08T21:08:49.812Z","updated_at":"2025-04-23T14:47:57.105Z","avatar_url":"https://github.com/danielefongo.png","language":"Lua","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Microscope\n\n![ci](https://img.shields.io/github/actions/workflow/status/danielefongo/microscope/ci.yml?style=for-the-badge)\n![code-coverage](https://img.shields.io/codecov/c/github/danielefongo/microscope?style=for-the-badge)\n\nA micro and highly composable finder for Neovim with no dependencies.\n\n## Base setup\n\nOn [lazy.nvim](https://github.com/folke/lazy.nvim)\n\n```lua\n{\n  \"danielefongo/microscope\",\n  dependencies = {\n    \"danielefongo/microscope-files\",\n    \"danielefongo/microscope-buffers\",\n  },\n  config = function()\n    local microscope = require(\"microscope\")\n    local actions = require(\"microscope.builtin.actions\")\n    local layouts = require(\"microscope.builtin.layouts\")\n\n    local files = require(\"microscope-files\")\n    local buffers = require(\"microscope-buffers\")\n\n    microscope.setup({\n      prompt = \":\u003e \" -- optional (default is \"\u003e \")\n      size = {\n        width = 80,\n        height = 30,\n      },\n      layout = layouts.default,\n      bindings = {\n        [\"\u003cc-j\u003e\"] = actions.next,\n        [\"\u003cc-k\u003e\"] = actions.previous,\n        [\"\u003ca-j\u003e\"] = actions.scroll_down,\n        [\"\u003ca-k\u003e\"] = actions.scroll_up,\n        [\"\u003ccr\u003e\"] = actions.open,\n        [\"\u003cesc\u003e\"] = actions.close,\n        [\"\u003ctab\u003e\"] = actions.select,\n        [\"\u003cc-f\u003e\"] = actions.toggle_full_screen,\n        [\"\u003ca-s\u003e\"] = actions.refine,\n      },\n      spinner = { ... } -- optional\n      args = { ... } -- optional\n    })\n\n    microscope.register(files.finders)\n    microscope.register(buffers.finders)\n\n    -- Bind using microscope instance\n    vim.keymap.set(\"n\", \"\u003cleader\u003eof\", microscope.finders.file:bind())\n    vim.keymap.set(\"n\", \"\u003cleader\u003eoo\", microscope.finders.old_file:bind())\n\n    -- Bind using microscope commands\n    vim.keymap.set(\"n\", \"\u003cleader\u003eof\", \":Microscope file\u003ccr\u003e\")\n    vim.keymap.set(\"n\", \"\u003cleader\u003eoo\", \":Microscope old_file\u003ccr\u003e\")\n  end,\n}\n```\n\nYou can create your own [finder](#finder).\n\n## Finder\n\n#### Finder opts\n\nEach finder can be defined using the following options:\n\n```lua\nlocal opts = {\n  lens = lens_spec, -- required\n  parsers = list_of_parsers, -- optional\n  open = open_fn, -- optional\n  preview = preview_fn, -- optional\n  layout = layout_fn, -- optional\n  full_screen = full_screen, -- optional\n  size = custom_size, -- optional (overrides/extends the microscope size option)\n  args = override_args, -- optional (overrides the lens args)\n  bindings = custom_bindings, -- optional (overrides/extends the microscope bindings option)\n  prompt = prompt, -- optional (overrides/extends the microscope prompt option)\n  spinner = spinner, -- optional (overrides/extends the microscope spinner option)\n}\n```\n\nThese properties are explained below.\n\n#### Creation\n\nA finder can be created in one of two ways:\n\n```lua\nlocal microscope = require(\"microscope\")\n\n-- First way\nlocal finder = microscope.finder(opts)\n\n-- Second way (registers the finder as microscope.finders.name)\nlocal finder = microscope.register({\n  name = opts,\n})\n```\n\n#### Binding\n\nAfter creating the finder, you can use it by binding it to a shortcut. There are three ways to achieve this:\n\n```lua\n-- First way\nvim.keymap.set(\"n\", \"\u003cleader\u003eof\", finder:bind(override_opts))\n\n-- Second way\nvim.keymap.set(\"n\", \"\u003cleader\u003eof\", microscope.finders.name:bind(override_opts))\n\n-- Third way\nvim.keymap.set(\"n\", \"\u003cleader\u003eof\", \":Microscope name override_opts\u003ccr\u003e\")\n```\n\nNote: `override_opts` is optional and may be a partial of [finder opts](#finder-opts) (see next section). These are evaluated at invocation time, so they are the last to be evaluated.\n\n#### Resuming\n\nAfter using the `hide` builtin [action](#actions), it is possible to resume the finder. There are different ways to achieve this:\n\n```lua\n-- First way\nvim.keymap.set(\"n\", \"\u003cleader\u003eh\", microscope.resume)\n\n-- Second way\nvim.keymap.set(\"n\", \"\u003cleader\u003eh\", \":Microscope resume\")\n```\n\nNote: If the finder is resumed from a different buffer or window, the data corresponding to the [request](#request) will be replaced, and no new search will be triggered until the input is changed. In such cases, the updated request will be used for the subsequent search.\n\n#### Opts override\n\nAfter creating the finder, you have the option to override its opts using the `override()` method. Here's an example:\n\nExample:\n\n```lua\nfinder:override({\n  size = {\n    width = 30,\n  },\n  bindings = {\n    [\"\u003ccr\u003e\"] = actions.nothing,\n  },\n})\n```\n\n### Lens spec\n\nThis element represents a specification for a [lens](#lens), which is used to retrieve or filter data (e.g. list of files or buffers), based on the [request](#request). It is a table that contains:\n\n- a [lens function](#lens-function)\n- an optional list of input lens specification\n- an optional table or [default args](#default-args)\n\n```lua\nlocal lens = {\n  fun = function(flow, request, args, context)\n    -- logic\n  end,\n  inputs = { ... }, -- optional list of other lens specs\n  args = { ... }, -- optional table of args\n}\n```\n\nExample:\n\n```lua\nlocal lenses = {}\n\nfunction lenses.rg(cwd)\n  return {\n    fun = function(flow)\n      flow.consume(flow.cmd.shell(\"rg\", { \"--files\" }, cwd))\n    end,\n  }\nend\n\nfunction lenses.fzf(...)\n  return {\n    fun = function(flow, request)\n      flow.consume(flow.cmd.iter(flow.read_iter()):pipe(\"fzf\", { \"-f\", request.text }))\n    end,\n    inputs = { ... },\n  }\nend\n\nlocal my_lens = lenses.fzf(lenses.rg())\n```\n\n#### Default args\n\nEach lens may have default arguments, which either extend or replace the ones received from the input lenses. Therefore, the final arguments consist of the combination of all the lenses args.\n\n### Parsers\n\nThis is a list of functions, called [parser functions](#parser-function), used to parse the data retrieved from the [lens](#lens).\n\n#### Parser function\n\nThis function accepts result data and the original [request](#request), and transforms the data by adding extra information. The first parser will receive a data containing only the `text` field. It is important for each parser function to propagate this field, even if unmodified, as it will be used to render the result. The additional information can include things like `file` or any other custom data you wish to add. The final data table will be passed to the [open function](#open-function) and the [preview function](#preview-function).\n\nThere is another special data field called `highlights`, which should be propagated unless you intend to remove it. This field represents a list of highlights to be applied to the result. For more details, please refer to the [highlight section](#highlight).\n\nExample:\n\n```lua\nlocal parser_fn = function(data, _)\n  local elements = vim.split(data.text, \":\", {})\n\n  data.highlights = build_highlights(data)\n  data.buffer = tonumber(elements[1])\n\n  return data\nend\n```\n\n### Open function\n\nThe open function is called when the [open action](#actions) is triggered. It takes a single result data obtained from the parsers, the [request](#request), and optional metadata (sent by `results:open`).\n\nExample:\n\n```lua\nlocal open_fn = function(data, request, metadata)\n  vim.cmd(\"e \" .. data.file)\nend\n```\n\n### Preview function\n\nThe preview function is called when a result is focused. It takes a single result data obtained from the parsers and the `preview` microscope instance.\n\nExample:\n\n```lua\nlocal preview_fn = function(data, window)\n  window:write({ data.text })\nend\n```\n\nFor more details about the preview window API, refer to the [preview window section](#preview-window).\nYou can use the api `treesitter` ([treesitter](#treesitter)) to highlight the content of the window.\n\n### Layout function\n\nThe layout function is responsible for defining the structure of the finder using the [display](#display) API. It receives a table with the following fields:\n\n- `finder_size`: represents the [size](#size) provided in the microscope settings.\n- `ui_size`: represents the size of the entire UI.\n- `preview`: indicates whether the [preview function](#preview-function) has been set or not.\n- `full_screen`: indicates whether the full-screen option has been set or not.\n\nExample:\n\n```lua\nlocal layout_fn = function(opts)\n  return display\n    .vertical({\n      display.horizontal({\n        display.results(\"40%\"),\n        display.preview(),\n      }),\n      display.input(1),\n    })\n    :build(opts.finder_size)\nend\n```\n\nThe function can also be defined using the `finder_layout` or `ui_layout` methods of the [display](#display).\n\n### Full screen\n\nThe `full_screen` is represented as a boolean, defaults to `false`.\n\n### Size\n\nThe `size` is represented as a table containing `width` and `height` fields.\n\n### Override args\n\nThe `args` is a table used to override the [default lens args](#default-args). Attempting to set arguments with types different from those of the defaults will trigger a critical microscope error.\n\n### Bindings\n\nThe `bindings` table is used to define shortcut bindings for microscope [actions](#action).\n\nExample:\n\n```lua\nlocal actions = require(\"microscope.builtin.actions\")\n\nlocal bindings = {\n  [\"\u003cc-j\u003e\"] = actions.next,\n  [\"\u003cc-k\u003e\"] = actions.previous,\n  [\"\u003ca-j\u003e\"] = actions.scroll_down,\n  [\"\u003ca-k\u003e\"] = actions.scroll_up,\n  [\"\u003ccr\u003e\"] = actions.open,\n  [\"\u003cesc\u003e\"] = actions.close,\n  [\"\u003ctab\u003e\"] = actions.select,\n  [\"\u003cc-f\u003e\"] = actions.toggle_full_screen,\n  [\"\u003cc-h\u003e\"] = actions.hide,\n}\n```\n\n#### Action\n\nThis function accepts a [microscope finder](#microscope-finder) instance to interact with it.\n\nExample:\n\n```lua\nlocal close_action = function(microscope)\n  microscope:close()\nend\n```\n\n### Prompt\n\nThe `prompt` is a string prefixed to the search query, defaults to \"\u003e \".\n\n### Spinner\n\nThe `spinner` table represents the specification for the loading spinner, which replaces the results title while retrieving the results. Its structure is as follows:\n\n```lua\nlocal spinner = {\n  interval = 500,\n  delay = 300,\n  position = \"center\",\n  symbols = {\n    \".   \",\n    \" .  \",\n    \"  . \",\n    \"   .\",\n    \"  . \",\n    \" .  \",\n  },\n}\n```\n\n## Builtins\n\nAll the builtin modules can be accessed using one of the following methods:\n\n- `require(\"microscope.builtin.MODULE\")`\n- `require(\"microscope').builtin.MODULE`\n\n### Lenses\n\nMicroscope exposes a list of lens specs in `microscope.builtin.lenses`:\n\n- `cache(...)`: caches results\n- `fzf(...)`: filters results using fzf\n- `head(...)`: limits results. Default args are: `{ limit = 5000 }`\n- `write(data)`: writes data directly into the flow\n- `shell(command)`: executes a command (raw string). The command can contain pipes (e.g. `echo x | grep \"x\"`)\n- `fn(fun)`: executes a function\n\n### Actions\n\nMicroscope exposes a list of actions in `microscope.builtin.actions`:\n\n- `previous`: goes to the previous result\n- `next`: goes to the next result\n- `scroll_down`: scrolls down preview\n- `scroll_up`: scrolls up preview\n- `toggle_full_screen`: toggles full screen\n- `open`: opens selected results\n- `select`: selects result\n- `set_layout(layout_fun)`: accepts a [layout function](#layout-function) and returns the corresponding action\n- `rotate_layouts(layout_funs)`: accepts a list of [layout function](#layout-function) and returns the corresponding action\n- `set_args(arguments)`: accepts an [args](#args) table and returns the corresponding action\n- `alter(override_opts)`: accepts a table of options to override in the finder's instance\n- `refine`: starts a new search on retrieved results using a fuzzy lens\n- `refine_with(lens, parser, prompt)`: starts a new search on retrieved results using a specific lens, parser and optional prompt\n- `hide`: hides the finder\n- `close`: closes the finder\n\n### Parsers\n\nMicroscope exposes a list of parsers in `microscope.builtin.parsers`:\n\n- `fuzzy`: highlights result\n\n### Layouts\n\nMicroscope exposes a list of layouts in `microscope.builtin.layouts`:\n\n- `default`\n\n## API\n\nAll the api modules can be accessed using one of the following methods:\n\n- `require(\"microscope.api.MODULE\")`\n- `require(\"microscope').api.MODULE`\n\n### Lens\n\nA lens is used to retrieve or filter data (e.g., a list of files or buffers) depending on the request, and it can be piped into other lenses. It accepts a [lens spec](#lens-spec).\n\nExample:\n\n```lua\nlocal lens = require(\"microscope.api.lens\")\n\nlocal function rg(cwd)\n  return {\n    fun = function(flow)\n      flow.consume(flow.cmd.shell(\"rg\", { \"--files\" }, cwd))\n    end,\n  }\nend\n\nlocal function fzf(...)\n  return {\n    fun = function(flow, request)\n      flow.consume(flow.cmd.shell(\"fzf\", { \"-f\", request.text }))\n    end,\n    inputs = { ... },\n  }\nend\n\nlocal my_lens = lens.new(fzf(rg()))\n```\n\n#### Lens function\n\nThe lens function has these parameters:\n\n- [flow](#flow)\n- [request](#request)\n- [arguments](#arguments)\n- [context](#context)\n\n##### Flow\n\nThe **flow** is a bag of functions:\n\n- `can_read`: returns true if there is at least one input lens.\n- `read`: returns `array_string` or `nil` if there is no more input data.\n  \u003e `array_string` is a string representing a list of lines separated by a newline and terminating with a newline (e.g., \"hello\\nworld\\n\").\n- `read_iter`: returns an iterator over `read`.\n- `read_array`: returns an array of lines or `nil` if there is no more input data.\n- `read_array_iter`: returns an iterator over `read_array`.\n- `write`: accepts an `array_string` or a list of lines and propagates the data (e.g., to the next lens).\n  \u003e `array_string` is a string representing a list of lines separated by a newline and terminating with a newline (e.g., \"hello\\nworld\\n\").\n- `stop`: stops the flow.\n- `stopped`: returns true if the flow is stopped (e.g., you close the finder before reaching the end of the lens function).\n- `cmd`: accessor for [command](#command)\n- `collect`: accepts a [command](#command) and an optional `to_array` parameter, returning the output. If the second parameter is not provided, it will return an `array_string`.\n  \u003e `array_string` is a string representing a list of lines separated by a newline and terminating with a newline (e.g., \"hello\\nworld\\n\").\n- `consume`: accepts a [command](#command) and writes its output into the flow in a \"streaming\" manner.\n\n##### Request\n\nThe **request** is what the user provides using the `search` function of [scope](#scope). In the context of a finder, it is represented by a table containing the following fields:\n\n- `text`: the searched text\n- `buf`: the original bufnr\n- `win`: the original winnr\n\n##### Arguments\n\nThe **arguments** table represents the merged combination of [default args](#default-args) and [override_args](#override-args).\n\n##### Context\n\nThe **context** is a table that is shared across multiple requests. It can be used to cache results or to perform logic based on the previous request.\n\n#### Command\n\nThe `microscope.api.command` module provides a utility for running commands inside flows.\n\n##### Instantiation\n\nIt can be lazy-instantiated with different constructors:\n\n- `shell`: runs a shell command. Args and cwd are optional.\n\n  ```lua\n  local mycmd = cmd.shell(\"echo\", { \"-n\", \"hello\\nworld\" }, cwd)\n  ```\n\n- `iter`: consumes an iterator function.\n\n  ```lua\n  local elements = { \"hello\\n\", \"world\\n\" }\n  local iterator = function()\n    return table.remove(elements, 1)\n  end\n\n  local mycmd = cmd.iter(iterator)\n  ```\n\n- `const`: stores a constant.\n\n  ```lua\n  local mycmd = cmd.const(\"hello\\nworld\\n\")\n  -- array version\n  local mycmd = cmd.const({ \"hello\", \"world\" })\n  ```\n\n- `fn`: executes a Vim function passing varargs.\n\n  \u003e This is required because one cannot execute Vim functions like `vim.api.nvim_buf_get_name` inside coroutines.\n\n  ```lua\n  -- Synthetic way\n  local mycmd = cmd.fn(vim.api.nvim_buf_get_name, request.buf)\n  -- Verbose way\n  local mycmd = cmd.fn(function()\n     return vim.api.nvim_buf_get_name(request.buf)\n  end)\n  ```\n\n- `await`: executes a function passing varargs, awaiting for the callback resolution.\n\n  \u003e This is required because one cannot execute Vim functions like `vim.api.nvim_buf_get_name` inside coroutines.\n\n  ```lua\n  local mycmd = cmd.await(function(resolve, ...)\n    an_async_function(function(data)\n      resolve(data)\n    end)\n  end)\n  ```\n\n##### Chaining\n\nOnce instantiated, it can be chained with other commands:\n\n- `pipe`: pipe to another shell command. Args and cwd are optional.\n\n  ```lua\n  local mycmd = cmd\n    .shell(\"echo\", { \"-n\", \"hello\\nworld\" }, cwd)\n    :pipe(\"grep\", { \"hello\" })\n  ```\n\n- `filter`: filters using a lua function. The input is a string.\n\n  ```lua\n  local mycmd = cmd\n    .shell(\"echo\", { \"-n\", \"hello\\nworld\" }, cwd)\n    :filter(function(lines)\n      return string.gsub(data, \"hello\", \"hallo\")\n    end)\n  ```\n\n### Scope\n\nThe `microscope.api.scope` module provides a utility for working with lenses.\n\nThis module can be particularly useful when working with the [preview function](#preview-function). You can use it to directly write the obtained lines into the preview window, providing a convenient way to display and interact with the data retrieved by the lens.\n\nThe `new` function accepts an object with two fields:\n\n- `lens`: a [lens specification](#lens-spec)\n- `callback`: an optional callback function that is called at the end\n\nExample:\n\n```lua\nlocal scope = require(\"microscope.api.scope\")\n\nlocal cat_scope = scope.new({\n  lens = {\n    fun = function(flow, any_request)\n      flow.cmd.shell(\"cat\", { any_request.text })\n    end,\n  },\n  callback = function(lines, any_request)\n    do_something(lines)\n  end,\n})\n```\n\nThe `search` function accepts a request, which will be passed through the lens flow, and optionally, [override_args](#override-args). If these arguments have different types than the defaults, it will raise a critical microscope error.\n\n```lua\nlocal override_args = { ... }\n\ncat_scope:search({\n  text = \"my_file\",\n}, override_args)\n\n-- To stop before completion\ncat_scope:stop()\n```\n\n### Display\n\nThis module provides assistance in building layouts. The functions available for creating displays are as follows:\n\n- `input(size)`: defines the input display. The default size is 1.\n- `results(size)`: defines the results display. The default size is nil.\n- `preview(size)`: defines the preview display. The default size is nil.\n- `space(size)`: defines a fake display to create spacing between and around displays. The default size is nil.\n- `vertical(displays, size)`: represents a vertical display. The first parameter is a list of displays, and the second one is the size.\n- `horizontal(displays, size)`: represents a horizontal display. The first parameter is a list of displays, and the second one is the size.\n\nThe size can be specified in the following ways:\n\n- An integer: represents the number of rows or columns.\n- A percentage string (e.g., \"40%\"): represents the percentage of rows or columns.\n- `nil`: indicates that the component will expand to occupy as much space as possible.\n\nTo construct the layout, you can use the `build` function of the display instance by passing a size (e.g., finder size).\n\nExample:\n\n```lua\ndisplay\n  .vertical({\n    display.horizontal({\n      display.results(\"40%\"),\n      display.space(4),\n      display.preview(),\n    }),\n    display.space(\"10%\"),\n    display.input(1),\n  })\n  :build(finder_size)\n```\n\nIt is also possible to define a [layout function](#layout-function) using one of the instance methods:\n\n- `finder_layout`\n  \u003e `display.vertical(...):finder_layout()`\n- `ui_layout`\n  \u003e `display.vertical(...):ui_layout()`\n- `custom_layout`\n  \u003e `display.vertical(...):custom_layout(size)`\n  \n### Microscope Finder\n\nThe Microscope Finder instance exposes three components:\n\n- [Input Window](#input-window)\n- [Preview Window](#preview-window)\n- [Results Window](#results-window)\n\nIn addition, the finder provides the following functions:\n\n- `close()`: closes the finder.\n- `get_opts()`: retrieves the finder's instance options.\n- `set_opts(opts)`: overrides the finder's instance options.\n  \u003e This differs from [opts override](#opts-override) since it is only for the instance.\n- `alter(lambda)`: overrides the finder's instance options. It accepts a lambda function with one parameter, which is a copy of the finder's instance options. This function should return the new options (`opts`) that will be set. This function effectively combines the functionality of `get_opts` and `set_opts`.\n\n### Input Window\n\nThe input window provides the following functions:\n\n- `text()`: returns the text.\n- `set_text(text)`: sets the text.\n- `reset()`: resets the text.\n\n### Preview Window\n\nThe preview window provides the following functions:\n\n- `set_buf_hls(highlights)`: highlights the buffer using `highlights_table` (see [highlight table section](#highlight-table) for more details).\n- `clear_buf_hls()`: resets the highlights of the buffer.\n- `set_win_opt(key, value)`: sets a window option.\n- `get_win_opt(key)`: gets a window option.\n- `set_buf_opt(key, value)`: sets a buffer option.\n- `get_buf_opt(key)`: gets a buffer option.\n- `get_win()`: returns the window number of the preview window.\n- `get_buf()`: returns the buffer number of the preview window.\n- `set_cursor(cursor)`: sets the cursor position safely.\n- `get_cursor(cursor)`: retrieves the current cursor position.\n- `clear()`: clears the text in the preview window.\n- `write(lines, from, to)`: writes lines of text to the buffer of the preview window. The `from` and `to` parameters are optional.\n- `write_term(lines)`: writes ANSI lines to the buffer.\n- `read(from, to)`: reads lines from the buffer of the preview window.\n\n### Results Window\n\nThe results window provides the following functions:\n\n- `set_buf_hl(color, line, from, to)`: highlights the buffer.\n- `set_win_opt(key, value)`: sets a window option.\n- `set_buf_opt(key, value)`: sets a buffer option.\n- `get_win()`: returns the window number of the results window.\n- `get_buf()`: returns the buffer number of the results window.\n- `set_cursor(cursor)`: sets the cursor position safely.\n- `get_cursor(cursor)`: retrieves the current cursor position.\n- `clear()`: clears the text in the results window.\n- `write(lines, from, to)`: writes lines of text to the buffer of the results window. The `from` and `to` parameters are optional.\n- `read(from, to)`: reads lines from the buffer of the results window.\n- `raw_results()`: returns the list of retrieved and unparsed results.\n- `select()`: adds the focused result to the selected results.\n- `selected()`: obtains the list of selected results.\n- `open(metadata)`: opens the selected results. The `metadata` parameter can be any data you want to pass to the `open` function.\n\n### Highlight\n\nThe `microscope.api.highlight` module allows you to create highlights for a line.\n\nExample:\n\n```lua\nlocal highlight = require(\"microscope.api.highlight\")\n\nlocal data = {\n  text = \"1: buffer one\",\n  highlights = {},\n}\nlocal highlights = highlight\n  .new(data.highlights, data.text)\n  :hl_match(highlight.color.color1, \"(%d+:)(.*)\", 1) -- highlight the first group with color1\n  :hl(highlight.color.color2, 3, 10) -- highlight from column 3 to 10 with color2\n  :get_highlights()\n```\n\nThe module provides several methods for creating highlights:\n\n- `hl(color, from, to)`: highlights text from position from to to with the specified color.\n- `hl_match(color, pattern, group)`: highlights the specified capture group from a Lua pattern match.\n- `hl_match_with(highlight_fun, pattern, group)`: applies highlights generated by highlight_fun to a matched group. The highlight_fun should return an [highlight table](#highlight-table).\n\n#### Highlight table\n\nThe highlights table is a table where keys are line numbers (1-based) and values are arrays of highlight definitions. Each highlight definition is a table with the following properties:\n\n- `from`: starting position (1-based)\n- `to`: ending position (1-based)\n- `color`: highlight group name\n\nExample:\n\n```lua\nlocal highlights_table = {\n  [1] = {\n    { from = 1, to = 5, color = \"String\" },\n    { from = 7, to = 12, color = \"Comment\" }\n  },\n  [2] = {\n    { from = 1, to = 4, color = \"Keyword\" }\n  }\n}\n```\n\n### Treesitter\n\nThe `microscope.api.treesitter` module allows you to get the `highlights_table` for a given text or buffer.\nThe module provides these methods:\n\n- `for_buffer(buf, lang)`\n- `for_text(text, lang)`\n\n### Error\n\nThe `microscope.api.error` module allows you to display an error. It provides two useful functions:\n\n- `generic(message)`: displays a generic error message.\n- `critical(message)`: displays an error message and closes the finder.\n\n## Plugins\n\nPlugins can expose finders, lens specs, actions, parsers, previews, and open functions. This allows you to use pre-packaged finders or easily create custom finders using the provided building blocks. Here's an example:\n\n```lua\nlocal microscope = require(\"microscope\")\nlocal lenses = require(\"microscope.builtin.lenses\")\nlocal parsers = require(\"microscope.builtin.parsers\")\n\nlocal files = require(\"microscope-files\")\n\nlocal function ls()\n  return {\n    fun = function(flow)\n      flow.consume(flow.cmd.shell(\"ls\"))\n    end,\n  }\nend\n\nmicroscope.setup({ ... })\n\nmicroscope.register(files.finders)\nmicroscope.register({\n  ls = {\n    lens = lenses.fzf(ls()),\n    preview = files.preview.cat,\n    parsers = { files.parsers.file, parsers.fuzzy },\n  },\n})\n```\n\nYou can also explore the already published plugins:\n\n- [microscope-buffers](https://github.com/danielefongo/microscope-buffers)\n- [microscope-code](https://github.com/danielefongo/microscope-code)\n- [microscope-files](https://github.com/danielefongo/microscope-files)\n- [microscope-git](https://github.com/danielefongo/microscope-git)\n\n## Testing\n\n- Clone [plenary.nvim](https://github.com/nvim-lua/plenary.nvim) in root folder.\n- Install luacov\n\n  ```bash\n  luarocks install luacov\n  luarocks install luacov-console\n  luarocks install luacov-html\n  luarocks install luacheck\n  cargo install stylua\n  ```\n\n- Run tests\n\n  ```bash\n  # without coverage\n  make test [test=path-to-file]\n  # with coverage\n  make testcov\n  # with coverage + html report\n  make testcov-html\n  # lint + stylua checks\n  make stylua lint\n  ```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanielefongo%2Fmicroscope","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdanielefongo%2Fmicroscope","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanielefongo%2Fmicroscope/lists"}