*snacks-profiler*                           snacks profiler docs

==============================================================================
Table of Contents                     *snacks.nvim-profiler-table-of-contents*

1. Features                                    |snacks.nvim-profiler-features|
2. Why?                                            |snacks.nvim-profiler-why?|
3. Usage                                          |snacks.nvim-profiler-usage|
  - Caveats                               |snacks.nvim-profiler-usage-caveats|
  - Recommended Setup           |snacks.nvim-profiler-usage-recommended-setup|
  - Profiling Neovim Startup|snacks.nvim-profiler-usage-profiling-neovim-startup|
  - Filtering                           |snacks.nvim-profiler-usage-filtering|
4. Setup                                          |snacks.nvim-profiler-setup|
5. Config                                        |snacks.nvim-profiler-config|
6. Types                                          |snacks.nvim-profiler-types|
  - Traces                                 |snacks.nvim-profiler-types-traces|
  - Pick: grouping, filtering and sorting|snacks.nvim-profiler-types-pick:-grouping,-filtering-and-sorting|
  - UI                                         |snacks.nvim-profiler-types-ui|
  - Other                                   |snacks.nvim-profiler-types-other|
7. Module                                        |snacks.nvim-profiler-module|
  - Snacks.profiler.find()|snacks.nvim-profiler-module-snacks.profiler.find()|
  - Snacks.profiler.highlight()|snacks.nvim-profiler-module-snacks.profiler.highlight()|
  - Snacks.profiler.pick()|snacks.nvim-profiler-module-snacks.profiler.pick()|
  - Snacks.profiler.running()|snacks.nvim-profiler-module-snacks.profiler.running()|
  - Snacks.profiler.scratch()|snacks.nvim-profiler-module-snacks.profiler.scratch()|
  - Snacks.profiler.start()|snacks.nvim-profiler-module-snacks.profiler.start()|
  - Snacks.profiler.startup()|snacks.nvim-profiler-module-snacks.profiler.startup()|
  - Snacks.profiler.status()|snacks.nvim-profiler-module-snacks.profiler.status()|
  - Snacks.profiler.stop()|snacks.nvim-profiler-module-snacks.profiler.stop()|
  - Snacks.profiler.toggle()|snacks.nvim-profiler-module-snacks.profiler.toggle()|
8. Links                                          |snacks.nvim-profiler-links|
A low overhead Lua profiler for Neovim.


==============================================================================
1. Features                                    *snacks.nvim-profiler-features*

- low overhead **instrumentation**
- captures a function’s **def**inition and **ref**erence (_caller_) locations
- profiling of **autocmds**
- profiling of **require**d modules
- buffer **highlighting** of functions and calls
- lots of different ways to **filter** and **group** traces
- show traces with:
    - fzf-lua <https://github.com/ibhagwan/fzf-lua>
    - telescope.nvim <https://github.com/nvim-telescope/telescope.nvim>
    - trouble.nvim <https://github.com/folke/trouble.nvim>


==============================================================================
2. Why?                                            *snacks.nvim-profiler-why?*

Before the snacks profiler, I used to use a combination of my own profiler(s),
**lazy.nvim**’s internal profiler, profile.nvim
<https://github.com/stevearc/profile.nvim> and perfanno.nvim
<https://github.com/t-troebst/perfanno.nvim>.

They all have their strengths and weaknesses:

- **lazy.nvim**’s profiler is great for structured traces, but needed a lot of
    manual work to get the traces I wanted.
- **profile.nvim** does proper instrumentation, but was lacking in the UI department.
- **perfanno.nvim** has a great UI, but uses `jit.profile` which is not as
    detailed as instrumentation.

The snacks profiler tries to combine the best of all worlds.


==============================================================================
3. Usage                                          *snacks.nvim-profiler-usage*

The easiest way to use the profiler is to toggle it with the suggested
keybindings.

When the profiler stops, it will show a picker using the `on_stop` preset.

To quickly change picker options, you can use the `Snacks.profiler.scratch()`
scratch buffer.


CAVEATS                                   *snacks.nvim-profiler-usage-caveats*

- your Neovim session might slow down when profiling
- due to the overhead of instrumentation, fast functions that are called
    often, might skew the results. Best to add those to the `opts.filter_fn` config.
