# 🍿 `snacks.nvim`

A collection of small QoL plugins for Neovim.

## ✨ Features

<!-- toc:start -->

| Snack | Description | Setup |
| ----- | ----------- | :---: |
| [animate](https://github.com/folke/snacks.nvim/blob/main/docs/animate.md) | Efficient animations including over 45 easing functions _(library)_ |  |
| [bigfile](https://github.com/folke/snacks.nvim/blob/main/docs/bigfile.md) | Deal with big files | ‼ |
| [bufdelete](https://github.com/folke/snacks.nvim/blob/main/docs/bufdelete.md) | Delete buffers without disrupting window layout |  |
| [dashboard](https://github.com/folke/snacks.nvim/blob/main/docs/dashboard.md) |  Beautiful declarative dashboards | ‼ |
| [debug](https://github.com/folke/snacks.nvim/blob/main/docs/debug.md) | Pretty inspect & backtraces for debugging |  |
| [dim](https://github.com/folke/snacks.nvim/blob/main/docs/dim.md) | Focus on the active scope by dimming the rest |  |
| [explorer](https://github.com/folke/snacks.nvim/blob/main/docs/explorer.md) | A file explorer (picker in disguise) | ‼ |
| [git](https://github.com/folke/snacks.nvim/blob/main/docs/git.md) | Git utilities |  |
| [gitbrowse](https://github.com/folke/snacks.nvim/blob/main/docs/gitbrowse.md) | Open the current file, branch, commit, or repo in a browser (e.g. GitHub, GitLab, Bitbucket) |  |
| [image](https://github.com/folke/snacks.nvim/blob/main/docs/image.md) | Image viewer using Kitty Graphics Protocol, supported by `kitty`, `wezterm` and `ghostty` | ‼ |
| [indent](https://github.com/folke/snacks.nvim/blob/main/docs/indent.md) | Indent guides and scopes |  |
| [input](https://github.com/folke/snacks.nvim/blob/main/docs/input.md) | Better `vim.ui.input` | ‼ |
| [layout](https://github.com/folke/snacks.nvim/blob/main/docs/layout.md) | Window layouts |  |
| [lazygit](https://github.com/folke/snacks.nvim/blob/main/docs/lazygit.md) | Open LazyGit in a float, auto-configure colorscheme and integration with Neovim |  |
| [notifier](https://github.com/folke/snacks.nvim/blob/main/docs/notifier.md) | Pretty `vim.notify` | ‼ |
| [notify](https://github.com/folke/snacks.nvim/blob/main/docs/notify.md) | Utility functions to work with Neovim's `vim.notify` |  |
| [picker](https://github.com/folke/snacks.nvim/blob/main/docs/picker.md) | Picker for selecting items | ‼ |
| [profiler](https://github.com/folke/snacks.nvim/blob/main/docs/profiler.md) | Neovim lua profiler |  |
| [quickfile](https://github.com/folke/snacks.nvim/blob/main/docs/quickfile.md) | When doing `nvim somefile.txt`, it will render the file as quickly as possible, before loading your plugins. | ‼ |
| [rename](https://github.com/folke/snacks.nvim/blob/main/docs/rename.md) | LSP-integrated file renaming with support for plugins like [neo-tree.nvim](https://github.com/nvim-neo-tree/neo-tree.nvim) and [mini.files](https://github.com/echasnovski/mini.files). |  |
| [scope](https://github.com/folke/snacks.nvim/blob/main/docs/scope.md) | Scope detection, text objects and jumping based on treesitter or indent | ‼ |
| [scratch](https://github.com/folke/snacks.nvim/blob/main/docs/scratch.md) | Scratch buffers with a persistent file |  |
| [scroll](https://github.com/folke/snacks.nvim/blob/main/docs/scroll.md) | Smooth scrolling | ‼ |
| [statuscolumn](https://github.com/folke/snacks.nvim/blob/main/docs/statuscolumn.md) | Pretty status column | ‼ |
| [terminal](https://github.com/folke/snacks.nvim/blob/main/docs/terminal.md) | Create and toggle floating/split terminals |  |
| [toggle](https://github.com/folke/snacks.nvim/blob/main/docs/toggle.md) | Toggle keymaps integrated with which-key icons / colors |  |
| [util](https://github.com/folke/snacks.nvim/blob/main/docs/util.md) | Utility functions for Snacks _(library)_ |  |
| [win](https://github.com/folke/snacks.nvim/blob/main/docs/win.md) | Create and manage floating windows or splits |  |
| [words](https://github.com/folke/snacks.nvim/blob/main/docs/words.md) | Auto-show LSP references and quickly navigate between them | ‼ |
| [zen](https://github.com/folke/snacks.nvim/blob/main/docs/zen.md) | Zen mode • distraction-free coding |  |

<!-- toc:end -->

## ⚡ Requirements

- **Neovim** >= 0.9.4
- for proper icons support:
  - [mini.icons](https://github.com/echasnovski/mini.icons) _(optional)_
  - [nvim-web-devicons](https://github.com/nvim-tree/nvim-web-devicons) _(optional)_
  - a [Nerd Font](https://www.nerdfonts.com/) **_(optional)_**

## 📦 Installation

Install the plugin with your package manager:

### [lazy.nvim](https://github.com/folke/lazy.nvim)

> [!important]
> A couple of plugins **require** `snacks.nvim` to be set-up early.
> Setup creates some autocmds and does not load any plugins.
> Check the [code](https://github.com/folke/snacks.nvim/blob/main/lua/snacks/init.lua) to see what it does.

> [!caution]
> You need to explicitly pass options for a plugin or set `enabled = true` to enable it.

> [!tip]
> It's a good idea to run `:checkhealth snacks` to see if everything is set up correctly.

```lua
{
  "folke/snacks.nvim",
  priority = 1000,
  lazy = false,
  ---@type snacks.Config
  opts = {
    -- your configuration comes here
    -- or leave it empty to use the default settings
    -- refer to the configuration section below
    bigfile = { enabled = true },
    dashboard = { enabled = true },
    explorer = { enabled = true },
    indent = { enabled = true },
    input = { enabled = true },
    picker = { enabled = true },
    notifier = { enabled = true },
    quickfile = { enabled = true },
    scope = { enabled = true },
    scroll = { enabled = true },
    statuscolumn = { enabled = true },
    words = { enabled = true },
  },
}
```

For an in-depth setup of `snacks.nvim` with `lazy.nvim`, check the [example](https://github.com/folke/snacks.nvim?tab=readme-ov-file#-usage) below.

## ⚙ Configuration

Please refer to the readme of each plugin for their specific configuration.

<details><summary>Default Options</summary>

<!-- config:start -->

```lua
---@class snacks.Config
---@field animate? snacks.animate.Config
---@field bigfile? snacks.bigfile.Config
---@field dashboard? snacks.dashboard.Config
---@field dim? snacks.dim.Config
---@field explorer? snacks.explorer.Config
---@field gitbrowse? snacks.gitbrowse.Config
---@field image? snacks.image.Config
---@field indent? snacks.indent.Config
---@field input? snacks.input.Config
---@field layout? snacks.layout.Config
---@field lazygit? snacks.lazygit.Config
---@field notifier? snacks.notifier.Config
---@field picker? snacks.picker.Config
---@field profiler? snacks.profiler.Config
---@field quickfile? snacks.quickfile.Config
---@field scope? snacks.scope.Config
---@field scratch? snacks.scratch.Config
---@field scroll? snacks.scroll.Config
---@field statuscolumn? snacks.statuscolumn.Config
---@field terminal? snacks.terminal.Config
---@field toggle? snacks.toggle.Config
---@field win? snacks.win.Config
---@field words? snacks.words.Config
---@field zen? snacks.zen.Config
---@field styles? table<string, snacks.win.Config>
---@field image? snacks.image.Config|{}
{
  image = {
    -- define these here, so that we don't need to load the image module
    formats = {
      "png",
      "jpg",
      "jpeg",
      "gif",
      "bmp",
      "webp",
      "tiff",
      "heic",
      "avif",
      "mp4",
      "mov",
      "avi",
      "mkv",
      "webm",
      "pdf",
    },
  },
}
```

<!-- config:end -->

</details>

Some plugins have examples in their documentation. You can include them in your
config like this:

```lua
{
  dashboard = { example = "github" }
}
```

If you want to customize options for a plugin after they have been resolved, you
can use the `config` function:

```lua
{
  gitbrowse = {
    config = function(opts, defaults)
      table.insert(opts.remote_patterns, { "my", "custom pattern" })
    end
  },
}
```

## 🚀 Usage

See the example below for how to configure `snacks.nvim`.

<!-- example:start -->

```lua
{
  "folke/snacks.nvim",
  priority = 1000,
  lazy = false,
  ---@type snacks.Config
  opts = {
    bigfile = { enabled = true },
    dashboard = { enabled = true },
    explorer = { enabled = true },
    indent = { enabled = true },
    input = { enabled = true },
    notifier = {
      enabled = true,
      timeout = 3000,
    },
    picker = { enabled = true },
    quickfile = { enabled = true },
    scope = { enabled = true },
    scroll = { enabled = true },
    statuscolumn = { enabled = true },
    words = { enabled = true },
    styles = {
      notification = {
        -- wo = { wrap = true } -- Wrap notifications
      }
    }
  },
  keys = {
    -- Top Pickers & Explorer
    { "<leader><space>", function() Snacks.picker.smart() end, desc = "Smart Find Files" },
    { "<leader>,", function() Snacks.picker.buffers() end, desc = "Buffers" },
    { "<leader>/", function() Snacks.picker.grep() end, desc = "Grep" },
    { "<leader>:", function() Snacks.picker.command_history() end, desc = "Command History" },
    { "<leader>n", function() Snacks.picker.notifications() end, desc = "Notification History" },
    { "<leader>e", function() Snacks.explorer() end, desc = "File Explorer" },
    -- find
    { "<leader>fb", function() Snacks.picker.buffers() end, desc = "Buffers" },
    { "<leader>fc", function() Snacks.picker.files({ cwd = vim.fn.stdpath("config") }) end, desc = "Find Config File" },
    { "<leader>ff", function() Snacks.picker.files() end, desc = "Find Files" },
    { "<leader>fg", function() Snacks.picker.git_files() end, desc = "Find Git Files" },
    { "<leader>fp", function() Snacks.picker.projects() end, desc = "Projects" },
    { "<leader>fr", function() Snacks.picker.recent() end, desc = "Recent" },
    -- git
    { "<leader>gb", function() Snacks.picker.git_branches() end, desc = "Git Branches" },
    { "<leader>gl", function() Snacks.picker.git_log() end, desc = "Git Log" },
    { "<leader>gL", function() Snacks.picker.git_log_line() end, desc = "Git Log Line" },
    { "<leader>gs", function() Snacks.picker.git_status() end, desc = "Git Status" },
    { "<leader>gS", function() Snacks.picker.git_stash() end, desc = "Git Stash" },
    { "<leader>gd", function() Snacks.picker.git_diff() end, desc = "Git Diff (Hunks)" },
    { "<leader>gf", function() Snacks.picker.git_log_file() end, desc = "Git Log File" },
    -- Grep
    { "<leader>sb", function() Snacks.picker.lines() end, desc = "Buffer Lines" },
    { "<leader>sB", function() Snacks.picker.grep_buffers() end, desc = "Grep Open Buffers" },
    { "<leader>sg", function() Snacks.picker.grep() end, desc = "Grep" },
    { "<leader>sw", function() Snacks.picker.grep_word() end, desc = "Visual selection or word", mode = { "n", "x" } },
    -- search
    { '<leader>s"', function() Snacks.picker.registers() end, desc = "Registers" },
    { '<leader>s/', function() Snacks.picker.search_history() end, desc = "Search History" },
    { "<leader>sa", function() Snacks.picker.autocmds() end, desc = "Autocmds" },
    { "<leader>sb", function() Snacks.picker.lines() end, desc = "Buffer Lines" },
    { "<leader>sc", function() Snacks.picker.command_history() end, desc = "Command History" },
    { "<leader>sC", function() Snacks.picker.commands() end, desc = "Commands" },
    { "<leader>sd", function() Snacks.picker.diagnostics() end, desc = "Diagnostics" },
    { "<leader>sD", function() Snacks.picker.diagnostics_buffer() end, desc = "Buffer Diagnostics" },
    { "<leader>sh", function() Snacks.picker.help() end, desc = "Help Pages" },
    { "<leader>sH", function() Snacks.picker.highlights() end, desc = "Highlights" },
    { "<leader>si", function() Snacks.picker.icons() end, desc = "Icons" },
    { "<leader>sj", function() Snacks.picker.jumps() end, desc = "Jumps" },
    { "<leader>sk", function() Snacks.picker.keymaps() end, desc = "Keymaps" },
    { "<leader>sl", function() Snacks.picker.loclist() end, desc = "Location List" },
    { "<leader>sm", function() Snacks.picker.marks() end, desc = "Marks" },
    { "<leader>sM", function() Snacks.picker.man() end, desc = "Man Pages" },
    { "<leader>sp", function() Snacks.picker.lazy() end, desc = "Search for Plugin Spec" },
    { "<leader>sq", function() Snacks.picker.qflist() end, desc = "Quickfix List" },
    { "<leader>sR", function() Snacks.picker.resume() end, desc = "Resume" },
    { "<leader>su", function() Snacks.picker.undo() end, desc = "Undo History" },
    { "<leader>uC", function() Snacks.picker.colorschemes() end, desc = "Colorschemes" },
    -- LSP
    { "gd", function() Snacks.picker.lsp_definitions() end, desc = "Goto Definition" },
    { "gD", function() Snacks.picker.lsp_declarations() end, desc = "Goto Declaration" },
    { "gr", function() Snacks.picker.lsp_references() end, nowait = true, desc = "References" },
    { "gI", function() Snacks.picker.lsp_implementations() end, desc = "Goto Implementation" },
    { "gy", function() Snacks.picker.lsp_type_definitions() end, desc = "Goto T[y]pe Definition" },
    { "<leader>ss", function() Snacks.picker.lsp_symbols() end, desc = "LSP Symbols" },
    { "<leader>sS", function() Snacks.picker.lsp_workspace_symbols() end, desc = "LSP Workspace Symbols" },
    -- Other
    { "<leader>z",  function() Snacks.zen() end, desc = "Toggle Zen Mode" },
    { "<leader>Z",  function() Snacks.zen.zoom() end, desc = "Toggle Zoom" },
    { "<leader>.",  function() Snacks.scratch() end, desc = "Toggle Scratch Buffer" },
    { "<leader>S",  function() Snacks.scratch.select() end, desc = "Select Scratch Buffer" },
    { "<leader>n",  function() Snacks.notifier.show_history() end, desc = "Notification History" },
    { "<leader>bd", function() Snacks.bufdelete() end, desc = "Delete Buffer" },
    { "<leader>cR", function() Snacks.rename.rename_file() end, desc = "Rename File" },
    { "<leader>gB", function() Snacks.gitbrowse() end, desc = "Git Browse", mode = { "n", "v" } },
    { "<leader>gg", function() Snacks.lazygit() end, desc = "Lazygit" },
    { "<leader>un", function() Snacks.notifier.hide() end, desc = "Dismiss All Notifications" },
    { "<c-/>",      function() Snacks.terminal() end, desc = "Toggle Terminal" },
    { "<c-_>",      function() Snacks.terminal() end, desc = "which_key_ignore" },
    { "]]",         function() Snacks.words.jump(vim.v.count1) end, desc = "Next Reference", mode = { "n", "t" } },
    { "[[",         function() Snacks.words.jump(-vim.v.count1) end, desc = "Prev Reference", mode = { "n", "t" } },
    {
      "<leader>N",
      desc = "Neovim News",
      function()
        Snacks.win({
          file = vim.api.nvim_get_runtime_file("doc/news.txt", false)[1],
          width = 0.6,
          height = 0.6,
          wo = {
            spell = false,
            wrap = false,
            signcolumn = "yes",
            statuscolumn = " ",
            conceallevel = 3,
          },
        })
      end,
    }
  },
  init = function()
    vim.api.nvim_create_autocmd("User", {
      pattern = "VeryLazy",
      callback = function()
        -- Setup some globals for debugging (lazy-loaded)
        _G.dd = function(...)
          Snacks.debug.inspect(...)
        end
        _G.bt = function()
          Snacks.debug.backtrace()
        end
        vim.print = _G.dd -- Override print to use snacks for `:=` command

        -- Create some toggle mappings
        Snacks.toggle.option("spell", { name = "Spelling" }):map("<leader>us")
        Snacks.toggle.option("wrap", { name = "Wrap" }):map("<leader>uw")
        Snacks.toggle.option("relativenumber", { name = "Relative Number" }):map("<leader>uL")
        Snacks.toggle.diagnostics():map("<leader>ud")
        Snacks.toggle.line_number():map("<leader>ul")
        Snacks.toggle.option("conceallevel", { off = 0, on = vim.o.conceallevel > 0 and vim.o.conceallevel or 2 }):map("<leader>uc")
        Snacks.toggle.treesitter():map("<leader>uT")
        Snacks.toggle.option("background", { off = "light", on = "dark", name = "Dark Background" }):map("<leader>ub")
        Snacks.toggle.inlay_hints():map("<leader>uh")
        Snacks.toggle.indent():map("<leader>ug")
        Snacks.toggle.dim():map("<leader>uD")
      end,
    })
  end,
}
```

<!-- example:end -->

## 🌈 Highlight Groups

Snacks defines **a lot** of highlight groups and it's impossible to document them all.

Instead, you can use the picker to see all the highlight groups.

```lua
Snacks.picker.highlights({pattern = "hl_group:^Snacks"})
```

README.md


## Description

<!-- Describe the big picture of your changes to communicate to the maintainers
  why we should accept this pull request. -->

## Related Issue(s)

<!--
  If this PR fixes any issues, please link to the issue here.
  - Fixes #<issue_number>
-->

## Screenshots

<!-- Add screenshots of the changes if applicable. -->

.github/PULL_REQUEST_TEMPLATE.md

🍿 A collection of QoL plugins for Neovim


# 🍿 animate

Efficient animation library including over 45 easing functions:

- [Emmanuel Oga's easing functions](https://github.com/EmmanuelOga/easing)
- [Easing functions overview](https://github.com/kikito/tween.lua?tab=readme-ov-file#easing-functions)

There's at any given time at most one timer running, that takes
care of all active animations, controlled by the `fps` setting.

You can at any time disable all animations with:

- `vim.g.snacks_animate = false` globally
- `vim.b.snacks_animate = false` locally for the buffer

Doing this, will disable `scroll`, `indent`, `dim` and all other animations.

<!-- docgen -->

## 📦 Setup

```lua
-- lazy.nvim
{
  "folke/snacks.nvim",
  ---@type snacks.Config
  opts = {
    animate = {
      -- your animate configuration comes here
      -- or leave it empty to use the default settings
      -- refer to the configuration section below
    }
  }
}
```

## ⚙ Config

```lua
---@class snacks.animate.Config
---@field easing? snacks.animate.easing|snacks.animate.easing.Fn
{
  ---@type snacks.animate.Duration|number
  duration = 20, -- ms per step
  easing = "linear",
  fps = 60, -- frames per second. Global setting for all animations
}
```

## 📚 Types

All easing functions take these parameters:

* `t` _(time)_: should go from 0 to duration
* `b` _(begin)_: value of the property being ease.
* `c` _(change)_: ending value of the property - beginning value of the property
* `d` _(duration)_: total duration of the animation

Some functions allow additional modifiers, like the elastic functions
which also can receive an amplitud and a period parameters (defaults
are included)

```lua
---@alias snacks.animate.easing.Fn fun(t: number, b: number, c: number, d: number): number
```

Duration can be specified as the total duration or the duration per step.
When both are specified, the minimum of both is used.

```lua
---@class snacks.animate.Duration
---@field step? number duration per step in ms
---@field total? number total duration in ms
```

```lua
---@class snacks.animate.Opts: snacks.animate.Config
---@field buf? number optional buffer to check if animations should be enabled
---@field int? boolean interpolate the value to an integer
---@field id? number|string unique identifier for the animation
```

```lua
---@class snacks.animate.ctx
---@field anim snacks.animate.Animation
---@field prev number
---@field done boolean
```

```lua
---@alias snacks.animate.cb fun(value:number, ctx: snacks.animate.ctx)
```

## 📦 Module

### `Snacks.animate()`

```lua
---@type fun(from: number, to: number, cb: snacks.animate.cb, opts?: snacks.animate.Opts): snacks.animate.Animation
Snacks.animate()
```

### `Snacks.animate.add()`

Add an animation

```lua
---@param from number
---@param to number
---@param cb snacks.animate.cb
---@param opts? snacks.animate.Opts
Snacks.animate.add(from, to, cb, opts)
```

### `Snacks.animate.del()`

Delete an animation

```lua
---@param id number|string
Snacks.animate.del(id)
```

### `Snacks.animate.enabled()`

Check if animations are enabled.
Will return false if `snacks_animate` is set to false or if the buffer
local variable `snacks_animate` is set to false.

```lua
---@param opts? {buf?: number, name?: string}
Snacks.animate.enabled(opts)
```

docs/animate.md


# 🍿 bigfile

`bigfile` adds a new filetype `bigfile` to Neovim that triggers when the file is
larger than the configured size. This automatically prevents things like LSP
and Treesitter attaching to the buffer.

Use the `setup` config function to further make changes to a `bigfile` buffer.
The context provides the actual filetype.

The default implementation enables `syntax` for the buffer and disables
[mini.animate](https://github.com/echasnovski/mini.animate) (if used)

<!-- docgen -->

## 📦 Setup

```lua
-- lazy.nvim
{
  "folke/snacks.nvim",
  ---@type snacks.Config
  opts = {
    bigfile = {
      -- your bigfile configuration comes here
      -- or leave it empty to use the default settings
      -- refer to the configuration section below
    }
  }
}
```

## ⚙ Config

```lua
---@class snacks.bigfile.Config
---@field enabled? boolean
{
  notify = true, -- show notification when big file detected
  size = 1.5 * 1024 * 1024, -- 1.5MB
  line_length = 1000, -- average line length (useful for minified files)
  -- Enable or disable features when big file detected
  ---@param ctx {buf: number, ft:string}
  setup = function(ctx)
    if vim.fn.exists(":NoMatchParen") ~= 0 then
      vim.cmd([[NoMatchParen]])
    end
    Snacks.util.wo(0, { foldmethod = "manual", statuscolumn = "", conceallevel = 0 })
    vim.b.minianimate_disable = true
    vim.schedule(function()
      if vim.api.nvim_buf_is_valid(ctx.buf) then
        vim.bo[ctx.buf].syntax = ctx.ft
      end
    end)
  end,
}
```

docs/bigfile.md


# 🍿 bufdelete

Delete buffers without disrupting window layout.

If the buffer you want to close has changes,
a prompt will be shown to save or discard.

<!-- docgen -->

## 📚 Types

```lua
---@class snacks.bufdelete.Opts
---@field buf? number Buffer to delete. Defaults to the current buffer
---@field file? string Delete buffer by file name. If provided, `buf` is ignored
---@field force? boolean Delete the buffer even if it is modified
---@field filter? fun(buf: number): boolean Filter buffers to delete
---@field wipe? boolean Wipe the buffer instead of deleting it (see `:h :bwipeout`)
```

## 📦 Module

### `Snacks.bufdelete()`

```lua
---@type fun(buf?: number|snacks.bufdelete.Opts)
Snacks.bufdelete()
```

### `Snacks.bufdelete.all()`

Delete all buffers

```lua
---@param opts? snacks.bufdelete.Opts
Snacks.bufdelete.all(opts)
```

### `Snacks.bufdelete.delete()`

Delete a buffer:
- either the current buffer if `buf` is not provided
- or the buffer `buf` if it is a number
- or every buffer for which `buf` returns true if it is a function

```lua
---@param opts? number|snacks.bufdelete.Opts
Snacks.bufdelete.delete(opts)
```

### `Snacks.bufdelete.other()`

Delete all buffers except the current one

```lua
---@param opts? snacks.bufdelete.Opts
Snacks.bufdelete.other(opts)
```

docs/bufdelete.md


# 🍿 dashboard

## ✨ Features

- declarative configuration
- flexible layouts
- multiple vertical panes
- built-in sections:
  - **header**: show a header
  - **keys**: show keymaps
  - **projects**: show recent projects
  - **recent_files**: show recent files
  - **session**: session support
  - **startup**: startup time (lazy.nvim)
  - **terminal**: colored terminal output
- super fast `terminal` sections with automatic caching

## 🚀 Usage

The dashboard comes with a set of default sections, that
can be customized with `opts.preset` or
fully replaced with `opts.sections`.

The default preset comes with support for:

- pickers:
  - [fzf-lua](https://github.com/ibhagwan/fzf-lua)
  - [telescope.nvim](https://github.com/nvim-telescope/telescope.nvim)
  - [mini.pick](https://github.com/echasnovski/mini.pick)
- session managers: (only works with [lazy.nvim](https://github.com/folke/lazy.nvim))
  - [persistence.nvim](https://github.com/folke/persistence.nvim)
  - [persisted.nvim](https://github.com/olimorris/persisted.nvim)
  - [neovim-session-manager](https://github.com/Shatur/neovim-session-manager)
  - [posession.nvim](https://github.com/jedrzejboczar/possession.nvim)
  - [mini.sessions](https://github.com/echasnovski/mini.sessions)

### Section actions

A section can have an `action` property that will be executed as:

- a command if it starts with `:`
- a keymap if it's a string not starting with `:`
- a function if it's a function

```lua
-- command
{
  action = ":Telescope find_files",
  key = "f",
},
```

```lua
-- keymap
{
  action = "<leader>ff",
  key = "f",
},
```

```lua
-- function
{
  action = function()
    require("telescope.builtin").find_files()
  end,
  key = "h",
},
```

### Item text

Every item should have a `text` property with an array of `snacks.dashboard.Text` objects.
If the `text` property is not provided, the `snacks.dashboard.Config.formats`
will be used to generate the text.

In the example below, both sections are equivalent.

```lua
{
  text = {
    { "  ", hl = "SnacksDashboardIcon" },
    { "Find File", hl = "SnacksDashboardDesc", width = 50 },
    { "[f]", hl = "SnacksDashboardKey" },
  },
  action = ":Telescope find_files",
  key = "f",
},
```

```lua
{
  action = ":Telescope find_files",
  key = "f",
  desc = "Find File",
  icon = " ",
},
```

<!-- docgen -->

## 📦 Setup

```lua
-- lazy.nvim
{
  "folke/snacks.nvim",
  ---@type snacks.Config
  opts = {
    dashboard = {
      -- your dashboard configuration comes here
      -- or leave it empty to use the default settings
      -- refer to the configuration section below
    }
  }
}
```

## ⚙ Config

```lua
---@class snacks.dashboard.Config
---@field enabled? boolean
---@field sections snacks.dashboard.Section
---@field formats table<string, snacks.dashboard.Text|fun(item:snacks.dashboard.Item, ctx:snacks.dashboard.Format.ctx):snacks.dashboard.Text>
{
  width = 60,
  row = nil, -- dashboard position. nil for center
  col = nil, -- dashboard position. nil for center
  pane_gap = 4, -- empty columns between vertical panes
  autokeys = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", -- autokey sequence
  -- These settings are used by some built-in sections
  preset = {
    -- Defaults to a picker that supports `fzf-lua`, `telescope.nvim` and `mini.pick`
    ---@type fun(cmd:string, opts:table)|nil
    pick = nil,
    -- Used by the `keys` section to show keymaps.
    -- Set your custom keymaps here.
    -- When using a function, the `items` argument are the default keymaps.
    ---@type snacks.dashboard.Item[]
    keys = {
      { icon = " ", key = "f", desc = "Find File", action = ":lua Snacks.dashboard.pick('files')" },
      { icon = " ", key = "n", desc = "New File", action = ":ene | startinsert" },
      { icon = " ", key = "g", desc = "Find Text", action = ":lua Snacks.dashboard.pick('live_grep')" },
      { icon = " ", key = "r", desc = "Recent Files", action = ":lua Snacks.dashboard.pick('oldfiles')" },
      { icon = " ", key = "c", desc = "Config", action = ":lua Snacks.dashboard.pick('files', {cwd = vim.fn.stdpath('config')})" },
      { icon = " ", key = "s", desc = "Restore Session", section = "session" },
      { icon = "󰒲 ", key = "L", desc = "Lazy", action = ":Lazy", enabled = package.loaded.lazy ~= nil },
      { icon = " ", key = "q", desc = "Quit", action = ":qa" },
    },
    -- Used by the `header` section
    header = [[
███╗   ██╗███████╗ ██████╗ ██╗   ██╗██╗███╗   ███╗
████╗  ██║██╔════╝██╔═══██╗██║   ██║██║████╗ ████║
██╔██╗ ██║█████╗  ██║   ██║██║   ██║██║██╔████╔██║
██║╚██╗██║██╔══╝  ██║   ██║╚██╗ ██╔╝██║██║╚██╔╝██║
██║ ╚████║███████╗╚██████╔╝ ╚████╔╝ ██║██║ ╚═╝ ██║
╚═╝  ╚═══╝╚══════╝ ╚═════╝   ╚═══╝  ╚═╝╚═╝     ╚═╝]],
  },
  -- item field formatters
  formats = {
    icon = function(item)
      if item.file and item.icon == "file" or item.icon == "directory" then
        return M.icon(item.file, item.icon)
      end
      return { item.icon, width = 2, hl = "icon" }
    end,
    footer = { "%s", align = "center" },
    header = { "%s", align = "center" },
    file = function(item, ctx)
      local fname = vim.fn.fnamemodify(item.file, ":~")
      fname = ctx.width and #fname > ctx.width and vim.fn.pathshorten(fname) or fname
      if #fname > ctx.width then
        local dir = vim.fn.fnamemodify(fname, ":h")
        local file = vim.fn.fnamemodify(fname, ":t")
        if dir and file then
          file = file:sub(-(ctx.width - #dir - 2))
          fname = dir .. "/…" .. file
        end
      end
      local dir, file = fname:match("^(.*)/(.+)$")
      return dir and { { dir .. "/", hl = "dir" }, { file, hl = "file" } } or { { fname, hl = "file" } }
    end,
  },
  sections = {
    { section = "header" },
    { section = "keys", gap = 1, padding = 1 },
    { section = "startup" },
  },
}
```

## 🚀 Examples

### `advanced`

A more advanced example using multiple panes
![image](https://github.com/user-attachments/assets/bbf4d2cd-6fc5-4122-a462-0ca59ba89545)

```lua
{
  sections = {
    { section = "header" },
    {
      pane = 2,
      section = "terminal",
      cmd = "colorscript -e square",
      height = 5,
      padding = 1,
    },
    { section = "keys", gap = 1, padding = 1 },
    { pane = 2, icon = " ", title = "Recent Files", section = "recent_files", indent = 2, padding = 1 },
    { pane = 2, icon = " ", title = "Projects", section = "projects", indent = 2, padding = 1 },
    {
      pane = 2,
      icon = " ",
      title = "Git Status",
      section = "terminal",
      enabled = function()
        return Snacks.git.get_root() ~= nil
      end,
      cmd = "git status --short --branch --renames",
      height = 5,
      padding = 1,
      ttl = 5 * 60,
      indent = 3,
    },
    { section = "startup" },
  },
}
```

### `chafa`

An example using the `chafa` command to display an image
![image](https://github.com/user-attachments/assets/e498ef8f-83ce-4917-a720-8cb31d98ecec)

```lua
{
  sections = {
    {
      section = "terminal",
      cmd = "chafa ~/.config/wall.png --format symbols --symbols vhalf --size 60x17 --stretch; sleep .1",
      height = 17,
      padding = 1,
    },
    {
      pane = 2,
      { section = "keys", gap = 1, padding = 1 },
      { section = "startup" },
    },
  },
}
```

### `compact_files`

A more compact version of the `files` example
![image](https://github.com/user-attachments/assets/772e84fe-b220-4841-bbe9-6e28780dc30a)

```lua
{
  sections = {
    { section = "header" },
    { icon = " ", title = "Keymaps", section = "keys", indent = 2, padding = 1 },
    { icon = " ", title = "Recent Files", section = "recent_files", indent = 2, padding = 1 },
    { icon = " ", title = "Projects", section = "projects", indent = 2, padding = 1 },
    { section = "startup" },
  },
}
```

### `doom`

Similar to the Emacs Doom dashboard
![image](https://github.com/user-attachments/assets/823f702d-e5d0-449a-afd2-684e1fb97622)

```lua
{
  sections = {
    { section = "header" },
    { section = "keys", gap = 1, padding = 1 },
    { section = "startup" },
  },
}
```

### `files`

A simple example with a header, keys, recent files, and projects
![image](https://github.com/user-attachments/assets/e98997b6-07d3-4162-bc06-2768b78fe353)

```lua
{
  sections = {
    { section = "header" },
    { section = "keys", gap = 1 },
    { icon = " ", title = "Recent Files", section = "recent_files", indent = 2, padding = { 2, 2 } },
    { icon = " ", title = "Projects", section = "projects", indent = 2, padding = 2 },
    { section = "startup" },
  },
}
```

### `github`

Advanced example using the GitHub CLI.
![image](https://github.com/user-attachments/assets/747d7386-ef05-487f-9550-3e5ef94869fc)

```lua
{
  sections = {
    { section = "header" },
    {
      pane = 2,
      section = "terminal",
      cmd = "colorscript -e square",
      height = 5,
      padding = 1,
    },
    { section = "keys", gap = 1, padding = 1 },
    {
      pane = 2,
      icon = " ",
      desc = "Browse Repo",
      padding = 1,
      key = "b",
      action = function()
        Snacks.gitbrowse()
      end,
    },
    function()
      local in_git = Snacks.git.get_root() ~= nil
      local cmds = {
        {
          title = "Notifications",
          cmd = "gh notify -s -a -n5",
          action = function()
            vim.ui.open("https://github.com/notifications")
          end,
          key = "n",
          icon = " ",
          height = 5,
          enabled = true,
        },
        {
          title = "Open Issues",
          cmd = "gh issue list -L 3",
          key = "i",
          action = function()
            vim.fn.jobstart("gh issue list --web", { detach = true })
          end,
          icon = " ",
          height = 7,
        },
        {
          icon = " ",
          title = "Open PRs",
          cmd = "gh pr list -L 3",
          key = "P",
          action = function()
            vim.fn.jobstart("gh pr list --web", { detach = true })
          end,
          height = 7,
        },
        {
          icon = " ",
          title = "Git Status",
          cmd = "git --no-pager diff --stat -B -M -C",
          height = 10,
        },
      }
      return vim.tbl_map(function(cmd)
        return vim.tbl_extend("force", {
          pane = 2,
          section = "terminal",
          enabled = in_git,
          padding = 1,
          ttl = 5 * 60,
          indent = 3,
        }, cmd)
      end, cmds)
    end,
    { section = "startup" },
  },
}
```

### `pokemon`

Pokemons, because why not?
![image](https://github.com/user-attachments/assets/2fb17ecc-8bc0-48d3-a023-aa8dfc70247e)

```lua
{
  sections = {
    { section = "header" },
    { section = "keys", gap = 1, padding = 1 },
    { section = "startup" },
    {
      section = "terminal",
      cmd = "pokemon-colorscripts -r --no-title; sleep .1",
      random = 10,
      pane = 2,
      indent = 4,
      height = 30,
    },
  },
}
```

### `startify`

Similar to the Vim Startify dashboard
![image](https://github.com/user-attachments/assets/561eff8c-ddf0-4de9-8485-e6be18a19c0b)

```lua
{
  formats = {
    key = function(item)
      return { { "[", hl = "special" }, { item.key, hl = "key" }, { "]", hl = "special" } }
    end,
  },
  sections = {
    { section = "terminal", cmd = "fortune -s | cowsay", hl = "header", padding = 1, indent = 8 },
    { title = "MRU", padding = 1 },
    { section = "recent_files", limit = 8, padding = 1 },
    { title = "MRU ", file = vim.fn.fnamemodify(".", ":~"), padding = 1 },
    { section = "recent_files", cwd = true, limit = 8, padding = 1 },
    { title = "Sessions", padding = 1 },
    { section = "projects", padding = 1 },
    { title = "Bookmarks", padding = 1 },
    { section = "keys" },
  },
}
```

## 🎨 Styles

Check the [styles](https://github.com/folke/snacks.nvim/blob/main/docs/styles.md)
docs for more information on how to customize these styles

### `dashboard`

The default style for the dashboard.
When opening the dashboard during startup, only the `bo` and `wo` options are used.
The other options are used with `:lua Snacks.dashboard()`

```lua
{
  zindex = 10,
  height = 0,
  width = 0,
  bo = {
    bufhidden = "wipe",
    buftype = "nofile",
    buflisted = false,
    filetype = "snacks_dashboard",
    swapfile = false,
    undofile = false,
  },
  wo = {
    colorcolumn = "",
    cursorcolumn = false,
    cursorline = false,
    foldmethod = "manual",
    list = false,
    number = false,
    relativenumber = false,
    sidescrolloff = 0,
    signcolumn = "no",
    spell = false,
    statuscolumn = "",
    statusline = "",
    winbar = "",
    winhighlight = "Normal:SnacksDashboardNormal,NormalFloat:SnacksDashboardNormal",
    wrap = false,
  },
}
```

## 📚 Types

```lua
---@class snacks.dashboard.Item
---@field indent? number
---@field align? "left" | "center" | "right"
---@field gap? number the number of empty lines between child items
---@field padding? number | {[1]:number, [2]:number} bottom or {bottom, top} padding
--- The action to run when the section is selected or the key is pressed.
--- * if it's a string starting with `:`, it will be run as a command
--- * if it's a string, it will be executed as a keymap
--- * if it's a function, it will be called
---@field action? snacks.dashboard.Action
---@field enabled? boolean|fun(opts:snacks.dashboard.Opts):boolean if false, the section will be disabled
---@field section? string the name of a section to include. See `Snacks.dashboard.sections`
---@field [string] any section options
---@field key? string shortcut key
---@field hidden? boolean when `true`, the item will not be shown, but the key will still be assigned
---@field autokey? boolean automatically assign a numerical key
---@field label? string
---@field desc? string
---@field file? string
---@field footer? string
---@field header? string
---@field icon? string
---@field title? string
---@field text? string|snacks.dashboard.Text[]
```

```lua
---@alias snacks.dashboard.Format.ctx {width?:number}
---@alias snacks.dashboard.Action string|fun(self:snacks.dashboard.Class)
---@alias snacks.dashboard.Gen fun(self:snacks.dashboard.Class):snacks.dashboard.Section?
---@alias snacks.dashboard.Section snacks.dashboard.Item|snacks.dashboard.Gen|snacks.dashboard.Section[]
```

```lua
---@class snacks.dashboard.Text
---@field [1] string the text
---@field hl? string the highlight group
---@field width? number the width used for alignment
---@field align? "left" | "center" | "right"
```

```lua
---@class snacks.dashboard.Opts: snacks.dashboard.Config
---@field buf? number the buffer to use. If not provided, a new buffer will be created
---@field win? number the window to use. If not provided, a new floating window will be created
```

## 📦 Module

### `Snacks.dashboard()`

```lua
---@type fun(opts?: snacks.dashboard.Opts): snacks.dashboard.Class
Snacks.dashboard()
```

### `Snacks.dashboard.have_plugin()`

Checks if the plugin is installed.
Only works with [lazy.nvim](https://github.com/folke/lazy.nvim)

```lua
---@param name string
Snacks.dashboard.have_plugin(name)
```

### `Snacks.dashboard.health()`

```lua
Snacks.dashboard.health()
```

### `Snacks.dashboard.icon()`

Get an icon

```lua
---@param name string
---@param cat? string
---@return snacks.dashboard.Text
Snacks.dashboard.icon(name, cat)
```

### `Snacks.dashboard.oldfiles()`

```lua
---@param opts? {filter?: table<string, boolean>}
---@return fun():string?
Snacks.dashboard.oldfiles(opts)
```

### `Snacks.dashboard.open()`

```lua
---@param opts? snacks.dashboard.Opts
---@return snacks.dashboard.Class
Snacks.dashboard.open(opts)
```

### `Snacks.dashboard.pick()`

Used by the default preset to pick something

```lua
---@param cmd? string
Snacks.dashboard.pick(cmd, opts)
```

### `Snacks.dashboard.sections.header()`

```lua
---@return snacks.dashboard.Gen
Snacks.dashboard.sections.header()
```

### `Snacks.dashboard.sections.keys()`

```lua
---@return snacks.dashboard.Gen
Snacks.dashboard.sections.keys()
```

### `Snacks.dashboard.sections.projects()`

Get the most recent projects based on git roots of recent files.
The default action will change the directory to the project root,
try to restore the session and open the picker if the session is not restored.
You can customize the behavior by providing a custom action.
Use `opts.dirs` to provide a list of directories to use instead of the git roots.

```lua
---@param opts? {limit?:number, dirs?:(string[]|fun():string[]), pick?:boolean, session?:boolean, action?:fun(dir)}
Snacks.dashboard.sections.projects(opts)
```

### `Snacks.dashboard.sections.recent_files()`

Get the most recent files, optionally filtered by the
current working directory or a custom directory.

```lua
---@param opts? {limit?:number, cwd?:string|boolean, filter?:fun(file:string):boolean?}
---@return snacks.dashboard.Gen
Snacks.dashboard.sections.recent_files(opts)
```

### `Snacks.dashboard.sections.session()`

Adds a section to restore the session if any of the supported plugins are installed.

```lua
---@param item? snacks.dashboard.Item
---@return snacks.dashboard.Item?
Snacks.dashboard.sections.session(item)
```

### `Snacks.dashboard.sections.startup()`

Add the startup section

```lua
---@param opts? {icon?:string}
---@return snacks.dashboard.Section?
Snacks.dashboard.sections.startup(opts)
```

### `Snacks.dashboard.sections.terminal()`

```lua
---@param opts {cmd:string|string[], ttl?:number, height?:number, width?:number, random?:number}|snacks.dashboard.Item
---@return snacks.dashboard.Gen
Snacks.dashboard.sections.terminal(opts)
```

### `Snacks.dashboard.setup()`

Check if the dashboard should be opened

```lua
Snacks.dashboard.setup()
```

### `Snacks.dashboard.update()`

Update the dashboard

```lua
Snacks.dashboard.update()
```

docs/dashboard.md


# 🍿 debug

Utility functions you can use in your code.

Personally, I have the code below at the top of my `init.lua`:

```lua
_G.dd = function(...)
  Snacks.debug.inspect(...)
end
_G.bt = function()
  Snacks.debug.backtrace()
end
vim.print = _G.dd
```

What this does:

- Add a global `dd(...)` you can use anywhere to quickly show a
  notification with a pretty printed dump of the object(s)
  with lua treesitter highlighting
- Add a global `bt()` to show a notification with a pretty
  backtrace.
- Override Neovim's `vim.print`, which is also used by `:= {something = 123}`

![image](https://github.com/user-attachments/assets/0517aed7-fbd0-42ee-8058-c213410d80a7)

<!-- docgen -->

## 📚 Types

```lua
---@class snacks.debug.cmd
---@field cmd string|string[]
---@field level? snacks.notifier.level
---@field title? string
---@field args? string[]
---@field cwd? string
---@field group? boolean
---@field notify? boolean
---@field footer? string
---@field header? string
---@field props? table<string, string>
```

```lua
---@alias snacks.debug.Trace {name: string, time: number, [number]:snacks.debug.Trace}
---@alias snacks.debug.Stat {name:string, time:number, count?:number, depth?:number}
```

## 📦 Module

### `Snacks.debug()`

```lua
---@type fun(...)
Snacks.debug()
```

### `Snacks.debug.backtrace()`

Show a notification with a pretty backtrace

```lua
---@param msg? string|string[]
---@param opts? snacks.notify.Opts
Snacks.debug.backtrace(msg, opts)
```

### `Snacks.debug.cmd()`

```lua
---@param opts snacks.debug.cmd
Snacks.debug.cmd(opts)
```

### `Snacks.debug.inspect()`

Show a notification with a pretty printed dump of the object(s)
with lua treesitter highlighting and the location of the caller

```lua
Snacks.debug.inspect(...)
```

### `Snacks.debug.log()`

Log a message to the file `./debug.log`.
- a timestamp will be added to every message.
- accepts multiple arguments and pretty prints them.
- if the argument is not a string, it will be printed using `vim.inspect`.
- if the message is smaller than 120 characters, it will be printed on a single line.

```lua
Snacks.debug.log("Hello", { foo = "bar" }, 42)
-- 2024-11-08 08:56:52 Hello { foo = "bar" } 42
```

```lua
Snacks.debug.log(...)
```

### `Snacks.debug.metrics()`

```lua
Snacks.debug.metrics()
```

### `Snacks.debug.profile()`

Very simple function to profile a lua function.
* **flush**: set to `true` to use `jit.flush` in every iteration.
* **count**: defaults to 100

```lua
---@param fn fun()
---@param opts? {count?: number, flush?: boolean, title?: string}
Snacks.debug.profile(fn, opts)
```

### `Snacks.debug.run()`

Run the current buffer or a range of lines.
Shows the output of `print` inlined with the code.
Any error will be shown as a diagnostic.

```lua
---@param opts? {name?:string, buf?:number, print?:boolean}
Snacks.debug.run(opts)
```

### `Snacks.debug.size()`

```lua
Snacks.debug.size(bytes)
```

### `Snacks.debug.stats()`

```lua
---@param opts? {min?: number, show?:boolean}
---@return {summary:table<string, snacks.debug.Stat>, trace:snacks.debug.Stat[], traces:snacks.debug.Trace[]}
Snacks.debug.stats(opts)
```

### `Snacks.debug.trace()`

```lua
---@param name string?
Snacks.debug.trace(name)
```

### `Snacks.debug.tracemod()`

```lua
---@param modname string
---@param mod? table
---@param suffix? string
Snacks.debug.tracemod(modname, mod, suffix)
```

docs/debug.md


# 🍿 dim

Focus on the active scope by dimming the rest.

Similar plugins:

- [twilight.nvim](https://github.com/folke/twilight.nvim)
- [limelight.vim](https://github.com/junegunn/limelight.vim)
- [goyo.vim](https://github.com/junegunn/goyo.vim)

![image](https://github.com/user-attachments/assets/c0c5ffda-aaeb-4578-8a18-abee2e443a93)

<!-- docgen -->

## 📦 Setup

```lua
-- lazy.nvim
{
  "folke/snacks.nvim",
  ---@type snacks.Config
  opts = {
    dim = {
      -- your dim configuration comes here
      -- or leave it empty to use the default settings
      -- refer to the configuration section below
    }
  }
}
```

## ⚙ Config

```lua
---@class snacks.dim.Config
{
  ---@type snacks.scope.Config
  scope = {
    min_size = 5,
    max_size = 20,
    siblings = true,
  },
  -- animate scopes. Enabled by default for Neovim >= 0.10
  -- Works on older versions but has to trigger redraws during animation.
  ---@type snacks.animate.Config|{enabled?: boolean}
  animate = {
    enabled = vim.fn.has("nvim-0.10") == 1,
    easing = "outQuad",
    duration = {
      step = 20, -- ms per step
      total = 300, -- maximum duration
    },
  },
  -- what buffers to dim
  filter = function(buf)
    return vim.g.snacks_dim ~= false and vim.b[buf].snacks_dim ~= false and vim.bo[buf].buftype == ""
  end,
}
```

## 📦 Module

### `Snacks.dim()`

```lua
---@type fun(opts: snacks.dim.Config)
Snacks.dim()
```

### `Snacks.dim.disable()`

Disable dimming

```lua
Snacks.dim.disable()
```

### `Snacks.dim.enable()`

```lua
---@param opts? snacks.dim.Config
Snacks.dim.enable(opts)
```

docs/dim.md


# 🍿 explorer

A file explorer for snacks. This is actually a [picker](https://github.com/folke/snacks.nvim/blob/main/docs/picker.md#explorer) in disguise.

This module provide a shortcut to open the explorer picker and
a setup function to replace netrw with the explorer.

When the explorer and `replace_netrw` is enabled, the explorer will be opened:

- when you start `nvim` with a directory
- when you open a directory in vim

Configuring the explorer picker is done with the [picker options](https://github.com/folke/snacks.nvim/blob/main/docs/picker.md#explorer).

```lua
-- lazy.nvim
{
  "folke/snacks.nvim",
  ---@type snacks.Config
  opts = {
    explorer = {
      -- your explorer configuration comes here
      -- or leave it empty to use the default settings
      -- refer to the configuration section below
    },
    picker = {
      sources = {
        explorer = {
          -- your explorer picker configuration comes here
          -- or leave it empty to use the default settings
        }
      }
    }
  }
}
```

![image](https://github.com/user-attachments/assets/e09d25f8-8559-441c-a0f7-576d2aa57097)

<!-- docgen -->

## 📦 Setup

```lua
-- lazy.nvim
{
  "folke/snacks.nvim",
  ---@type snacks.Config
  opts = {
    explorer = {
      -- your explorer configuration comes here
      -- or leave it empty to use the default settings
      -- refer to the configuration section below
    }
  }
}
```

## ⚙ Config

These are just the general explorer settings.
To configure the explorer picker, see `snacks.picker.explorer.Config`

```lua
---@class snacks.explorer.Config
{
  replace_netrw = true, -- Replace netrw with the snacks explorer
}
```

## 📦 Module

### `Snacks.explorer()`

```lua
---@type fun(opts?: snacks.picker.explorer.Config): snacks.Picker
Snacks.explorer()
```

### `Snacks.explorer.open()`

Shortcut to open the explorer picker

```lua
---@param opts? snacks.picker.explorer.Config|{}
Snacks.explorer.open(opts)
```

### `Snacks.explorer.reveal()`

Reveals the given file/buffer or the current buffer in the explorer

```lua
---@param opts? {file?:string, buf?:number}
Snacks.explorer.reveal(opts)
```

docs/explorer.md


# 🍿 git

<!-- docgen -->

## 🎨 Styles

Check the [styles](https://github.com/folke/snacks.nvim/blob/main/docs/styles.md)
docs for more information on how to customize these styles

### `blame_line`

```lua
{
  width = 0.6,
  height = 0.6,
  border = "rounded",
  title = " Git Blame ",
  title_pos = "center",
  ft = "git",
}
```

## 📦 Module

### `Snacks.git.blame_line()`

Show git log for the current line.

```lua
---@param opts? snacks.terminal.Opts | {count?: number}
Snacks.git.blame_line(opts)
```

### `Snacks.git.get_root()`

Gets the git root for a buffer or path.
Defaults to the current buffer.

```lua
---@param path? number|string buffer or path
---@return string?
Snacks.git.get_root(path)
```

docs/git.md


# 🍿 gitbrowse

Open the repo of the active file in the browser (e.g., GitHub)

<!-- docgen -->

## 📦 Setup

```lua
-- lazy.nvim
{
  "folke/snacks.nvim",
  ---@type snacks.Config
  opts = {
    gitbrowse = {
      -- your gitbrowse configuration comes here
      -- or leave it empty to use the default settings
      -- refer to the configuration section below
    }
  }
}
```

## ⚙ Config

```lua
---@class snacks.gitbrowse.Config
---@field url_patterns? table<string, table<string, string|fun(fields:snacks.gitbrowse.Fields):string>>
{
  notify = true, -- show notification on open
  -- Handler to open the url in a browser
  ---@param url string
  open = function(url)
    if vim.fn.has("nvim-0.10") == 0 then
      require("lazy.util").open(url, { system = true })
      return
    end
    vim.ui.open(url)
  end,
  ---@type "repo" | "branch" | "file" | "commit" | "permalink"
  what = "commit", -- what to open. not all remotes support all types
  branch = nil, ---@type string?
  line_start = nil, ---@type number?
  line_end = nil, ---@type number?
  -- patterns to transform remotes to an actual URL
  remote_patterns = {
    { "^(https?://.*)%.git$"              , "%1" },
    { "^git@(.+):(.+)%.git$"              , "https://%1/%2" },
    { "^git@(.+):(.+)$"                   , "https://%1/%2" },
    { "^git@(.+)/(.+)$"                   , "https://%1/%2" },
    { "^org%-%d+@(.+):(.+)%.git$"         , "https://%1/%2" },
    { "^ssh://git@(.*)$"                  , "https://%1" },
    { "^ssh://([^:/]+)(:%d+)/(.*)$"       , "https://%1/%3" },
    { "^ssh://([^/]+)/(.*)$"              , "https://%1/%2" },
    { "ssh%.dev%.azure%.com/v3/(.*)/(.*)$", "dev.azure.com/%1/_git/%2" },
    { "^https://%w*@(.*)"                 , "https://%1" },
    { "^git@(.*)"                         , "https://%1" },
    { ":%d+"                              , "" },
    { "%.git$"                            , "" },
  },
  url_patterns = {
    ["github%.com"] = {
      branch = "/tree/{branch}",
      file = "/blob/{branch}/{file}#L{line_start}-L{line_end}",
      permalink = "/blob/{commit}/{file}#L{line_start}-L{line_end}",
      commit = "/commit/{commit}",
    },
    ["gitlab%.com"] = {
      branch = "/-/tree/{branch}",
      file = "/-/blob/{branch}/{file}#L{line_start}-L{line_end}",
      permalink = "/-/blob/{commit}/{file}#L{line_start}-L{line_end}",
      commit = "/-/commit/{commit}",
    },
    ["bitbucket%.org"] = {
      branch = "/src/{branch}",
      file = "/src/{branch}/{file}#lines-{line_start}-L{line_end}",
      permalink = "/src/{commit}/{file}#lines-{line_start}-L{line_end}",
      commit = "/commits/{commit}",
    },
    ["git.sr.ht"] = {
      branch = "/tree/{branch}",
      file = "/tree/{branch}/item/{file}",
      permalink = "/tree/{commit}/item/{file}#L{line_start}",
      commit = "/commit/{commit}",
    },
  },
}
```

## 📚 Types

```lua
---@class snacks.gitbrowse.Fields
---@field branch? string
---@field file? string
---@field line_start? number
---@field line_end? number
---@field commit? string
---@field line_count? number
```

## 📦 Module

### `Snacks.gitbrowse()`

```lua
---@type fun(opts?: snacks.gitbrowse.Config)
Snacks.gitbrowse()
```

### `Snacks.gitbrowse.get_url()`

```lua
---@param repo string
---@param fields snacks.gitbrowse.Fields
---@param opts? snacks.gitbrowse.Config
Snacks.gitbrowse.get_url(repo, fields, opts)
```

### `Snacks.gitbrowse.open()`

```lua
---@param opts? snacks.gitbrowse.Config
Snacks.gitbrowse.open(opts)
```

docs/gitbrowse.md


# 🍿 health

<!-- docgen -->

## 📚 Types

```lua
---@class snacks.health.Tool
---@field cmd string|string[]
---@field version? string|false
---@field enabled? boolean
```

```lua
---@alias snacks.health.Tool.spec (string|snacks.health.Tool)[]|snacks.health.Tool|string
```

## 📦 Module

```lua
---@class snacks.health
---@field ok fun(msg: string)
---@field warn fun(msg: string)
---@field error fun(msg: string)
---@field info fun(msg: string)
---@field start fun(msg: string)
Snacks.health = {}
```

### `Snacks.health.check()`

```lua
Snacks.health.check()
```

### `Snacks.health.has_lang()`

Check if the given languages are available in treesitter

```lua
---@param langs string[]|string
Snacks.health.has_lang(langs)
```

### `Snacks.health.have_tool()`

Check if any of the tools are available, with an optional version check

```lua
---@param tools snacks.health.Tool.spec
Snacks.health.have_tool(tools)
```

docs/health.md


# 🍿 image

![Image](https://github.com/user-attachments/assets/4e8a686c-bf41-4989-9d74-1641ecf2835f)

## ✨ Features

- Image viewer using the [Kitty Graphics Protocol](https://sw.kovidgoyal.net/kitty/graphics-protocol/).
- open images in a wide range of formats:
  `pdf`, `png`, `jpg`, `jpeg`, `gif`, `bmp`, `webp`, `tiff`, `heic`, `avif`, `mp4`, `mov`, `avi`, `mkv`, `webm`
- Supports inline image rendering in:
  `markdown`, `html`, `norg`, `tsx`, `javascript`, `css`, `vue`, `svelte`, `scss`, `latex`, `typst`
- LaTex math expressions in `markdown` and `latex` documents

Terminal support:

- [kitty](https://sw.kovidgoyal.net/kitty/)
- [ghostty](https://ghostty.org/)
- [wezterm](https://wezfurlong.org/wezterm/)
  Wezterm has only limited support for the kitty graphics protocol.
  Inline image rendering is not supported.
- [tmux](https://github.com/tmux/tmux)
  Snacks automatically tries to enable `allow-passthrough=on` for tmux,
  but you may need to enable it manually in your tmux configuration.
- [zellij](https://github.com/zellij-org/zellij) is **not** supported,
  since they don't have any support for passthrough

Image will be transferred to the terminal by filename or by sending the image
date in case `ssh` is detected.

In some cases you may need to force snacks to detect or not detect a certain
environment. You can do this by setting `SNACKS_${ENV_NAME}` to `true` or `false`.

For example, to force detection of **ghostty** you can set `SNACKS_GHOSTTY=true`.

In order to automatically display the image when opening an image file,
or to have imaged displayed in supported document formats like `markdown` or `html`,
you need to enable the `image` plugin in your `snacks` config.

[ImageMagick](https://imagemagick.org/index.php) is required to convert images
to the supported formats (all except PNG).

In case of issues, make sure to run `:checkhealth snacks`.

<!-- docgen -->

## 📦 Setup

```lua
-- lazy.nvim
{
  "folke/snacks.nvim",
  ---@type snacks.Config
  opts = {
    image = {
      -- your image configuration comes here
      -- or leave it empty to use the default settings
      -- refer to the configuration section below
    }
  }
}
```

## ⚙ Config

```lua
---@class snacks.image.Config
---@field enabled? boolean enable image viewer
---@field wo? vim.wo|{} options for windows showing the image
---@field bo? vim.bo|{} options for the image buffer
---@field formats? string[]
--- Resolves a reference to an image with src in a file (currently markdown only).
--- Return the absolute path or url to the image.
--- When `nil`, the path is resolved relative to the file.
---@field resolve? fun(file: string, src: string): string?
---@field convert? snacks.image.convert.Config
{
  formats = {
    "png",
    "jpg",
    "jpeg",
    "gif",
    "bmp",
    "webp",
    "tiff",
    "heic",
    "avif",
    "mp4",
    "mov",
    "avi",
    "mkv",
    "webm",
    "pdf",
  },
  force = false, -- try displaying the image, even if the terminal does not support it
  doc = {
    -- enable image viewer for documents
    -- a treesitter parser must be available for the enabled languages.
    enabled = true,
    -- render the image inline in the buffer
    -- if your env doesn't support unicode placeholders, this will be disabled
    -- takes precedence over `opts.float` on supported terminals
    inline = true,
    -- render the image in a floating window
    -- only used if `opts.inline` is disabled
    float = true,
    max_width = 80,
    max_height = 40,
    -- Set to `true`, to conceal the image text when rendering inline.
    conceal = false, -- (experimental)
  },
  img_dirs = { "img", "images", "assets", "static", "public", "media", "attachments" },
  -- window options applied to windows displaying image buffers
  -- an image buffer is a buffer with `filetype=image`
  wo = {
    wrap = false,
    number = false,
    relativenumber = false,
    cursorcolumn = false,
    signcolumn = "no",
    foldcolumn = "0",
    list = false,
    spell = false,
    statuscolumn = "",
  },
  cache = vim.fn.stdpath("cache") .. "/snacks/image",
  debug = {
    request = false,
    convert = false,
    placement = false,
  },
  env = {},
  ---@class snacks.image.convert.Config
  convert = {
    notify = true, -- show a notification on error
    ---@type snacks.image.args
    mermaid = function()
      local theme = vim.o.background == "light" and "neutral" or "dark"
      return { "-i", "{src}", "-o", "{file}", "-b", "transparent", "-t", theme, "-s", "{scale}" }
    end,
    ---@type table<string,snacks.image.args>
    magick = {
      default = { "{src}[0]", "-scale", "1920x1080>" }, -- default for raster images
      vector = { "-density", 192, "{src}[0]" }, -- used by vector images like svg
      math = { "-density", 192, "{src}[0]", "-trim" },
      pdf = { "-density", 192, "{src}[0]", "-background", "white", "-alpha", "remove", "-trim" },
    },
  },
  math = {
    enabled = true, -- enable math expression rendering
    -- in the templates below, `${header}` comes from any section in your document,
    -- between a start/end header comment. Comment syntax is language-specific.
    -- * start comment: `// snacks: header start`
    -- * end comment:   `// snacks: header end`
    typst = {
      tpl = [[
        #set page(width: auto, height: auto, margin: (x: 2pt, y: 2pt))
        #show math.equation.where(block: false): set text(top-edge: "bounds", bottom-edge: "bounds")
        #set text(size: 12pt, fill: rgb("${color}"))
        ${header}
        ${content}]],
    },
    latex = {
      font_size = "Large", -- see https://www.sascha-frank.com/latex-font-size.html
      -- for latex documents, the doc packages are included automatically,
      -- but you can add more packages here. Useful for markdown documents.
      packages = { "amsmath", "amssymb", "amsfonts", "amscd", "mathtools" },
      tpl = [[
        \documentclass[preview,border=2pt,varwidth,12pt]{standalone}
        \usepackage{${packages}}
        \begin{document}
        ${header}
        { \${font_size} \selectfont
          \color[HTML]{${color}}
        ${content}}
        \end{document}]],
    },
  },
}
```

## 🎨 Styles

Check the [styles](https://github.com/folke/snacks.nvim/blob/main/docs/styles.md)
docs for more information on how to customize these styles

### `snacks_image`

```lua
{
  relative = "cursor",
  border = "rounded",
  focusable = false,
  backdrop = false,
  row = 1,
  col = 1,
  -- width/height are automatically set by the image size unless specified below
}
```

## 📚 Types

```lua
---@alias snacks.image.Size {width: number, height: number}
---@alias snacks.image.Pos {[1]: number, [2]: number}
---@alias snacks.image.Loc snacks.image.Pos|snacks.image.Size|{zindex?: number}
```

```lua
---@class snacks.image.Env
---@field name string
---@field env table<string, string|true>
---@field supported? boolean default: false
---@field placeholders? boolean default: false
---@field setup? fun(): boolean?
---@field transform? fun(data: string): string
---@field detected? boolean
---@field remote? boolean this is a remote client, so full transfer of the image data is required
```

```lua
---@class snacks.image.Opts
---@field pos? snacks.image.Pos (row, col) (1,0)-indexed. defaults to the top-left corner
---@field range? Range4
---@field inline? boolean render the image inline in the buffer
---@field width? number
---@field min_width? number
---@field max_width? number
---@field height? number
---@field min_height? number
---@field max_height? number
---@field on_update? fun(placement: snacks.image.Placement)
---@field on_update_pre? fun(placement: snacks.image.Placement)
```

## 📦 Module

```lua
---@class snacks.image
---@field terminal snacks.image.terminal
---@field image snacks.Image
---@field placement snacks.image.Placement
---@field util snacks.image.util
---@field buf snacks.image.buf
---@field doc snacks.image.doc
---@field convert snacks.image.convert
Snacks.image = {}
```

### `Snacks.image.hover()`

Show the image at the cursor in a floating window

```lua
Snacks.image.hover()
```

### `Snacks.image.langs()`

```lua
---@return string[]
Snacks.image.langs()
```

### `Snacks.image.supports()`

Check if the file format is supported and the terminal supports the kitty graphics protocol

```lua
---@param file string
Snacks.image.supports(file)
```

### `Snacks.image.supports_file()`

Check if the file format is supported

```lua
---@param file string
Snacks.image.supports_file(file)
```

### `Snacks.image.supports_terminal()`

Check if the terminal supports the kitty graphics protocol

```lua
Snacks.image.supports_terminal()
```

docs/image.md


# 🍿 indent

Visualize indent guides and scopes based on treesitter or indent.

Similar plugins:

- [indent-blankline.nvim](https://github.com/lukas-reineke/indent-blankline.nvim)
- [mini.indentscope](https://github.com/echasnovski/mini.indentscope)

![image](https://github.com/user-attachments/assets/56a99495-05ab-488e-9619-574cb7ff2b7d)

<!-- docgen -->

## 📦 Setup

```lua
-- lazy.nvim
{
  "folke/snacks.nvim",
  ---@type snacks.Config
  opts = {
    indent = {
      -- your indent configuration comes here
      -- or leave it empty to use the default settings
      -- refer to the configuration section below
    }
  }
}
```

## ⚙ Config

```lua
---@class snacks.indent.Config
---@field enabled? boolean
{
  indent = {
    priority = 1,
    enabled = true, -- enable indent guides
    char = "│",
    only_scope = false, -- only show indent guides of the scope
    only_current = false, -- only show indent guides in the current window
    hl = "SnacksIndent", ---@type string|string[] hl groups for indent guides
    -- can be a list of hl groups to cycle through
    -- hl = {
    --     "SnacksIndent1",
    --     "SnacksIndent2",
    --     "SnacksIndent3",
    --     "SnacksIndent4",
    --     "SnacksIndent5",
    --     "SnacksIndent6",
    --     "SnacksIndent7",
    --     "SnacksIndent8",
    -- },
  },
  -- animate scopes. Enabled by default for Neovim >= 0.10
  -- Works on older versions but has to trigger redraws during animation.
  ---@class snacks.indent.animate: snacks.animate.Config
  ---@field enabled? boolean
  --- * out: animate outwards from the cursor
  --- * up: animate upwards from the cursor
  --- * down: animate downwards from the cursor
  --- * up_down: animate up or down based on the cursor position
  ---@field style? "out"|"up_down"|"down"|"up"
  animate = {
    enabled = vim.fn.has("nvim-0.10") == 1,
    style = "out",
    easing = "linear",
    duration = {
      step = 20, -- ms per step
      total = 500, -- maximum duration
    },
  },
  ---@class snacks.indent.Scope.Config: snacks.scope.Config
  scope = {
    enabled = true, -- enable highlighting the current scope
    priority = 200,
    char = "│",
    underline = false, -- underline the start of the scope
    only_current = false, -- only show scope in the current window
    hl = "SnacksIndentScope", ---@type string|string[] hl group for scopes
  },
  chunk = {
    -- when enabled, scopes will be rendered as chunks, except for the
    -- top-level scope which will be rendered as a scope.
    enabled = false,
    -- only show chunk scopes in the current window
    only_current = false,
    priority = 200,
    hl = "SnacksIndentChunk", ---@type string|string[] hl group for chunk scopes
    char = {
      corner_top = "┌",
      corner_bottom = "└",
      -- corner_top = "╭",
      -- corner_bottom = "╰",
      horizontal = "─",
      vertical = "│",
      arrow = ">",
    },
  },
  -- filter for buffers to enable indent guides
  filter = function(buf)
    return vim.g.snacks_indent ~= false and vim.b[buf].snacks_indent ~= false and vim.bo[buf].buftype == ""
  end,
}
```

## 📚 Types

```lua
---@class snacks.indent.Scope: snacks.scope.Scope
---@field win number
---@field step? number
---@field animate? {from: number, to: number}
```

## 📦 Module

### `Snacks.indent.disable()`

Disable indent guides

```lua
Snacks.indent.disable()
```

### `Snacks.indent.enable()`

Enable indent guides

```lua
Snacks.indent.enable()
```

docs/indent.md


# 🍿 init

<!-- docgen -->

## ⚙ Config

```lua
---@class snacks.Config
---@field animate? snacks.animate.Config
---@field bigfile? snacks.bigfile.Config
---@field dashboard? snacks.dashboard.Config
---@field dim? snacks.dim.Config
---@field explorer? snacks.explorer.Config
---@field gitbrowse? snacks.gitbrowse.Config
---@field image? snacks.image.Config
---@field indent? snacks.indent.Config
---@field input? snacks.input.Config
---@field layout? snacks.layout.Config
---@field lazygit? snacks.lazygit.Config
---@field notifier? snacks.notifier.Config
---@field picker? snacks.picker.Config
---@field profiler? snacks.profiler.Config
---@field quickfile? snacks.quickfile.Config
---@field scope? snacks.scope.Config
---@field scratch? snacks.scratch.Config
---@field scroll? snacks.scroll.Config
---@field statuscolumn? snacks.statuscolumn.Config
---@field terminal? snacks.terminal.Config
---@field toggle? snacks.toggle.Config
---@field win? snacks.win.Config
---@field words? snacks.words.Config
---@field zen? snacks.zen.Config
---@field styles? table<string, snacks.win.Config>
---@field image? snacks.image.Config|{}
{
  image = {
    -- define these here, so that we don't need to load the image module
    formats = {
      "png",
      "jpg",
      "jpeg",
      "gif",
      "bmp",
      "webp",
      "tiff",
      "heic",
      "avif",
      "mp4",
      "mov",
      "avi",
      "mkv",
      "webm",
      "pdf",
    },
  },
}
```

## 📚 Types

```lua
---@class snacks.Config.base
---@field example? string
---@field config? fun(opts: table, defaults: table)
```

## 📦 Module

```lua
---@class Snacks
---@field animate snacks.animate
---@field bigfile snacks.bigfile
---@field bufdelete snacks.bufdelete
---@field dashboard snacks.dashboard
---@field debug snacks.debug
---@field dim snacks.dim
---@field explorer snacks.explorer
---@field git snacks.git
---@field gitbrowse snacks.gitbrowse
---@field health snacks.health
---@field image snacks.image
---@field indent snacks.indent
---@field input snacks.input
---@field layout snacks.layout
---@field lazygit snacks.lazygit
---@field meta snacks.meta
---@field notifier snacks.notifier
---@field notify snacks.notify
---@field picker snacks.picker
---@field profiler snacks.profiler
---@field quickfile snacks.quickfile
---@field rename snacks.rename
---@field scope snacks.scope
---@field scratch snacks.scratch
---@field scroll snacks.scroll
---@field statuscolumn snacks.statuscolumn
---@field terminal snacks.terminal
---@field toggle snacks.toggle
---@field util snacks.util
---@field win snacks.win
---@field words snacks.words
---@field zen snacks.zen
Snacks = {}
```

### `Snacks.init.config.example()`

Get an example config from the docs/examples directory.

```lua
---@param snack string
---@param name string
---@param opts? table
Snacks.init.config.example(snack, name, opts)
```

### `Snacks.init.config.get()`

```lua
---@generic T: table
---@param snack string
---@param defaults T
---@param ... T[]
---@return T
Snacks.init.config.get(snack, defaults, ...)
```

### `Snacks.init.config.merge()`

Merges the values similar to vim.tbl_deep_extend with the **force** behavior,
but the values can be any type

```lua
---@generic T
---@param ... T
---@return T
Snacks.init.config.merge(...)
```

### `Snacks.init.config.style()`

Register a new window style config.

```lua
---@param name string
---@param defaults snacks.win.Config|{}
---@return string
Snacks.init.config.style(name, defaults)
```

### `Snacks.init.setup()`

```lua
---@param opts snacks.Config?
Snacks.init.setup(opts)
```

docs/init.md


# 🍿 input

Better `vim.ui.input`.

![image](https://github.com/user-attachments/assets/f7579302-bea1-4f1c-8b3b-723c3f4ca04b)

<!-- docgen -->

## 📦 Setup

```lua
-- lazy.nvim
{
  "folke/snacks.nvim",
  ---@type snacks.Config
  opts = {
    input = {
      -- your input configuration comes here
      -- or leave it empty to use the default settings
      -- refer to the configuration section below
    }
  }
}
```

## ⚙ Config

```lua
---@class snacks.input.Config
---@field enabled? boolean
---@field win? snacks.win.Config|{}
---@field icon? string
---@field icon_pos? snacks.input.Pos
---@field prompt_pos? snacks.input.Pos
{
  icon = " ",
  icon_hl = "SnacksInputIcon",
  icon_pos = "left",
  prompt_pos = "title",
  win = { style = "input" },
  expand = true,
}
```

## 🎨 Styles

Check the [styles](https://github.com/folke/snacks.nvim/blob/main/docs/styles.md)
docs for more information on how to customize these styles

### `input`

```lua
{
  backdrop = false,
  position = "float",
  border = "rounded",
  title_pos = "center",
  height = 1,
  width = 60,
  relative = "editor",
  noautocmd = true,
  row = 2,
  -- relative = "cursor",
  -- row = -3,
  -- col = 0,
  wo = {
    winhighlight = "NormalFloat:SnacksInputNormal,FloatBorder:SnacksInputBorder,FloatTitle:SnacksInputTitle",
    cursorline = false,
  },
  bo = {
    filetype = "snacks_input",
    buftype = "prompt",
  },
  --- buffer local variables
  b = {
    completion = false, -- disable blink completions in input
  },
  keys = {
    n_esc = { "<esc>", { "cmp_close", "cancel" }, mode = "n", expr = true },
    i_esc = { "<esc>", { "cmp_close", "stopinsert" }, mode = "i", expr = true },
    i_cr = { "<cr>", { "cmp_accept", "confirm" }, mode = "i", expr = true },
    i_tab = { "<tab>", { "cmp_select_next", "cmp" }, mode = "i", expr = true },
    i_ctrl_w = { "<c-w>", "<c-s-w>", mode = "i", expr = true },
    i_up = { "<up>", { "hist_up" }, mode = { "i", "n" } },
    i_down = { "<down>", { "hist_down" }, mode = { "i", "n" } },
    q = "cancel",
  },
}
```

## 📚 Types

```lua
---@alias snacks.input.Pos "left"|"title"|false
```

```lua
---@class snacks.input.Opts: snacks.input.Config,{}
---@field prompt? string
---@field default? string
---@field completion? string
---@field highlight? fun()
```

## 📦 Module

### `Snacks.input()`

```lua
---@type fun(opts: snacks.input.Opts, on_confirm: fun(value?: string)): snacks.win
Snacks.input()
```

### `Snacks.input.disable()`

```lua
Snacks.input.disable()
```

### `Snacks.input.enable()`

```lua
Snacks.input.enable()
```

### `Snacks.input.input()`

```lua
---@param opts? snacks.input.Opts
---@param on_confirm fun(value?: string)
Snacks.input.input(opts, on_confirm)
```

docs/input.md


# 🍿 layout

<!-- docgen -->

## 📦 Setup

```lua
-- lazy.nvim
{
  "folke/snacks.nvim",
  ---@type snacks.Config
  opts = {
    layout = {
      -- your layout configuration comes here
      -- or leave it empty to use the default settings
      -- refer to the configuration section below
    }
  }
}
```

## ⚙ Config

```lua
---@class snacks.layout.Config
---@field show? boolean show the layout on creation (default: true)
---@field wins table<string, snacks.win> windows to include in the layout
---@field layout snacks.layout.Box layout definition
---@field fullscreen? boolean open in fullscreen
---@field hidden? string[] list of windows that will be excluded from the layout (but can be toggled)
---@field on_update? fun(layout: snacks.layout)
---@field on_update_pre? fun(layout: snacks.layout)
{
  layout = {
    width = 0.6,
    height = 0.6,
    zindex = 50,
  },
}
```

## 📚 Types

```lua
---@class snacks.layout.Win: snacks.win.Config,{}
---@field depth? number
---@field win string layout window name
```

```lua
---@class snacks.layout.Box: snacks.layout.Win,{}
---@field box "horizontal" | "vertical"
---@field id? number
---@field [number] snacks.layout.Win | snacks.layout.Box children
```

```lua
---@alias snacks.layout.Widget snacks.layout.Win | snacks.layout.Box
```

## 📦 Module

```lua
---@class snacks.layout
---@field opts snacks.layout.Config
---@field root snacks.win
---@field wins table<string, snacks.win|{enabled?:boolean, layout?:boolean}>
---@field box_wins snacks.win[]
---@field win_opts table<string, snacks.win.Config>
---@field closed? boolean
---@field split? boolean
---@field screenpos number[]?
Snacks.layout = {}
```

### `Snacks.layout.new()`

```lua
---@param opts snacks.layout.Config
Snacks.layout.new(opts)
```

### `layout:close()`

Close the layout

```lua
---@param opts? {wins?: boolean}
layout:close(opts)
```

### `layout:each()`

```lua
---@param cb fun(widget: snacks.layout.Widget, parent?: snacks.layout.Box)
---@param opts? {wins?:boolean, boxes?:boolean, box?:snacks.layout.Box}
layout:each(cb, opts)
```

### `layout:hide()`

```lua
layout:hide()
```

### `layout:is_enabled()`

Check if the window has been used in the layout

```lua
---@param w string
layout:is_enabled(w)
```

### `layout:is_hidden()`

Check if a window is hidden

```lua
---@param win string
layout:is_hidden(win)
```

### `layout:maximize()`

Toggle fullscreen

```lua
layout:maximize()
```

### `layout:needs_layout()`

```lua
---@param win string
layout:needs_layout(win)
```

### `layout:show()`

Show the layout

```lua
layout:show()
```

### `layout:toggle()`

Toggle a window

```lua
---@param win string
---@param enable? boolean
---@param on_update? fun(enabled: boolean) called when the layout will be updated
layout:toggle(win, enable, on_update)
```

### `layout:unhide()`

```lua
layout:unhide()
```

### `layout:valid()`

Check if layout is valid (visible)

```lua
layout:valid()
```

docs/layout.md


# 🍿 lazygit

Automatically configures lazygit with a theme generated based on your Neovim colorscheme
and integrate edit with the current neovim instance.

![image](https://github.com/user-attachments/assets/5e5ca232-af65-4ebc-b0ca-02bc9c33d23d)

<!-- docgen -->

## 📦 Setup

```lua
-- lazy.nvim
{
  "folke/snacks.nvim",
  ---@type snacks.Config
  opts = {
    lazygit = {
      -- your lazygit configuration comes here
      -- or leave it empty to use the default settings
      -- refer to the configuration section below
    }
  }
}
```

## ⚙ Config

```lua
---@class snacks.lazygit.Config: snacks.terminal.Opts
---@field args? string[]
---@field theme? snacks.lazygit.Theme
{
  -- automatically configure lazygit to use the current colorscheme
  -- and integrate edit with the current neovim instance
  configure = true,
  -- extra configuration for lazygit that will be merged with the default
  -- snacks does NOT have a full yaml parser, so if you need `"test"` to appear with the quotes
  -- you need to double quote it: `"\"test\""`
  config = {
    os = { editPreset = "nvim-remote" },
    gui = {
      -- set to an empty string "" to disable icons
      nerdFontsVersion = "3",
    },
  },
  theme_path = svim.fs.normalize(vim.fn.stdpath("cache") .. "/lazygit-theme.yml"),
  -- Theme for lazygit
  theme = {
    [241]                      = { fg = "Special" },
    activeBorderColor          = { fg = "MatchParen", bold = true },
    cherryPickedCommitBgColor  = { fg = "Identifier" },
    cherryPickedCommitFgColor  = { fg = "Function" },
    defaultFgColor             = { fg = "Normal" },
    inactiveBorderColor        = { fg = "FloatBorder" },
    optionsTextColor           = { fg = "Function" },
    searchingActiveBorderColor = { fg = "MatchParen", bold = true },
    selectedLineBgColor        = { bg = "Visual" }, -- set to `default` to have no background colour
    unstagedChangesColor       = { fg = "DiagnosticError" },
  },
  win = {
    style = "lazygit",
  },
}
```

## 🎨 Styles

Check the [styles](https://github.com/folke/snacks.nvim/blob/main/docs/styles.md)
docs for more information on how to customize these styles

### `lazygit`

```lua
{}
```

## 📚 Types

```lua
---@alias snacks.lazygit.Color {fg?:string, bg?:string, bold?:boolean}
```

```lua
---@class snacks.lazygit.Theme: table<number, snacks.lazygit.Color>
---@field activeBorderColor snacks.lazygit.Color
---@field cherryPickedCommitBgColor snacks.lazygit.Color
---@field cherryPickedCommitFgColor snacks.lazygit.Color
---@field defaultFgColor snacks.lazygit.Color
---@field inactiveBorderColor snacks.lazygit.Color
---@field optionsTextColor snacks.lazygit.Color
---@field searchingActiveBorderColor snacks.lazygit.Color
---@field selectedLineBgColor snacks.lazygit.Color
---@field unstagedChangesColor snacks.lazygit.Color
```

## 📦 Module

### `Snacks.lazygit()`

```lua
---@type fun(opts?: snacks.lazygit.Config): snacks.win
Snacks.lazygit()
```

### `Snacks.lazygit.log()`

Opens lazygit with the log view

```lua
---@param opts? snacks.lazygit.Config
Snacks.lazygit.log(opts)
```

### `Snacks.lazygit.log_file()`

Opens lazygit with the log of the current file

```lua
---@param opts? snacks.lazygit.Config
Snacks.lazygit.log_file(opts)
```

### `Snacks.lazygit.open()`

Opens lazygit, properly configured to use the current colorscheme
and integrate with the current neovim instance

```lua
---@param opts? snacks.lazygit.Config
Snacks.lazygit.open(opts)
```

docs/lazygit.md


# 🍿 meta

Meta functions for Snacks

<!-- docgen -->

## 📚 Types

```lua
---@class snacks.meta.Meta
---@field desc string
---@field needs_setup? boolean
---@field hide? boolean
---@field readme? boolean
---@field docs? boolean
---@field health? boolean
---@field types? boolean
---@field config? boolean
---@field merge? { [string|number]: string }
```

```lua
---@class snacks.meta.Plugin
---@field name string
---@field file string
---@field meta snacks.meta.Meta
---@field health? fun()
```

## 📦 Module

### `Snacks.meta.file()`

```lua
Snacks.meta.file(name)
```

### `Snacks.meta.get()`

Get the metadata for all snacks plugins

```lua
---@return snacks.meta.Plugin[]
Snacks.meta.get()
```

docs/meta.md


# 🍿 notifier

![image](https://github.com/user-attachments/assets/b89eb279-08fb-40b2-9330-9a77014b9389)

## Notification History

![image](https://github.com/user-attachments/assets/0dc449f4-b275-49e4-a25f-f58efcba3079)

## 💡 Examples

Replace a notification

```lua
-- to replace an existing notification just use the same id.
-- you can also use the return value of the notify function as id.
for i = 1, 10 do
  vim.defer_fn(function()
    vim.notify("Hello " .. i, "info", { id = "test" })
  end, i * 500)
end
```

Simple LSP Progress

```lua
vim.api.nvim_create_autocmd("LspProgress", {
  ---@param ev {data: {client_id: integer, params: lsp.ProgressParams}}
  callback = function(ev)
    local spinner = { "⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏" }
    vim.notify(vim.lsp.status(), "info", {
      id = "lsp_progress",
      title = "LSP Progress",
      opts = function(notif)
        notif.icon = ev.data.params.value.kind == "end" and " "
          or spinner[math.floor(vim.uv.hrtime() / (1e6 * 80)) % #spinner + 1]
      end,
    })
  end,
})
```

Advanced LSP Progress

![image](https://github.com/user-attachments/assets/a81b411c-150a-43ec-8def-87270c6f8dde)

```lua
---@type table<number, {token:lsp.ProgressToken, msg:string, done:boolean}[]>
local progress = vim.defaulttable()
vim.api.nvim_create_autocmd("LspProgress", {
  ---@param ev {data: {client_id: integer, params: lsp.ProgressParams}}
  callback = function(ev)
    local client = vim.lsp.get_client_by_id(ev.data.client_id)
    local value = ev.data.params.value --[[@as {percentage?: number, title?: string, message?: string, kind: "begin" | "report" | "end"}]]
    if not client or type(value) ~= "table" then
      return
    end
    local p = progress[client.id]

    for i = 1, #p + 1 do
      if i == #p + 1 or p[i].token == ev.data.params.token then
        p[i] = {
          token = ev.data.params.token,
          msg = ("[%3d%%] %s%s"):format(
            value.kind == "end" and 100 or value.percentage or 100,
            value.title or "",
            value.message and (" **%s**"):format(value.message) or ""
          ),
          done = value.kind == "end",
        }
        break
      end
    end

    local msg = {} ---@type string[]
    progress[client.id] = vim.tbl_filter(function(v)
      return table.insert(msg, v.msg) or not v.done
    end, p)

    local spinner = { "⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏" }
    vim.notify(table.concat(msg, "\n"), "info", {
      id = "lsp_progress",
      title = client.name,
      opts = function(notif)
        notif.icon = #progress[client.id] == 0 and " "
          or spinner[math.floor(vim.uv.hrtime() / (1e6 * 80)) % #spinner + 1]
      end,
    })
  end,
})
```

## 📦 Setup

```lua
-- lazy.nvim
{
  "folke/snacks.nvim",
  ---@type snacks.Config
  opts = {
    notifier = {
      -- your notifier configuration comes here
      -- or leave it empty to use the default settings
      -- refer to the configuration section below
    }
  }
}
```

## ⚙ Config

```lua
---@class snacks.notifier.Config
---@field enabled? boolean
---@field keep? fun(notif: snacks.notifier.Notif): boolean # global keep function
---@field filter? fun(notif: snacks.notifier.Notif): boolean # filter our unwanted notifications (return false to hide)
{
  timeout = 3000, -- default timeout in ms
  width = { min = 40, max = 0.4 },
  height = { min = 1, max = 0.6 },
  -- editor margin to keep free. tabline and statusline are taken into account automatically
  margin = { top = 0, right = 1, bottom = 0 },
  padding = true, -- add 1 cell of left/right padding to the notification window
  sort = { "level", "added" }, -- sort by level and time
  -- minimum log level to display. TRACE is the lowest
  -- all notifications are stored in history
  level = vim.log.levels.TRACE,
  icons = {
    error = " ",
    warn = " ",
    info = " ",
    debug = " ",
    trace = " ",
  },
  keep = function(notif)
    return vim.fn.getcmdpos() > 0
  end,
  ---@type snacks.notifier.style
  style = "compact",
  top_down = true, -- place notifications from top to bottom
  date_format = "%R", -- time format for notifications
  -- format for footer when more lines are available
  -- `%d` is replaced with the number of lines.
  -- only works for styles with a border
  ---@type string|boolean
  more_format = " ↓ %d lines ",
  refresh = 50, -- refresh at most every 50ms
}
```

## 🎨 Styles

Check the [styles](https://github.com/folke/snacks.nvim/blob/main/docs/styles.md)
docs for more information on how to customize these styles

### `notification`

```lua
{
  border = "rounded",
  zindex = 100,
  ft = "markdown",
  wo = {
    winblend = 5,
    wrap = false,
    conceallevel = 2,
    colorcolumn = "",
  },
  bo = { filetype = "snacks_notif" },
}
```

### `notification_history`

```lua
{
  border = "rounded",
  zindex = 100,
  width = 0.6,
  height = 0.6,
  minimal = false,
  title = " Notification History ",
  title_pos = "center",
  ft = "markdown",
  bo = { filetype = "snacks_notif_history", modifiable = false },
  wo = { winhighlight = "Normal:SnacksNotifierHistory" },
  keys = { q = "close" },
}
```

## 📚 Types

Render styles:
* compact: use border for icon and title
* minimal: no border, only icon and message
* fancy: similar to the default nvim-notify style

```lua
---@alias snacks.notifier.style snacks.notifier.render|"compact"|"fancy"|"minimal"
```

### Notifications

Notification options

```lua
---@class snacks.notifier.Notif.opts
---@field id? number|string
---@field msg? string
---@field level? number|snacks.notifier.level
---@field title? string
---@field icon? string
---@field timeout? number|boolean timeout in ms. Set to 0|false to keep until manually closed
---@field ft? string
---@field keep? fun(notif: snacks.notifier.Notif): boolean
---@field style? snacks.notifier.style
---@field opts? fun(notif: snacks.notifier.Notif) -- dynamic opts
---@field hl? snacks.notifier.hl -- highlight overrides
---@field history? boolean
```

Notification object

```lua
---@class snacks.notifier.Notif: snacks.notifier.Notif.opts
---@field id number|string
---@field msg string
---@field win? snacks.win
---@field icon string
---@field level snacks.notifier.level
---@field timeout number
---@field dirty? boolean
---@field added number timestamp with nano precision
---@field updated number timestamp with nano precision
---@field shown? number timestamp with nano precision
---@field hidden? number timestamp with nano precision
---@field layout? { top?: number, width: number, height: number }
```

### Rendering

```lua
---@alias snacks.notifier.render fun(buf: number, notif: snacks.notifier.Notif, ctx: snacks.notifier.ctx)
```

```lua
---@class snacks.notifier.hl
---@field title string
---@field icon string
---@field border string
---@field footer string
---@field msg string
```

```lua
---@class snacks.notifier.ctx
---@field opts snacks.win.Config
---@field notifier snacks.notifier.Class
---@field hl snacks.notifier.hl
---@field ns number
```

### History

```lua
---@class snacks.notifier.history
---@field filter? snacks.notifier.level|fun(notif: snacks.notifier.Notif): boolean
---@field sort? string[] # sort fields, default: {"added"}
---@field reverse? boolean
```

```lua
---@alias snacks.notifier.level "trace"|"debug"|"info"|"warn"|"error"
```

## 📦 Module

### `Snacks.notifier()`

```lua
---@type fun(msg: string, level?: snacks.notifier.level|number, opts?: snacks.notifier.Notif.opts): number|string
Snacks.notifier()
```

### `Snacks.notifier.get_history()`

```lua
---@param opts? snacks.notifier.history
Snacks.notifier.get_history(opts)
```

### `Snacks.notifier.hide()`

```lua
---@param id? number|string
Snacks.notifier.hide(id)
```

### `Snacks.notifier.notify()`

```lua
---@param msg string
---@param level? snacks.notifier.level|number
---@param opts? snacks.notifier.Notif.opts
Snacks.notifier.notify(msg, level, opts)
```

### `Snacks.notifier.show_history()`

```lua
---@param opts? snacks.notifier.history
Snacks.notifier.show_history(opts)
```

docs/notifier.md


# 🍿 notify

<!-- docgen -->

## 📚 Types

```lua
---@alias snacks.notify.Opts snacks.notifier.Notif.opts|{once?: boolean}
```

## 📦 Module

### `Snacks.notify()`

```lua
---@type fun(msg: string|string[], opts?: snacks.notify.Opts)
Snacks.notify()
```

### `Snacks.notify.error()`

```lua
---@param msg string|string[]
---@param opts? snacks.notify.Opts
Snacks.notify.error(msg, opts)
```

### `Snacks.notify.info()`

```lua
---@param msg string|string[]
---@param opts? snacks.notify.Opts
Snacks.notify.info(msg, opts)
```

### `Snacks.notify.notify()`

```lua
---@param msg string|string[]
---@param opts? snacks.notify.Opts
Snacks.notify.notify(msg, opts)
```

### `Snacks.notify.warn()`

```lua
---@param msg string|string[]
---@param opts? snacks.notify.Opts
Snacks.notify.warn(msg, opts)
```

docs/notify.md