- by default, only captures functions defined on lua modules.
    If you want to profile others, add them to `opts.globals`
- the profiler is not perfect and might not capture all calls
- the profiler might not work well with some plugins
- it can only profile `autocmds` created when the profiler is running.
- only `autocmds` with a lua function callback can be profiled
- functions that `resume` or `yield` won’t be captured correctly
- functions that do blocking calls like `vim.fn.getchar` will work,
    but the time will include the time spent waiting for the blocking call


RECOMMENDED SETUP               *snacks.nvim-profiler-usage-recommended-setup*

>lua
    {
      {
        "folke/snacks.nvim",
        opts = function()
          -- Toggle the profiler
          Snacks.toggle.profiler():map("<leader>pp")
          -- Toggle the profiler highlights
          Snacks.toggle.profiler_highlights():map("<leader>ph")
        end,
        keys = {
          { "<leader>ps", function() Snacks.profiler.scratch() end, desc = "Profiler Scratch Bufer" },
        }
      },
      -- optional lualine component to show captured events
      -- when the profiler is running
      {
        "nvim-lualine/lualine.nvim",
        opts = function(_, opts)
          table.insert(opts.sections.lualine_x, Snacks.profiler.status())
        end,
      },
    }
<


PROFILING NEOVIM STARTUP *snacks.nvim-profiler-usage-profiling-neovim-startup*

In order to profile Neovim’s startup, you need to make sure `snacks.nvim` is
installed and loaded **before** doing anything else. So also before loading
your plugin manager.

You can add something like the below to the top of your `init.lua`.

Then you can profile your Neovim session, with `PROF=1 nvim`.

>lua
    if vim.env.PROF then
      -- example for lazy.nvim
      -- change this to the correct path for your plugin manager
      local snacks = vim.fn.stdpath("data") .. "/lazy/snacks.nvim"
      vim.opt.rtp:append(snacks)
      require("snacks.profiler").startup({
        startup = {
          event = "VimEnter", -- stop profiler on this event. Defaults to `VimEnter`
          -- event = "UIEnter",
          -- event = "VeryLazy",
        },
      })
    end
<


FILTERING                               *snacks.nvim-profiler-usage-filtering*

For the full definition, see the `snacks.profiler.Filter` type.

Each field can be a string or a boolean.

When a field is a string, it will match the exact value, unless it starts with
`^` in which case it will match the pattern.

When any of the `def`/`ref` fields are `true`, the filter matches the current
location of the cursor.

For example, `{ref_file = true}` will match all traces calling something, in
the current file.

All other fields equal to `true` will match if the trace has a value for that
field.


==============================================================================
4. Setup                                          *snacks.nvim-profiler-setup*

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


==============================================================================
5. Config                                        *snacks.nvim-profiler-config*

>lua
    ---@class snacks.profiler.Config
    {
      autocmds = true,
      runtime = vim.env.VIMRUNTIME, ---@type string
      -- thresholds for buttons to be shown as info, warn or error
      -- value is a tuple of [warn, error]
      thresholds = {
        time = { 2, 10 },
        pct = { 10, 20 },
        count = { 10, 100 },
      },
      on_stop = {
        highlights = true, -- highlight entries after stopping the profiler
        pick = true, -- show a picker after stopping the profiler (uses the `on_stop` preset)
      },
      ---@type snacks.profiler.Highlights
      highlights = {
        min_time = 0, -- only highlight entries with time > min_time (in ms)
        max_shade = 20, -- time in ms for the darkest shade
        badges = { "time", "pct", "count", "trace" },
        align = 80,
      },
      pick = {
        picker = "snacks", ---@type snacks.profiler.Picker
        ---@type snacks.profiler.Badge.type[]
        badges = { "time", "count", "name" },
        ---@type snacks.profiler.Highlights
        preview = {
          badges = { "time", "pct", "count" },
          align = "right",
        },
      },
      startup = {
        event = "VimEnter", -- stop profiler on this event. Defaults to `VimEnter`
        after = true, -- stop the profiler **after** the event. When false it stops **at** the event
        pattern = nil, -- pattern to match for the autocmd
        pick = true, -- show a picker after starting the profiler (uses the `startup` preset)
      },
      ---@type table<string, snacks.profiler.Pick|fun():snacks.profiler.Pick?>
      presets = {
        startup = { min_time = 1, sort = false },
        on_stop = {},
        filter_by_plugin = function()
          return { filter = { def_plugin = vim.fn.input("Filter by plugin: ") } }
        end,
      },
      ---@type string[]
      globals = {
        -- "vim",
        -- "vim.api",
        -- "vim.keymap",
        -- "Snacks.dashboard.Dashboard",
      },
      -- filter modules by pattern.
      -- longest patterns are matched first
      filter_mod = {
        default = true, -- default value for unmatched patterns
        ["^vim%."] = false,
        ["mason-core.functional"] = false,
        ["mason-core.functional.data"] = false,
        ["mason-core.optional"] = false,
        ["which-key.state"] = false,
      },
      filter_fn = {
        default = true,
        ["^.*%._[^%.]*$"] = false,
        ["trouble.filter.is"] = false,
        ["trouble.item.__index"] = false,
        ["which-key.node.__index"] = false,
        ["smear_cursor.draw.wo"] = false,
        ["^ibl%.utils%."] = false,
      },
      icons = {
        time    = " ",
        pct     = " ",
        count   = " ",
        require = "󰋺 ",
        modname = "󰆼 ",
        plugin  = " ",
        autocmd = "⚡",
        file    = " ",
        fn      = "󰊕 ",
        status  = "󰈸 ",
      },
    }
<


==============================================================================
6. Types                                          *snacks.nvim-profiler-types*


TRACES                                     *snacks.nvim-profiler-types-traces*

>lua
    ---@class snacks.profiler.Trace
    ---@field name string fully qualified name of the function
    ---@field time number time in nanoseconds
    ---@field depth number stack depth
    ---@field [number] snacks.profiler.Trace child traces
    ---@field fname string function name
    ---@field fn function function reference
    ---@field modname? string module name
    ---@field require? string special case for require
    ---@field autocmd? string special case for autocmd
    ---@field count? number number of calls
    ---@field def? snacks.profiler.Loc location of the definition
    ---@field ref? snacks.profiler.Loc location of the reference (caller)
    ---@field loc? snacks.profiler.Loc normalized location
<

>lua
    ---@class snacks.profiler.Loc
    ---@field file string path to the file
    ---@field line number line number
    ---@field loc? string normalized location
    ---@field modname? string module name
    ---@field plugin? string plugin name
<


PICK: GROUPING, FILTERING AND SORTING*snacks.nvim-profiler-types-pick:-grouping,-filtering-and-sorting*

>lua
    ---@class snacks.profiler.Find
    ---@field structure? boolean show traces as a tree or flat list
    ---@field sort? "time"|"count"|false sort by time or count, or keep original order
    ---@field loc? "def"|"ref" what location to show in the preview
    ---@field group? boolean|snacks.profiler.Field group traces by field
    ---@field filter? snacks.profiler.Filter filter traces by field(s)
    ---@field min_time? number only show grouped traces with `time >= min_time`
<

>lua
    ---@class snacks.profiler.Pick: snacks.profiler.Find
    ---@field picker? snacks.profiler.Picker
<

>lua
    ---@alias snacks.profiler.Picker "snacks"|"trouble"
    ---@alias snacks.profiler.Pick.spec snacks.profiler.Pick|{preset?:string}|fun():snacks.profiler.Pick
<

>lua
    ---@alias snacks.profiler.Field
    ---| "name" fully qualified name of the function
    ---| "def" definition
    ---| "ref" reference (caller)
    ---| "require" require
    ---| "autocmd" autocmd
    ---| "modname" module name of the called function
    ---| "def_file" file of the definition
    ---| "def_modname" module name of the definition
    ---| "def_plugin" plugin that defines the function
    ---| "ref_file" file of the reference
    ---| "ref_modname" module name of the reference
    ---| "ref_plugin" plugin that references the function
<

>lua
    ---@class snacks.profiler.Filter
    ---@field name? string|boolean fully qualified name of the function
    ---@field def? string|boolean location of the definition
    ---@field ref? string|boolean location of the reference (caller)
    ---@field require? string|boolean special case for require
    ---@field autocmd? string|boolean special case for autocmd
    ---@field modname? string|boolean module name
    ---@field def_file? string|boolean file of the definition
    ---@field def_modname? string|boolean module name of the definition
    ---@field def_plugin? string|boolean plugin that defines the function
    ---@field ref_file? string|boolean file of the reference
    ---@field ref_modname? string|boolean module name of the reference
    ---@field ref_plugin? string|boolean plugin that references the function
<


UI                                             *snacks.nvim-profiler-types-ui*

>lua
    ---@alias snacks.profiler.Badge {icon:string, text:string, padding?:boolean, level?:string}
    ---@alias snacks.profiler.Badge.type "time"|"pct"|"count"|"name"|"trace"
<

>lua
    ---@class snacks.profiler.Highlights
    ---@field min_time? number only highlight entries with time >= min_time
    ---@field max_shade? number -- time in ms for the darkest shade
    ---@field badges? snacks.profiler.Badge.type[] badges to show
    ---@field align? "right"|"left"|number align the badges right, left or at a specific column
<


OTHER                                       *snacks.nvim-profiler-types-other*

>lua
    ---@class snacks.profiler.Startup
    ---@field event? string
    ---@field pattern? string|string[] pattern to match for the autocmd
<

>lua
    ---@alias snacks.profiler.GroupFn fun(entry:snacks.profiler.Trace):{key:string, name?:string}?
<


==============================================================================
7. Module                                        *snacks.nvim-profiler-module*

>lua
    ---@class snacks.profiler
    ---@field core snacks.profiler.core
    ---@field loc snacks.profiler.loc
    ---@field tracer snacks.profiler.tracer
    ---@field ui snacks.profiler.ui
    ---@field picker snacks.profiler.picker
    Snacks.profiler = {}
<


`Snacks.profiler.find()`                              *Snacks.profiler.find()*

Group and filter traces

>lua
    ---@param opts snacks.profiler.Find
    Snacks.profiler.find(opts)
<


`Snacks.profiler.highlight()`                          *Snacks.profiler.highlight()*

Toggle the profiler highlights

>lua
    ---@param enable? boolean
    Snacks.profiler.highlight(enable)
<


`Snacks.profiler.pick()`                              *Snacks.profiler.pick()*

Group and filter traces and open a picker

>lua
    ---@param opts? snacks.profiler.Pick.spec
    Snacks.profiler.pick(opts)
<


`Snacks.profiler.running()`                          *Snacks.profiler.running()*

Check if the profiler is running

>lua
    Snacks.profiler.running()
<


`Snacks.profiler.scratch()`                          *Snacks.profiler.scratch()*

Open a scratch buffer with the profiler picker options

>lua
    Snacks.profiler.scratch()
<


`Snacks.profiler.start()`                            *Snacks.profiler.start()*

Start the profiler

>lua
    ---@param opts? snacks.profiler.Config
    Snacks.profiler.start(opts)
<


`Snacks.profiler.startup()`                          *Snacks.profiler.startup()*

Start the profiler on startup, and stop it after the event has been triggered.

>lua
    ---@param opts snacks.profiler.Config
    Snacks.profiler.startup(opts)
<


`Snacks.profiler.status()`                          *Snacks.profiler.status()*

Statusline component

>lua
    Snacks.profiler.status()
<


`Snacks.profiler.stop()`                              *Snacks.profiler.stop()*

Stop the profiler

>lua
    ---@param opts? {highlights?:boolean, pick?:snacks.profiler.Pick.spec}
    Snacks.profiler.stop(opts)
<


`Snacks.profiler.toggle()`                          *Snacks.profiler.toggle()*

Toggle the profiler

>lua
    Snacks.profiler.toggle()
<

==============================================================================
8. Links                                          *snacks.nvim-profiler-links*

1. *image*: https://github.com/user-attachments/assets/cebb1308-077b-4f20-bee3-28644fb121b8
2. *image*: https://github.com/user-attachments/assets/4ee557c4-a290-4a52-b5c9-64e325bf1094
3. *image*: https://github.com/user-attachments/assets/ec03e440-6719-4463-a649-a8626dcfe2ec

Generated by panvimdoc <https://github.com/kdheepak/panvimdoc>

vim:tw=78:ts=8:noet:ft=help:norl:
