[
  {
    "path": ".cargo/config.toml",
    "content": "[target.x86_64-apple-darwin]\nrustflags = [\n  \"-C\", \"link-arg=-undefined\",\n  \"-C\", \"link-arg=dynamic_lookup\",\n]\n\n[target.aarch64-apple-darwin]\nrustflags = [\n  \"-C\", \"link-arg=-undefined\",\n  \"-C\", \"link-arg=dynamic_lookup\",\n]\n"
  },
  {
    "path": ".gitignore",
    "content": ".archive.lua\ndual/\nresult\n.direnv\n.devenv\n\n/target\n\n"
  },
  {
    "path": ".stylua.toml",
    "content": "column_width = 120\nline_endings = \"Unix\"\nindent_type = \"Spaces\"\nindent_width = 2\nquote_style = \"AutoPreferSingle\"\ncall_parentheses = \"Always\"\ncollapse_simple_statement = \"Always\"\n"
  },
  {
    "path": "Cargo.toml",
    "content": "[package]\nname = \"blink_delimiters\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nanyhow = \"1.0.95\"\nnvim-oxi = { version = \"0.5.1\", features = [\"neovim-0-10\"] }\nportable-pty = \"0.9.0\"\nserde = \"1.0.216\"\nlazy_static = \"1.5.0\"\nlogos = \"0.15.0\"\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2024 Liam Dyer\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "<div align=\"center\">\n\n# blink.nvim\n\nExperimental library of neovim plugins with a focus on performance and simplicity\n\n</div>\n\n## Modules\n\n| status | module                                                  | description                                                                                                                                                     |\n|--------|---------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| stable | [blink.chartoggle](/readmes/chartoggle/README.md)       | Toggles a character at the end of the current line                                                                                                              |\n| stable | [blink.cmp](https://github.com/saghen/blink.cmp)        | Performant autocompletion plugin, inspired by [nvim-cmp](https://github.com/hrsh7th/nvim-cmp)                                                                   |\n| alpha  | [blink.pairs](https://github.com/saghen/blink.pairs)    | Rainbow highlighting and intelligent auto-pairs                                                                                         \t\t\t     |\n| stable | [blink.indent](https://github.com/saghen/blink.indent)  | Indent guides with scope on every keystroke                                                                                                                     |\n| WIP    | [blink.select](/readmes/select/README.md)               | Generic selection UI with built-in providers                                                                                                                    |\n| alpha  | [blink.tree](/readmes/tree/README.md)                   | Tree plugin with async io and FFI git, similar to [neo-tree](https://github.com/nvim-neo-tree/neo-tree.nvim) but eventually to be rewritten to be like oil.nvim |\n\n## Installation\n\n`lazy.nvim`\n\n```lua\n{\n  'saghen/blink.nvim',\n  build = 'cargo build --release', -- for delimiters\n  keys = {\n\t-- chartoggle\n\t{\n\t  '<C-;>',\n\t  function()\n\t  \trequire('blink.chartoggle').toggle_char_eol(';')\n\t  end,\n\t  mode = { 'n', 'v' },\n\t  desc = 'Toggle ; at eol',\n\t},\n\t{\n\t  ',',\n\t  function()\n\t  \trequire('blink.chartoggle').toggle_char_eol(',')\n\t  end,\n\t  mode = { 'n', 'v' },\n\t  desc = 'Toggle , at eol',\n\t},\n\n\t-- tree\n\t{ '<C-e>', '<cmd>BlinkTree reveal<cr>', desc = 'Reveal current file in tree' },\n\t{ '<leader>E', '<cmd>BlinkTree toggle<cr>', desc = 'Reveal current file in tree' },\n\t{ '<leader>e', '<cmd>BlinkTree toggle-focus<cr>', desc = 'Toggle file tree focus' },\n  },\n  -- all modules handle lazy loading internally\n  lazy = false,\n  opts = {\n    chartoggle = { enabled = true },\n    tree = { enabled = true }\n  }\n}\n"
  },
  {
    "path": "flake.nix",
    "content": "{\n  description = \"Set of simple, performant neovim plugins\";\n\n  inputs = {\n    nixpkgs.url = \"github:nixos/nixpkgs/nixpkgs-unstable\";\n    flake-parts.url = \"github:hercules-ci/flake-parts\";\n    fenix.url = \"github:nix-community/fenix\";\n    fenix.inputs.nixpkgs.follows = \"nixpkgs\";\n  };\n\n  outputs = inputs@{ flake-parts, nixpkgs, fenix, ... }:\n    flake-parts.lib.mkFlake { inherit inputs; } {\n      systems = [\n        \"x86_64-linux\"\n        \"i686-linux\"\n        \"x86_64-darwin\"\n        \"aarch64-linux\"\n        \"aarch64-darwin\"\n      ];\n\n      perSystem = { config, self', inputs', pkgs, system, lib, ... }: {\n        # define the packages provided by this flake\n        packages = {\n          blink-nvim = pkgs.vimUtils.buildVimPlugin {\n            pname = \"blink-nvim\";\n            version = \"2024-11-11\";\n            src = ./.;\n\n            meta = {\n              description = \"Set of simple, performant neovim plugins\";\n              homepage = \"https://github.com/saghen/blink.nvim\";\n              license = lib.licenses.mit;\n              maintainers = with lib.maintainers; [ redxtech ];\n            };\n          };\n\n          default = self'.packages.blink-nvim;\n        };\n      };\n    };\n}\n"
  },
  {
    "path": "lua/blink/chartoggle/config.lua",
    "content": "local M = {}\n\nM.default = {\n  delimiters = { ',', ';' },\n}\n\nfunction M.setup(opts) M.config = vim.tbl_deep_extend('force', M.default, opts or {}) end\n\nreturn setmetatable(M, { __index = function(_, k) return M.config[k] end })\n"
  },
  {
    "path": "lua/blink/chartoggle/init.lua",
    "content": "local M = {}\n\nfunction M.setup(opts) require('blink.chartoggle.config').setup(opts) end\n\n-- implementation from https://github.com/saifulapm/chartoggle.nvim\n-- todo: make a blink plugin with config, delimiters per language, filetype blocklist\nfunction M.toggle_char_eol(character)\n  local api = vim.api\n  local delimiters = require('blink.chartoggle.config').delimiters\n\n  local mode = api.nvim_get_mode().mode\n  local is_visual = mode == 'v' or mode == 'V' or mode == '\\22' -- <C-v>\n\n  -- have to exit visual mode for the marks to update\n  if is_visual then vim.fn.feedkeys(':', 'nx') end\n\n  local start_line = is_visual and vim.fn.getpos(\"'<\")[2] or api.nvim_win_get_cursor(0)[1]\n  local end_line = is_visual and vim.fn.getpos(\"'>\")[2] or start_line\n\n  for line_idx = start_line, end_line do\n    local line = api.nvim_buf_get_lines(0, line_idx - 1, line_idx, false)[1]\n    local last_char = line:sub(-1)\n\n    if last_char == character then\n      api.nvim_buf_set_lines(0, line_idx - 1, line_idx, false, { line:sub(1, #line - 1) })\n    elseif vim.tbl_contains(delimiters, last_char) then\n      api.nvim_buf_set_lines(0, line_idx - 1, line_idx, false, { line:sub(1, #line - 1) .. character })\n    else\n      api.nvim_buf_set_lines(0, line_idx - 1, line_idx, false, { line .. character })\n    end\n  end\nend\n\nreturn M\n"
  },
  {
    "path": "lua/blink/clue/init.lua",
    "content": "--- NOTE: This is just mini.clue with support for backspace\n---\n--- *mini.clue* Show next key clues\n--- *MiniClue*\n---\n--- MIT License Copyright (c) 2023 Evgeni Chasnovski\n---\n--- ==============================================================================\n---\n--- Features:\n--- - Implement custom key query process to reach target key combination:\n---     - Starts after customizable opt-in triggers (mode + keys).\n---\n---     - Each key press narrows down set of possible targets.\n---       Pressing `<BS>` removes previous user entry.\n---       Pressing `<Esc>` or `<C-c>` leads to an early stop.\n---       Doesn't depend on 'timeoutlen' and has basic support for 'langmap'.\n---\n---     - Ends when there is at most one target left or user pressed `<CR>`.\n---       Results into emulating pressing all query keys plus possible postkeys.\n---\n--- - Show window (after configurable delay) with clues. It lists available\n---   next keys along with their descriptions (auto generated from descriptions\n---   present keymaps and user-supplied clues; preferring the former).\n---\n--- - Configurable \"postkeys\" for key combinations - keys which will be emulated\n---   after combination is reached during key query process.\n---\n--- - Provide customizable sets of clues for common built-in keys/concepts:\n---     - `g` key.\n---     - `z` key.\n---     - Window commands.\n---     - Built-in completion.\n---     - Marks.\n---     - Registers.\n---\n--- - Lua functions to disable/enable triggers globally or per buffer.\n---\n--- For more details see:\n--- - |MiniClue-key-query-process|.\n--- - |MiniClue-examples|.\n--- - |MiniClue.config|.\n--- - |MiniClue.gen_clues|.\n---\n--- Notes:\n--- - Works on all supported versions but using Neovim>=0.9 is recommended.\n---\n--- - There is no functionality to create mappings while defining clues.\n---   This is done to clearly separate these two different actions.\n---   The best suggested practice is to manually create mappings with\n---   descriptions (`desc` field in options), as they will be automatically\n---   used inside clue window.\n---\n--- - Triggers are implemented as special buffer-local mappings. This leads to\n---   several caveats:\n---     - They will override same regular buffer-local mappings and have\n---       precedence over global one.\n---\n---       Example: having set `<C-w>` as Normal mode trigger means that\n---       there should not be another `<C-w>` mapping.\n---\n---     - They need to be the latest created buffer-local mappings or they will\n---       not function properly. Most common indicator of this is that some\n---       mapping starts to work only after clue window is shown.\n---\n---       Example: `g` is set as Normal mode trigger, but `gcc` from |mini.comment|\n---       doesn't work right away. This is probably because there are some\n---       other buffer-local mappings starting with `g` which were created after\n---       mapping for `g` trigger. Most common places for this are in LSP server's\n---       `on_attach` or during tree-sitter start in buffer.\n---\n---       To check if trigger is the most recent buffer-local mapping, execute\n---       `:<mode-char>map <trigger-keys>` (like `:nmap g` for previous example).\n---       Mapping for trigger should be the first listed.\n---\n---       This module makes the best effort to work out of the box and cover\n---       most common cases, but it is not full proof. The solution here is to\n---       ensure that triggers are created after making all buffer-local mappings:\n---       run either |MiniClue.setup()| or |MiniClue.ensure_buf_triggers()|.\n---\n--- - Descriptions from existing mappings take precedence over user-supplied\n---   clues. This is to ensure that information shown in clue window is as\n---   relevant as possible. To add/customize description of an already existing\n---   mapping, use |MiniClue.set_mapping_desc()|.\n---\n--- - Due to technical difficulties, there is no full proof support for\n---   Operator-pending mode triggers (like `a`/`i` from |mini.ai|):\n---     - Doesn't work as part of a command in \"temporary Normal mode\" (like\n---       after |i_CTRL-O|) due to implementation difficulties.\n---     - Can have unexpected behavior with custom operators.\n---\n--- - Has (mostly solved) issues with macros:\n---     - All triggers are disabled during macro recording due to technical\n---       reasons.\n---     - The `@` and `Q` keys are specially mapped inside |MiniClue.setup()|\n---       to temporarily disable triggers.\n---\n--- # Setup ~\n---\n--- This module needs a setup with `require('mini.clue').setup({})` (replace\n--- `{}` with your `config` table). It will create global Lua table `MiniClue`\n--- which you can use for scripting or manually (with `:lua MiniClue.*`).\n---\n--- Config table **needs to have triggers configured**, none is set up by default.\n---\n--- See |MiniClue.config| for available config settings.\n---\n--- You can override runtime config settings (like clues or window options)\n--- locally to a buffer inside `vim.b.miniclue_config` which should have same\n--- structure as `MiniClue.config`. See |mini.nvim-buffer-local-config| for\n--- more details.\n---\n--- # Comparisons ~\n---\n--- - 'folke/which-key.nvim':\n---     - Both have the same main goal: show available next keys along with\n---       their customizable descriptions.\n---     - Has different UI and content layout.\n---     - Allows creating mappings inside its configuration, while this module\n---       doesn't have this by design (to clearly separate two different tasks).\n---     - Doesn't allow creating submodes, while this module does (via `postkeys`).\n---\n--- - 'anuvyklack/hydra.nvim':\n---     - Both allow creating submodes: state which starts at certain key\n---       combination; treats some keys differently; ends after `<Esc>`.\n---     - Doesn't show information about available next keys (outside of\n---       submodes), while that is this module's main goal.\n---\n--- # Highlight groups ~\n---\n--- * `MiniClueBorder` - window border.\n--- * `MiniClueDescGroup` - group description in clue window.\n--- * `MiniClueDescSingle` - single target description in clue window.\n--- * `MiniClueNextKey` - next key label in clue window.\n--- * `MiniClueNextKeyWithPostkeys` - next key label with postkeys in clue window.\n--- * `MiniClueSeparator` - separator in clue window.\n--- * `MiniClueTitle` - window title.\n---\n--- To change any highlight group, modify it directly with |:highlight|.\n---\n--- # Disabling ~\n---\n--- To disable creating triggers, set `vim.g.miniclue_disable` (globally) or\n--- `vim.b.miniclue_disable` (for a buffer) to `true`. Considering high number\n--- of different scenarios and customization intentions, writing exact rules\n--- for disabling module's functionality is left to user. See\n--- |mini.nvim-disabling-recipes| for common recipes.\n\n--- # Key query process ~\n---\n--- ## General info ~\n---\n--- This module implements custom key query process imitating a usual built-in\n--- mechanism of user pressing keys in order to execute a mapping. General idea\n--- is the same: narrow down key combinations until the target is reached.\n---\n--- Main goals of its existence are:\n---\n--- - Allow reaching certain mappings be independent of 'timeoutlen'. That is,\n---   there is no fixed timeout after which currently typed keys are executed.\n---\n--- - Enable automated showing of next key clues after user-supplied delay\n---   (also independent of 'timeoutlen').\n---\n--- - Allow emulating configurable key presses after certain key combination is\n---   reached. This granular control allows creating so called \"submodes\".\n---   See more at |MiniClue-examples-submodes|.\n---\n--- This process is primarily designed for nested `<Leader>` mappings in Normal\n--- mode but works in all other main modes: Visual, Insert, Operator-pending\n--- (with caveats; no full proof guarantees), Command-line, Terminal.\n---\n--- ## Lifecycle ~\n---\n--- - Key query process starts when user types a trigger: certain keys in certain\n---   mode. Those keys are put into key query as a single user input. All possible\n---   mode key combinations are filtered to ones starting with the trigger keys.\n---\n---   Note: trigger is implemented as a regular mapping, so if it has at least\n---   two keys, they should be pressed within 'timeoutlen' milliseconds.\n---\n--- - Wait (indefinitely) for user to press a key. Advance depending on the key:\n---\n---     - Special key:\n---\n---         - If `<Esc>` or `<C-c>`, stop the process without any action.\n---\n---         - If `<CR>`, stop the process and execute current key query, meaning\n---           emulate (with |nvim_feedkeys()|) user pressing those keys.\n---\n---         - If `<BS>`, remove previous user input from the query. If query becomes\n---           empty, stop the process without any action.\n---\n---         - If a key for scrolling clue window (`scroll_down` / `scroll_up`\n---           in `config.window`; `<C-d>` / `<C-u>` by default), scroll clue window\n---           and wait for the next user key.\n---           Note: if clue window is not shown, treated as a not special key.\n---\n---     - Not special key. Add key to the query while filtering all available\n---       key combinations to start with the current key query. Advance:\n---\n---         - If there is a single available key combination matching current\n---           key query, execute it.\n---\n---         - If there is no key combinations starting with the current query,\n---           execute it. This, for instance, allows a seamless execution of\n---           operators in presence of a longer key combinations. Example: with\n---           `g` as trigger in Normal mode and available mappings `gc` / `gcc`\n---           (like from |mini.comment|), this allows typing `gcip` to comment\n---           current paragraph, although there are no key combinations\n---           starting with `gci`.\n---\n---         - Otherwise wait for the new user key press.\n---\n--- ## Clue window ~\n---\n--- After initiating key query process and after each key press, a timer is\n--- started to show a clue window: floating window with information about\n--- available next keys along with their descriptions. Note: if window is\n--- already shown, its content is updated right away.\n---\n--- Clues can have these types:\n---\n--- - \"Terminal next key\": when pressed, will lead to query execution.\n---\n--- - \"Terminal next key with postkeys\": when pressed, will lead to query\n---   execution plus some configured postkeys.\n---\n--- - \"Group next key\": when pressed, will narrow down available key combinations\n---   and wait for another key press. Note: can have configured description\n---   (inside `config.clues`) or it will be auto generated based on the number of\n---   available key combinations.\n---@tag MiniClue-key-query-process\n\n--- # Full starter example ~\n---\n--- If not sure where to start, try this example with all provided clues from\n--- this module plus all |<Leader>| mappings in Normal and Visual modes: >\n---\n---   local miniclue = require('mini.clue')\n---   miniclue.setup({\n---     triggers = {\n---       -- Leader triggers\n---       { mode = 'n', keys = '<Leader>' },\n---       { mode = 'x', keys = '<Leader>' },\n---\n---       -- Built-in completion\n---       { mode = 'i', keys = '<C-x>' },\n---\n---       -- `g` key\n---       { mode = 'n', keys = 'g' },\n---       { mode = 'x', keys = 'g' },\n---\n---       -- Marks\n---       { mode = 'n', keys = \"'\" },\n---       { mode = 'n', keys = '`' },\n---       { mode = 'x', keys = \"'\" },\n---       { mode = 'x', keys = '`' },\n---\n---       -- Registers\n---       { mode = 'n', keys = '\"' },\n---       { mode = 'x', keys = '\"' },\n---       { mode = 'i', keys = '<C-r>' },\n---       { mode = 'c', keys = '<C-r>' },\n---\n---       -- Window commands\n---       { mode = 'n', keys = '<C-w>' },\n---\n---       -- `z` key\n---       { mode = 'n', keys = 'z' },\n---       { mode = 'x', keys = 'z' },\n---     },\n---\n---     clues = {\n---       -- Enhance this by adding descriptions for <Leader> mapping groups\n---       miniclue.gen_clues.builtin_completion(),\n---       miniclue.gen_clues.g(),\n---       miniclue.gen_clues.marks(),\n---       miniclue.gen_clues.registers(),\n---       miniclue.gen_clues.windows(),\n---       miniclue.gen_clues.z(),\n---     },\n---   })\n---\n--- # Leader clues ~\n---\n--- Assume there are these |<Leader>| mappings set up: >\n---\n---   -- Set `<Leader>` before making any mappings and configuring 'mini.clue'\n---   vim.g.mapleader = ' '\n---\n---   local nmap_leader = function(suffix, rhs, desc)\n---     vim.keymap.set('n', '<Leader>' .. suffix, rhs, { desc = desc })\n---   end\n---   local xmap_leader = function(suffix, rhs, desc)\n---     vim.keymap.set('x', '<Leader>' .. suffix, rhs, { desc = desc })\n---   end\n---\n---   nmap_leader('bd', '<Cmd>lua MiniBufremove.delete()<CR>',  'Delete')\n---   nmap_leader('bw', '<Cmd>lua MiniBufremove.wipeout()<CR>', 'Wipeout')\n---\n---   nmap_leader('lf', '<Cmd>lua vim.lsp.buf.format()<CR>',     'Format')\n---   xmap_leader('lf', '<Cmd>lua vim.lsp.buf.format()<CR>',     'Format')\n---   nmap_leader('lr', '<Cmd>lua vim.lsp.buf.rename()<CR>',     'Rename')\n---   nmap_leader('lR', '<Cmd>lua vim.lsp.buf.references()<CR>', 'References')\n---\n---\n--- The following setup will enable |<Leader>| as trigger in Normal and Visual\n--- modes and add descriptions to mapping groups: >\n---\n---   require('mini.clue').setup({\n---     -- Register `<Leader>` as trigger\n---     triggers = {\n---       { mode = 'n', keys = '<Leader>' },\n---       { mode = 'x', keys = '<Leader>' },\n---     },\n---\n---     -- Add descriptions for mapping groups\n---     clues = {\n---       { mode = 'n', keys = '<Leader>b', desc = '+Buffers' },\n---       { mode = 'n', keys = '<Leader>l', desc = '+LSP' },\n---     },\n---   })\n---\n--- # Clues without mappings ~\n---\n--- Clues can be shown not only for actually present mappings. This is helpful for\n--- showing clues for built-in key combinations. Here is an example of clues for\n--- a subset of built-in completion (see |MiniClue.gen_clues.builtin_completion()|\n--- to generate clues for all available completion sources): >\n---\n---   require('mini.clue').setup({\n---     -- Make `<C-x>` a trigger. Otherwise, key query process won't start.\n---     triggers = {\n---       { mode = 'i', keys = '<C-x>' },\n---     },\n---\n---     -- Register custom clues\n---     clues = {\n---       { mode = 'i', keys = '<C-x><C-f>', desc = 'File names' },\n---       { mode = 'i', keys = '<C-x><C-l>', desc = 'Whole lines' },\n---       { mode = 'i', keys = '<C-x><C-o>', desc = 'Omni completion' },\n---       { mode = 'i', keys = '<C-x><C-s>', desc = 'Spelling suggestions' },\n---       { mode = 'i', keys = '<C-x><C-u>', desc = \"With 'completefunc'\" },\n---     }\n---   })\n--- <\n---                                                     *MiniClue-examples-submodes*\n--- # Submodes ~\n---\n--- Submode is a state initiated after pressing certain key combination (\"prefix\")\n--- during which some keys are interpreted differently.\n---\n--- In this module submode can be implemented following these steps:\n---\n--- - Create mappings for each key inside submode. Left hand side of mappings\n---   should consist from prefix followed by the key.\n---\n--- - Create clue for each key inside submode with `postkeys` value equal to\n---   prefix. It would mean that after executing particular key combination from\n---   this submode, pressing its prefix will be automatically emulated (leading\n---   back to being inside submode).\n---\n--- - Register submode prefix (or some of its starting part) as trigger.\n---\n--- ## Submode examples ~\n---\n--- - Submode for moving with |mini.move|:\n---     - Press `<Leader>m` to start submode.\n---     - Press any of `h`/`j`/`k`/`l` to move selection/line.\n---     - Press `<Esc>` to stop submode.\n---\n---   The code: >\n---\n---   require('mini.move').setup({\n---     mappings = {\n---       left       = '<Leader>mh',\n---       right      = '<Leader>ml',\n---       down       = '<Leader>mj',\n---       up         = '<Leader>mk',\n---       line_left  = '<Leader>mh',\n---       line_right = '<Leader>ml',\n---       line_down  = '<Leader>mj',\n---       line_up    = '<Leader>mk',\n---     },\n---   })\n---\n---   require('mini.clue').setup({\n---     triggers = {\n---       { mode = 'n', keys = '<Leader>m' },\n---       { mode = 'x', keys = '<Leader>m' },\n---     },\n---     clues = {\n---       { mode = 'n', keys = '<Leader>mh', postkeys = '<Leader>m' },\n---       { mode = 'n', keys = '<Leader>mj', postkeys = '<Leader>m' },\n---       { mode = 'n', keys = '<Leader>mk', postkeys = '<Leader>m' },\n---       { mode = 'n', keys = '<Leader>ml', postkeys = '<Leader>m' },\n---       { mode = 'x', keys = '<Leader>mh', postkeys = '<Leader>m' },\n---       { mode = 'x', keys = '<Leader>mj', postkeys = '<Leader>m' },\n---       { mode = 'x', keys = '<Leader>mk', postkeys = '<Leader>m' },\n---       { mode = 'x', keys = '<Leader>ml', postkeys = '<Leader>m' },\n---     },\n---   })\n---\n--- - Submode for iterating buffers and windows with |mini.bracketed|:\n---     - Press `[` or `]` to start key query process for certain direction.\n---     - Press `b` / `w` to iterate buffers/windows until reach target one.\n---     - Press `<Esc>` to stop submode.\n---\n---   The code: >\n---\n---   require('mini.bracketed').setup()\n---\n---   require('mini.clue').setup({\n---     triggers = {\n---       { mode = 'n', keys = ']' },\n---       { mode = 'n', keys = '[' },\n---     },\n---     clues = {\n---       { mode = 'n', keys = ']b', postkeys = ']' },\n---       { mode = 'n', keys = ']w', postkeys = ']' },\n---\n---       { mode = 'n', keys = '[b', postkeys = '[' },\n---       { mode = 'n', keys = '[w', postkeys = '[' },\n---     },\n---   })\n---\n--- - Submode for window commands using |MiniClue.gen_clues.windows()|:\n---     - Press `<C-w>` to start key query process.\n---     - Press keys which move / change focus / resize windows.\n---     - Press `<Esc>` to stop submode.\n---\n---   The code: >\n---\n---   local miniclue = require('mini.clue')\n---   miniclue.setup({\n---     triggers = {\n---       { mode = 'n', keys = '<C-w>' },\n---     },\n---     clues = {\n---       miniclue.gen_clues.windows({\n---         submode_move = true,\n---         submode_navigate = true,\n---         submode_resize = true,\n---       })\n---     },\n---   })\n---\n--- # Window config ~\n--- >\n---   require('mini.clue').setup({\n---     triggers = { { mode = 'n', keys = '<Leader>' } },\n---\n---     window = {\n---       -- Show window immediately\n---       delay = 0,\n---\n---       config = {\n---         -- Compute window width automatically\n---         width = 'auto',\n---\n---         -- Use double-line border\n---         border = 'double',\n---       },\n---     },\n---   })\n---@tag MiniClue-examples\n\n---@diagnostic disable:undefined-field\n---@diagnostic disable:discard-returns\n---@diagnostic disable:unused-local\n---@diagnostic disable:cast-local-type\n\n-- Module definition ==========================================================\nlocal MiniClue = {}\nlocal H = {}\n\n--- Module setup\n---\n---@param config table|nil Module config table. See |MiniClue.config|.\n---\n---@usage `require('mini.clue').setup({})` (replace `{}` with your `config` table).\n--- **Needs to have triggers configured**.\nMiniClue.setup = function(config)\n  -- Export module\n  _G.MiniClue = MiniClue\n\n  -- Setup config\n  config = H.setup_config(config)\n\n  -- Apply config\n  H.apply_config(config)\n\n  -- Define behavior\n  H.create_autocommands(config)\n\n  -- Create default highlighting\n  H.create_default_hl()\nend\n\n--stylua: ignore\n--- Module config\n---\n--- Default values:\n---@eval return MiniDoc.afterlines_to_code(MiniDoc.current.eval_section)\n---@text # General info ~\n---\n--- - To use |<Leader>| as part of the config (either as trigger or inside clues),\n---   set it prior to running |MiniClue.setup()|.\n---\n--- - See |MiniClue-examples| for examples.\n---\n--- # Clues ~\n---\n--- `config.clues` is an array with extra information about key combinations.\n--- Each element can be one of:\n--- - Clue table.\n--- - Array (possibly nested) of clue tables.\n--- - Callable (function) returning either of the previous two.\n---\n--- A clue table is a table with the following fields:\n--- - <mode> `(string)` - single character describing **single** mode short-name of\n---   key combination as in `nvim_set_keymap()` ('n', 'x', 'i', 'o', 'c', etc.).\n--- - <keys> `(string)` - key combination for which clue will be shown.\n---   \"Human-readable\" key names as in |key-notation| (like \"<Leader>\", \"<Space>\",\n---   \"<Tab>\", etc.) are allowed.\n--- - <desc> `(string|nil)` - optional key combination description which will\n---   be shown in clue window.\n--- - <postkeys> `(string|nil)` - optional postkeys which will be executed\n---   automatically after `keys`. Allows creation of submodes\n---   (see |MiniClue-examples-submodes|).\n---\n--- Notes:\n--- - Postkeys are literal simulation of keypresses with |nvim_feedkeys()|.\n---\n--- - Suggested approach to configuring clues is to create mappings with `desc`\n---   field while supplying to `config.clues` only elements describing groups,\n---   postkeys, and built-in mappings.\n---\n--- # Triggers ~\n---\n--- `config.triggers` is an array with information when |MiniClue-key-query-process|\n--- should start. Each element is a trigger table with the fields <mode> and\n--- <keys> which are treated the same as in clue table.\n---\n--- # Window ~\n---\n--- `config.window` defines behavior of clue window.\n---\n--- `config.window.delay` is a number of milliseconds after which clue window will\n--- appear. Can be 0 to show immediately.\n---\n--- `config.window.config` is a table defining floating window characteristics\n--- or a callable returning such table (will be called with identifier of\n--- window's buffer already showing all clues). It should have the same\n--- structure as in |nvim_open_win()| with the following enhancements:\n--- - <width> field can be equal to `\"auto\"` leading to window width being\n---   computed automatically based on its content. Default is fixed width of 30.\n--- - <row> and <col> can be equal to `\"auto\"` in which case they will be\n---   computed to \"stick\" to set anchor (\"SE\" by default; see |nvim_open_win()|).\n---   This allows changing corner in which window is shown: >\n---\n---   -- Pick one anchor\n---   local anchor = 'NW' -- top-left\n---   local anchor = 'NE' -- top-right\n---   local anchor = 'SW' -- bottom-left\n---   local anchor = 'SE' -- bottom-right\n---\n---   require('mini.clue').setup({\n---     window = {\n---       config = { anchor = anchor, row = 'auto', col = 'auto' },\n---     },\n---   })\n---\n--- `config.window.scroll_down` / `config.window.scroll_up` are strings defining\n--- keys which will scroll clue window down / up which is useful in case not\n--- all clues fit in current window height. Set to empty string `''` to disable\n--- either of them.\nMiniClue.config = {\n  -- Array of extra clues to show\n  clues = {},\n\n  -- Array of opt-in triggers which start custom key query process.\n  -- **Needs to have something in order to show clues**.\n  triggers = {},\n\n  -- Clue window settings\n  window = {\n    -- Floating window config\n    config = {},\n\n    -- Delay before showing clue window\n    delay = 1000,\n\n    -- Keys to scroll inside the clue window\n    scroll_down = '<C-d>',\n    scroll_up = '<C-u>',\n  },\n}\n--minidoc_afterlines_end\n\n--- Enable triggers in all listed buffers\nMiniClue.enable_all_triggers = function()\n  for _, buf_id in ipairs(vim.api.nvim_list_bufs()) do\n    -- Map only inside valid listed buffers\n    if vim.fn.buflisted(buf_id) == 1 then H.map_buf_triggers(buf_id) end\n  end\nend\n\n--- Enable triggers in buffer\n---\n---@param buf_id number|nil Buffer identifier. Default: current buffer.\nMiniClue.enable_buf_triggers = function(buf_id)\n  buf_id = (buf_id == nil or buf_id == 0) and vim.api.nvim_get_current_buf() or buf_id\n  if not H.is_valid_buf(buf_id) then H.error('`buf_id` should be a valid buffer identifier.') end\n  H.map_buf_triggers(buf_id)\nend\n\n--- Disable triggers in all buffers\nMiniClue.disable_all_triggers = function()\n  for _, buf_id in ipairs(vim.api.nvim_list_bufs()) do\n    H.unmap_buf_triggers(buf_id)\n  end\nend\n\n--- Disable triggers in buffer\n---\n---@param buf_id number|nil Buffer identifier. Default: current buffer.\nMiniClue.disable_buf_triggers = function(buf_id)\n  buf_id = (buf_id == nil or buf_id == 0) and vim.api.nvim_get_current_buf() or buf_id\n  if not H.is_valid_buf(buf_id) then H.error('`buf_id` should be a valid buffer identifier.') end\n  H.unmap_buf_triggers(buf_id)\nend\n\n--- Ensure all triggers are valid\nMiniClue.ensure_all_triggers = function()\n  MiniClue.disable_all_triggers()\n  MiniClue.enable_all_triggers()\nend\n\n--- Ensure buffer triggers are valid\n---\n---@param buf_id number|nil Buffer identifier. Default: current buffer.\nMiniClue.ensure_buf_triggers = function(buf_id)\n  MiniClue.disable_buf_triggers(buf_id)\n  MiniClue.enable_buf_triggers(buf_id)\nend\n\n--- Update description of an existing mapping\n---\n--- Notes:\n--- - Uses buffer-local mapping in case there are both global and buffer-local\n---   mappings with same mode and LHS. Similar to |maparg()|.\n--- - Requires Neovim>=0.8.\n---\n---@param mode string Mapping mode (as in `maparg()`).\n---@param lhs string Mapping left hand side (as `name` in `maparg()`).\n---@param desc string New description to set.\nMiniClue.set_mapping_desc = function(mode, lhs, desc)\n  if vim.fn.has('nvim-0.8') == 0 then H.error('`set_mapping_desc()` requires Neovim>=0.8.') end\n\n  if type(mode) ~= 'string' then H.error('`mode` should be string.') end\n  if type(lhs) ~= 'string' then H.error('`lhs` should be string.') end\n  if type(desc) ~= 'string' then H.error('`desc` should be string.') end\n\n  local ok_get, map_data = pcall(vim.fn.maparg, lhs, mode, false, true)\n  if not ok_get or vim.tbl_count(map_data) == 0 then\n    local msg = string.format('No mapping found for mode %s and LHS %s.', vim.inspect(mode), vim.inspect(lhs))\n    H.error(msg)\n  end\n\n  map_data.desc = desc\n  local ok_set = pcall(vim.fn.mapset, mode, false, map_data)\n  if not ok_set then H.error(vim.inspect(desc) .. ' is not a valid description.') end\nend\n\n--- Generate pre-configured clues\n---\n--- This is a table with function elements. Call to actually get array of clues.\nMiniClue.gen_clues = {}\n\n--- Generate clues for built-in completion\n---\n--- Contains clues for the following triggers: >\n---\n---   { mode = 'i', keys = '<C-x>' }\n---\n---@return table Array of clues.\nMiniClue.gen_clues.builtin_completion = function()\n  --stylua: ignore\n  return {\n    { mode = 'i', keys = '<C-x><C-d>', desc = 'Defined identifiers' },\n    { mode = 'i', keys = '<C-x><C-e>', desc = 'Scroll up' },\n    { mode = 'i', keys = '<C-x><C-f>', desc = 'File names' },\n    { mode = 'i', keys = '<C-x><C-i>', desc = 'Identifiers' },\n    { mode = 'i', keys = '<C-x><C-k>', desc = 'Identifiers from dictionary' },\n    { mode = 'i', keys = '<C-x><C-l>', desc = 'Whole lines' },\n    { mode = 'i', keys = '<C-x><C-n>', desc = 'Next completion' },\n    { mode = 'i', keys = '<C-x><C-o>', desc = 'Omni completion' },\n    { mode = 'i', keys = '<C-x><C-p>', desc = 'Previous completion' },\n    { mode = 'i', keys = '<C-x><C-s>', desc = 'Spelling suggestions' },\n    { mode = 'i', keys = '<C-x><C-t>', desc = 'Identifiers from thesaurus' },\n    { mode = 'i', keys = '<C-x><C-y>', desc = 'Scroll down' },\n    { mode = 'i', keys = '<C-x><C-u>', desc = \"With 'completefunc'\" },\n    { mode = 'i', keys = '<C-x><C-v>', desc = 'Like in command line' },\n    { mode = 'i', keys = '<C-x><C-z>', desc = 'Stop completion' },\n    { mode = 'i', keys = '<C-x><C-]>', desc = 'Tags' },\n    { mode = 'i', keys = '<C-x>s',     desc = 'Spelling suggestions' },\n  }\nend\n\n--- Generate clues for `g` key\n---\n--- Contains clues for the following triggers: >\n---\n---   { mode = 'n', keys = 'g' }\n---   { mode = 'x', keys = 'g' }\n---\n---@return table Array of clues.\nMiniClue.gen_clues.g = function()\n  --stylua: ignore\n  return {\n    { mode = 'n', keys = 'g0',     desc = 'Go to leftmost visible column' },\n    { mode = 'n', keys = 'g8',     desc = 'Print hex value of char under cursor' },\n    { mode = 'n', keys = 'ga',     desc = 'Print ascii value' },\n    { mode = 'n', keys = 'gD',     desc = 'Go to definition in file' },\n    { mode = 'n', keys = 'gd',     desc = 'Go to definition in function' },\n    { mode = 'n', keys = 'gE',     desc = 'Go backwards to end of previous WORD' },\n    { mode = 'n', keys = 'ge',     desc = 'Go backwards to end of previous word' },\n    { mode = 'n', keys = 'gF',     desc = 'Edit file under cursor + jump line' },\n    { mode = 'n', keys = 'gf',     desc = 'Edit file under cursor' },\n    { mode = 'n', keys = 'gg',     desc = 'Go to line (def: first)' },\n    { mode = 'n', keys = 'gH',     desc = 'Start Select line mode' },\n    { mode = 'n', keys = 'gh',     desc = 'Start Select mode' },\n    { mode = 'n', keys = 'gI',     desc = 'Start Insert at column 1' },\n    { mode = 'n', keys = 'gi',     desc = 'Start Insert where it stopped' },\n    { mode = 'n', keys = 'gJ',     desc = 'Join lines without extra spaces' },\n    { mode = 'n', keys = 'gj',     desc = 'Go down by screen lines' },\n    { mode = 'n', keys = 'gk',     desc = 'Go up by screen lines' },\n    { mode = 'n', keys = 'gM',     desc = 'Go to middle of text line' },\n    { mode = 'n', keys = 'gm',     desc = 'Go to middle of screen line' },\n    { mode = 'n', keys = 'gN',     desc = 'Select previous search match' },\n    { mode = 'n', keys = 'gn',     desc = 'Select next search match' },\n    { mode = 'n', keys = 'go',     desc = 'Go to byte' },\n    { mode = 'n', keys = 'gP',     desc = 'Put text before cursor + stay after it' },\n    { mode = 'n', keys = 'gp',     desc = 'Put text after cursor + stay after it' },\n    { mode = 'n', keys = 'gQ',     desc = 'Switch to \"Ex\" mode' },\n    { mode = 'n', keys = 'gq',     desc = 'Format text (operator)' },\n    { mode = 'n', keys = 'gR',     desc = 'Enter Virtual Replace mode' },\n    { mode = 'n', keys = 'gr',     desc = 'Virtual replace with character' },\n    { mode = 'n', keys = 'gs',     desc = 'Sleep' },\n    { mode = 'n', keys = 'gT',     desc = 'Go to previous tabpage' },\n    { mode = 'n', keys = 'gt',     desc = 'Go to next tabpage' },\n    { mode = 'n', keys = 'gU',     desc = 'Make uppercase (operator)' },\n    { mode = 'n', keys = 'gu',     desc = 'Make lowercase (operator)' },\n    { mode = 'n', keys = 'gV',     desc = 'Avoid reselect' },\n    { mode = 'n', keys = 'gv',     desc = 'Reselect previous Visual area' },\n    { mode = 'n', keys = 'gw',     desc = 'Format text + keep cursor (operator)' },\n    { mode = 'n', keys = 'gx',     desc = 'Execute app for file under cursor' },\n    { mode = 'n', keys = 'g<C-]>', desc = '`:tjump` to tag under cursor' },\n    { mode = 'n', keys = 'g<C-a>', desc = 'Dump a memory profile' },\n    { mode = 'n', keys = 'g<C-g>', desc = 'Show information about cursor' },\n    { mode = 'n', keys = 'g<C-h>', desc = 'Start Select block mode' },\n    { mode = 'n', keys = 'g<Tab>', desc = 'Go to last accessed tabpage' },\n    { mode = 'n', keys = \"g'\",     desc = \"Jump to mark (don't affect jumplist)\" },\n    { mode = 'n', keys = 'g#',     desc = 'Search backwards word under cursor' },\n    { mode = 'n', keys = 'g$',     desc = 'Go to rightmost visible column' },\n    { mode = 'n', keys = 'g%',     desc = 'Cycle through matching groups' },\n    { mode = 'n', keys = 'g&',     desc = 'Repeat last `:s` on all lines' },\n    { mode = 'n', keys = 'g*',     desc = 'Search word under cursor' },\n    { mode = 'n', keys = 'g+',     desc = 'Go to newer text state' },\n    { mode = 'n', keys = 'g,',     desc = 'Go to newer position in change list' },\n    { mode = 'n', keys = 'g-',     desc = 'Go to older text state' },\n    { mode = 'n', keys = 'g;',     desc = 'Go to older position in change list' },\n    { mode = 'n', keys = 'g<',     desc = 'Display previous command output' },\n    { mode = 'n', keys = 'g?',     desc = 'Rot13 encode (operator)' },\n    { mode = 'n', keys = 'g@',     desc = \"Call 'operatorfunc' (operator)\" },\n    { mode = 'n', keys = 'g]',     desc = '`:tselect` tag under cursor' },\n    { mode = 'n', keys = 'g^',     desc = 'Go to leftmost visible non-whitespace' },\n    { mode = 'n', keys = 'g_',     desc = 'Go to lower line' },\n    { mode = 'n', keys = 'g`',     desc = \"Jump to mark (don't affect jumplist)\" },\n    { mode = 'n', keys = 'g~',     desc = 'Swap case (operator)' },\n\n    { mode = 'x', keys = 'gf',     desc = 'Edit selected file' },\n    { mode = 'x', keys = 'gJ',     desc = 'Join selected lines without extra spaces' },\n    { mode = 'x', keys = 'gq',     desc = 'Format selection' },\n    { mode = 'x', keys = 'gV',     desc = 'Avoid reselect' },\n    { mode = 'x', keys = 'gw',     desc = 'Format selection + keep cursor' },\n    { mode = 'x', keys = 'g<C-]>', desc = '`:tjump` to selected tag' },\n    { mode = 'x', keys = 'g<C-a>', desc = 'Increment with compound' },\n    { mode = 'x', keys = 'g<C-g>', desc = 'Show information about selection' },\n    { mode = 'x', keys = 'g<C-x>', desc = 'Decrement with compound' },\n    { mode = 'x', keys = 'g]',     desc = '`:tselect` selected tag' },\n    { mode = 'x', keys = 'g?',     desc = 'Rot13 encode selection' },\n  }\nend\n\n--- Generate clues for marks\n---\n--- Contains clues for the following triggers: >\n---\n---   { mode = 'n', keys = \"'\" }\n---   { mode = 'n', keys = \"g'\" }\n---   { mode = 'n', keys = '`' }\n---   { mode = 'n', keys = 'g`' }\n---   { mode = 'x', keys = \"'\" }\n---   { mode = 'x', keys = \"g'\" }\n---   { mode = 'x', keys = '`' }\n---   { mode = 'x', keys = 'g`' }\n---\n--- Note: if you use \"g\" as trigger (like to enable |MiniClue.gen_clues.g()|),\n--- don't add \"g'\" and \"g`\" as triggers: they already will be taken into account.\n---\n---@return table Array of clues.\n---\n---@seealso |mark-motions|\nMiniClue.gen_clues.marks = function()\n  local describe_marks = function(mode, prefix)\n    local make_clue = function(register, desc) return { mode = mode, keys = prefix .. register, desc = desc } end\n\n    return {\n      make_clue('^', 'Latest insert position'),\n      make_clue('.', 'Latest change'),\n      make_clue('\"', 'Latest exited position'),\n      make_clue(\"'\", 'Line before jump'),\n      make_clue('`', 'Position before jump'),\n      make_clue('[', 'Start of latest changed or yanked text'),\n      make_clue(']', 'End of latest changed or yanked text'),\n      make_clue('(', 'Start of sentence'),\n      make_clue(')', 'End of sentence'),\n      make_clue('{', 'Start of paragraph'),\n      make_clue('}', 'End of paragraph'),\n      make_clue('<', 'Start of latest visual selection'),\n      make_clue('>', 'End of latest visual selection'),\n    }\n  end\n\n  --stylua: ignore\n  return {\n    -- Normal mode\n    describe_marks('n', \"'\"),\n    describe_marks('n', \"g'\"),\n    describe_marks('n', \"`\"),\n    describe_marks('n', \"g`\"),\n\n    -- Visual mode\n    describe_marks('x', \"'\"),\n    describe_marks('x', \"g'\"),\n    describe_marks('x', \"`\"),\n    describe_marks('x', \"g`\"),\n  }\nend\n\n--- Generate clues for registers\n---\n--- Contains clues for the following triggers: >\n---\n---   { mode = 'n', keys = '\"' }\n---   { mode = 'x', keys = '\"' }\n---   { mode = 'i', keys = '<C-r>' }\n---   { mode = 'c', keys = '<C-r>' }\n---\n---@param opts table|nil Options. Possible keys:\n---   - <show_contents> `(boolean)` - whether to show contents of all possible\n---     registers. If `false`, only description of special registers is shown.\n---     Default: `false`.\n---\n---@return table Array of clues.\n---\n---@seealso |registers|\nMiniClue.gen_clues.registers = function(opts)\n  opts = vim.tbl_deep_extend('force', { show_contents = false }, opts or {})\n\n  local describe_registers\n  if opts.show_contents then\n    describe_registers = H.make_clues_with_register_contents\n  else\n    describe_registers = function(mode, prefix)\n      local make_clue = function(register, desc) return { mode = mode, keys = prefix .. register, desc = desc } end\n      return {\n        make_clue('0', 'Latest yank'),\n        make_clue('1', 'Latest big delete'),\n        make_clue('\"', 'Default register'),\n        make_clue('#', 'Alternate buffer'),\n        make_clue('%', 'Name of the current file'),\n        make_clue('*', 'Selection clipboard'),\n        make_clue('+', 'System clipboard'),\n        make_clue('-', 'Latest small delete'),\n        make_clue('.', 'Latest inserted text'),\n        make_clue('/', 'Latest search pattern'),\n        make_clue(':', 'Latest executed command'),\n        make_clue('=', 'Result of expression'),\n        make_clue('_', 'Black hole'),\n      }\n    end\n  end\n\n  --stylua: ignore\n  return {\n    -- Normal mode\n    describe_registers('n', '\"'),\n\n    -- Visual mode\n    describe_registers('x', '\"'),\n\n    -- Insert mode\n    describe_registers('i', '<C-r>'),\n\n    { mode = 'i', keys = '<C-r><C-r>', desc = '+Insert literally' },\n    describe_registers('i', '<C-r><C-r>'),\n\n    { mode = 'i', keys = '<C-r><C-o>', desc = '+Insert literally + not auto-indent' },\n    describe_registers('i', '<C-r><C-o>'),\n\n    { mode = 'i', keys = '<C-r><C-p>', desc = '+Insert + fix indent' },\n    describe_registers('i', '<C-r><C-p>'),\n\n    -- Command-line mode\n    describe_registers('c', '<C-r>'),\n\n    { mode = 'c', keys = '<C-r><C-r>', desc = '+Insert literally' },\n    describe_registers('c', '<C-r><C-r>'),\n\n    { mode = 'c', keys = '<C-r><C-o>', desc = '+Insert literally' },\n    describe_registers('c', '<C-r><C-o>'),\n  }\nend\n\n--- Generate clues for window commands\n---\n--- Contains clues for the following triggers: >\n---\n---   { mode = 'n', keys = '<C-w>' }\n---\n--- Note: only non-duplicated commands are included. For full list see |CTRL-W|.\n---\n---@param opts table|nil Options. Possible keys:\n---   - <submode_move> `(boolean)` - whether to make move (change layout)\n---     commands a submode by using `postkeys` field. Default: `false`.\n---   - <submode_navigate> `(boolean)` - whether to make navigation (change\n---     focus) commands a submode by using `postkeys` field. Default: `false`.\n---   - <submode_resize> `(boolean)` - whether to make resize (change size)\n---     commands a submode by using `postkeys` field. Default: `false`.\n---\n---@return table Array of clues.\nMiniClue.gen_clues.windows = function(opts)\n  local default_opts = { submode_navigate = false, submode_move = false, submode_resize = false }\n  opts = vim.tbl_deep_extend('force', default_opts, opts or {})\n\n  local postkeys_move, postkeys_navigate, postkeys_resize = nil, nil, nil\n  if opts.submode_move then postkeys_move = '<C-w>' end\n  if opts.submode_navigate then postkeys_navigate = '<C-w>' end\n  if opts.submode_resize then postkeys_resize = '<C-w>' end\n\n  --stylua: ignore\n  return {\n    { mode = 'n', keys = '<C-w>+',      desc = 'Increase height',         postkeys = postkeys_resize },\n    { mode = 'n', keys = '<C-w>-',      desc = 'Decrease height',         postkeys = postkeys_resize },\n    { mode = 'n', keys = '<C-w><',      desc = 'Decrease width',          postkeys = postkeys_resize },\n    { mode = 'n', keys = '<C-w>>',      desc = 'Increase width',          postkeys = postkeys_resize },\n    { mode = 'n', keys = '<C-w>=',      desc = 'Make windows same dimensions' },\n    { mode = 'n', keys = '<C-w>]',      desc = 'Split + jump to tag' },\n    { mode = 'n', keys = '<C-w>^',      desc = 'Split + edit alternate file' },\n    { mode = 'n', keys = '<C-w>_',      desc = 'Set height (def: very high)' },\n    { mode = 'n', keys = '<C-w>|',      desc = 'Set width (def: very wide)' },\n    { mode = 'n', keys = '<C-w>}',      desc = 'Show tag in preview' },\n    { mode = 'n', keys = '<C-w>b',      desc = 'Focus bottom',            postkeys = postkeys_navigate },\n    { mode = 'n', keys = '<C-w>c',      desc = 'Close' },\n    { mode = 'n', keys = '<C-w>d',      desc = 'Split + jump to definition' },\n    { mode = 'n', keys = '<C-w>F',      desc = 'Split + edit file name + jump' },\n    { mode = 'n', keys = '<C-w>f',      desc = 'Split + edit file name' },\n    { mode = 'n', keys = '<C-w>g',      desc = '+Extra actions' },\n    { mode = 'n', keys = '<C-w>g]',     desc = 'Split + list tags' },\n    { mode = 'n', keys = '<C-w>g}',     desc = 'Do `:ptjump`' },\n    { mode = 'n', keys = '<C-w>g<C-]>', desc = 'Split + jump to tag with `:tjump`' },\n    { mode = 'n', keys = '<C-w>g<Tab>', desc = 'Focus last accessed tab', postkeys = postkeys_navigate },\n    { mode = 'n', keys = '<C-w>gF',     desc = 'New tabpage + edit file name + jump' },\n    { mode = 'n', keys = '<C-w>gf',     desc = 'New tabpage + edit file name' },\n    { mode = 'n', keys = '<C-w>gT',     desc = 'Focus previous tabpage',  postkeys = postkeys_navigate },\n    { mode = 'n', keys = '<C-w>gt',     desc = 'Focus next tabpage',      postkeys = postkeys_navigate },\n    { mode = 'n', keys = '<C-w>H',      desc = 'Move to very left',       postkeys = postkeys_move },\n    { mode = 'n', keys = '<C-w>h',      desc = 'Focus left',              postkeys = postkeys_navigate },\n    { mode = 'n', keys = '<C-w>i',      desc = 'Split + jump to declaration' },\n    { mode = 'n', keys = '<C-w>J',      desc = 'Move to very bottom',     postkeys = postkeys_move },\n    { mode = 'n', keys = '<C-w>j',      desc = 'Focus down',              postkeys = postkeys_navigate },\n    { mode = 'n', keys = '<C-w>K',      desc = 'Move to very top',        postkeys = postkeys_move },\n    { mode = 'n', keys = '<C-w>k',      desc = 'Focus up',                postkeys = postkeys_navigate },\n    { mode = 'n', keys = '<C-w>L',      desc = 'Move to very right',      postkeys = postkeys_move },\n    { mode = 'n', keys = '<C-w>l',      desc = 'Focus right',             postkeys = postkeys_navigate },\n    { mode = 'n', keys = '<C-w>n',      desc = 'Open new' },\n    { mode = 'n', keys = '<C-w>o',      desc = 'Close all but current' },\n    { mode = 'n', keys = '<C-w>P',      desc = 'Focus preview',           postkeys = postkeys_navigate },\n    { mode = 'n', keys = '<C-w>p',      desc = 'Focus last accessed',     postkeys = postkeys_navigate },\n    { mode = 'n', keys = '<C-w>q',      desc = 'Quit current' },\n    { mode = 'n', keys = '<C-w>R',      desc = 'Rotate up/left',          postkeys = postkeys_move },\n    { mode = 'n', keys = '<C-w>r',      desc = 'Rotate down/right',       postkeys = postkeys_move },\n    { mode = 'n', keys = '<C-w>s',      desc = 'Split horizontally' },\n    { mode = 'n', keys = '<C-w>T',      desc = 'Create new tabpage + move' },\n    { mode = 'n', keys = '<C-w>t',      desc = 'Focus top',               postkeys = postkeys_navigate },\n    { mode = 'n', keys = '<C-w>v',      desc = 'Split vertically' },\n    { mode = 'n', keys = '<C-w>W',      desc = 'Focus previous',          postkeys = postkeys_navigate },\n    { mode = 'n', keys = '<C-w>w',      desc = 'Focus next',              postkeys = postkeys_navigate },\n    { mode = 'n', keys = '<C-w>x',      desc = 'Exchange windows',        postkeys = postkeys_move },\n    { mode = 'n', keys = '<C-w>z',      desc = 'Close preview' },\n  }\nend\n\n--- Generate clues for `z` key\n---\n--- Contains clues for the following triggers: >\n---\n---   { mode = 'n', keys = 'z' }\n---   { mode = 'x', keys = 'z' }\n---\n---@return table Array of clues.\nMiniClue.gen_clues.z = function()\n  --stylua: ignore\n  return {\n    { mode = 'n', keys = 'zA',   desc = 'Toggle folds recursively' },\n    { mode = 'n', keys = 'za',   desc = 'Toggle fold' },\n    { mode = 'n', keys = 'zb',   desc = 'Redraw at bottom' },\n    { mode = 'n', keys = 'zC',   desc = 'Close folds recursively' },\n    { mode = 'n', keys = 'zc',   desc = 'Close fold' },\n    { mode = 'n', keys = 'zD',   desc = 'Delete folds recursively' },\n    { mode = 'n', keys = 'zd',   desc = 'Delete fold' },\n    { mode = 'n', keys = 'zE',   desc = 'Eliminate all folds' },\n    { mode = 'n', keys = 'ze',   desc = 'Scroll to cursor on right screen side' },\n    { mode = 'n', keys = 'zF',   desc = 'Create fold' },\n    { mode = 'n', keys = 'zf',   desc = 'Create fold (operator)' },\n    { mode = 'n', keys = 'zG',   desc = 'Temporarily mark as correctly spelled' },\n    { mode = 'n', keys = 'zg',   desc = 'Permanently mark as correctly spelled' },\n    { mode = 'n', keys = 'zH',   desc = 'Scroll left half screen' },\n    { mode = 'n', keys = 'zh',   desc = 'Scroll left' },\n    { mode = 'n', keys = 'zi',   desc = \"Toggle 'foldenable'\" },\n    { mode = 'n', keys = 'zj',   desc = 'Move to start of next fold' },\n    { mode = 'n', keys = 'zk',   desc = 'Move to end of previous fold' },\n    { mode = 'n', keys = 'zL',   desc = 'Scroll right half screen' },\n    { mode = 'n', keys = 'zl',   desc = 'Scroll right' },\n    { mode = 'n', keys = 'zM',   desc = 'Close all folds' },\n    { mode = 'n', keys = 'zm',   desc = 'Fold more' },\n    { mode = 'n', keys = 'zN',   desc = \"Set 'foldenable'\" },\n    { mode = 'n', keys = 'zn',   desc = \"Reset 'foldenable'\" },\n    { mode = 'n', keys = 'zO',   desc = 'Open folds recursively' },\n    { mode = 'n', keys = 'zo',   desc = 'Open fold' },\n    { mode = 'n', keys = 'zP',   desc = 'Paste without trailspace' },\n    { mode = 'n', keys = 'zp',   desc = 'Paste without trailspace' },\n    { mode = 'n', keys = 'zR',   desc = 'Open all folds' },\n    { mode = 'n', keys = 'zr',   desc = 'Fold less' },\n    { mode = 'n', keys = 'zs',   desc = 'Scroll to cursor on left screen side' },\n    { mode = 'n', keys = 'zt',   desc = 'Redraw at top' },\n    { mode = 'n', keys = 'zu',   desc = '+Undo spelling commands' },\n    { mode = 'n', keys = 'zug',  desc = 'Undo `zg`' },\n    { mode = 'n', keys = 'zuG',  desc = 'Undo `zG`' },\n    { mode = 'n', keys = 'zuw',  desc = 'Undo `zw`' },\n    { mode = 'n', keys = 'zuW',  desc = 'Undo `zW`' },\n    { mode = 'n', keys = 'zv',   desc = 'Open enough folds' },\n    { mode = 'n', keys = 'zW',   desc = 'Temporarily mark as incorrectly spelled' },\n    { mode = 'n', keys = 'zw',   desc = 'Permanently mark as incorrectly spelled' },\n    { mode = 'n', keys = 'zX',   desc = 'Update folds' },\n    { mode = 'n', keys = 'zx',   desc = 'Update folds + open enough folds' },\n    { mode = 'n', keys = 'zy',   desc = 'Yank without trailing spaces (operator)' },\n    { mode = 'n', keys = 'zz',   desc = 'Redraw at center' },\n    { mode = 'n', keys = 'z+',   desc = 'Redraw under bottom at top' },\n    { mode = 'n', keys = 'z-',   desc = 'Redraw at bottom + cursor on first non-blank' },\n    { mode = 'n', keys = 'z.',   desc = 'Redraw at center + cursor on first non-blank' },\n    { mode = 'n', keys = 'z=',   desc = 'Show spelling suggestions' },\n    { mode = 'n', keys = 'z^',   desc = 'Redraw above top at bottom' },\n\n    { mode = 'x', keys = 'zf',   desc = 'Create fold from selection' },\n  }\nend\n\n-- Helper data ================================================================\n-- Module default config\nH.default_config = vim.deepcopy(MiniClue.config)\n\n-- Namespaces\nH.ns_id = {\n  highlight = vim.api.nvim_create_namespace('MiniClueHighlight'),\n}\n\n-- State of user input\nH.state = {\n  trigger = nil,\n  -- Array of raw keys\n  query = {},\n  clues = {},\n  timer = vim.loop.new_timer(),\n  buf_id = nil,\n  win_id = nil,\n  is_after_postkeys = false,\n}\n\n-- Default window config\nH.default_win_config = {\n  anchor = 'SE',\n  border = 'single',\n  focusable = false,\n  relative = 'editor',\n  style = 'minimal',\n  width = 30,\n  zindex = 99,\n}\n\n-- Precomputed raw keys\nH.keys = {\n  -- bs = vim.api.nvim_replace_termcodes('<BS>', true, true, true),\n  -- cr = vim.api.nvim_replace_termcodes('<CR>', true, true, true),\n  exit = vim.api.nvim_replace_termcodes([[<C-\\><C-n>]], true, true, true),\n  ctrl_d = vim.api.nvim_replace_termcodes('<C-d>', true, true, true),\n  ctrl_u = vim.api.nvim_replace_termcodes('<C-u>', true, true, true),\n}\n\n-- Timers\nH.timers = {\n  getcharstr = vim.loop.new_timer(),\n}\n\n-- Undo command which depends on Neovim version\nH.undo_autocommand = 'au ModeChanged * ++once undo' .. (vim.fn.has('nvim-0.8') == 1 and '!' or '')\n\n-- Helper functionality =======================================================\n-- Settings -------------------------------------------------------------------\nH.setup_config = function(config)\n  -- General idea: if some table elements are not present in user-supplied\n  -- `config`, take them from default config\n  vim.validate({ config = { config, 'table', true } })\n  config = vim.tbl_deep_extend('force', vim.deepcopy(H.default_config), config or {})\n\n  vim.validate({\n    clues = { config.clues, 'table' },\n    triggers = { config.triggers, 'table' },\n    window = { config.window, 'table' },\n  })\n\n  local is_table_or_callable = function(x) return type(x) == 'table' or vim.is_callable(x) end\n  vim.validate({\n    ['window.delay'] = { config.window.delay, 'number' },\n    ['window.config'] = { config.window.config, is_table_or_callable, 'table or callable' },\n    ['window.scroll_down'] = { config.window.scroll_down, 'string' },\n    ['window.scroll_up'] = { config.window.scroll_up, 'string' },\n  })\n\n  return config\nend\n\nH.apply_config = function(config)\n  MiniClue.config = config\n\n  -- Create trigger keymaps for all existing buffers\n  MiniClue.enable_all_triggers()\n\n  -- Tweak macro execution\n  local macro_keymap_opts = { nowait = true, desc = \"Execute macro without 'mini.clue' triggers\" }\n  local exec_macro = function(keys)\n    local register = H.getcharstr()\n    if register == nil then return end\n    MiniClue.disable_all_triggers()\n    vim.schedule(MiniClue.enable_all_triggers)\n    pcall(vim.api.nvim_feedkeys, vim.v.count1 .. '@' .. register, 'nx', false)\n  end\n  vim.keymap.set('n', '@', exec_macro, macro_keymap_opts)\n\n  local exec_latest_macro = function(keys)\n    MiniClue.disable_all_triggers()\n    vim.schedule(MiniClue.enable_all_triggers)\n    vim.api.nvim_feedkeys(vim.v.count1 .. 'Q', 'nx', false)\n  end\n  vim.keymap.set('n', 'Q', exec_latest_macro, macro_keymap_opts)\nend\n\nH.is_disabled = function(buf_id)\n  local buf_disable = H.get_buf_var(buf_id, 'miniclue_disable')\n  return vim.g.miniclue_disable == true or buf_disable == true\nend\n\nH.create_autocommands = function(config)\n  local augroup = vim.api.nvim_create_augroup('MiniClue', {})\n\n  local au = function(event, pattern, callback, desc)\n    vim.api.nvim_create_autocmd(event, { group = augroup, pattern = pattern, callback = callback, desc = desc })\n  end\n\n  -- Ensure buffer-local mappings for triggers are the latest ones to fully\n  -- utilize `<nowait>`. Use `vim.schedule_wrap` to allow other events to\n  -- create `vim.b.miniclue_config` and `vim.b.miniclue_disable`.\n  local ensure_triggers = vim.schedule_wrap(function(data)\n    if not H.is_valid_buf(data.buf) then return end\n    MiniClue.ensure_buf_triggers(data.buf)\n  end)\n  -- - Respect `LspAttach` as it is a common source of buffer-local mappings\n  local events = vim.fn.has('nvim-0.8') == 1 and { 'BufAdd', 'LspAttach' } or { 'BufAdd' }\n  au(events, '*', ensure_triggers, 'Ensure buffer-local trigger keymaps')\n\n  -- Disable all triggers when recording macro as they interfere with what is\n  -- actually recorded\n  au('RecordingEnter', '*', MiniClue.disable_all_triggers, 'Disable all triggers')\n  au('RecordingLeave', '*', MiniClue.enable_all_triggers, 'Enable all triggers')\n\n  au('VimResized', '*', H.window_update, 'Update window on resize')\nend\n\n--stylua: ignore\nH.create_default_hl = function()\n  local hi = function(name, opts)\n    opts.default = true\n    vim.api.nvim_set_hl(0, name, opts)\n  end\n\n  hi('MiniClueBorder',              { link = 'FloatBorder' })\n  hi('MiniClueDescGroup',           { link = 'DiagnosticFloatingWarn' })\n  hi('MiniClueDescSingle',          { link = 'NormalFloat' })\n  hi('MiniClueNextKey',             { link = 'DiagnosticFloatingHint' })\n  hi('MiniClueNextKeyWithPostkeys', { link = 'DiagnosticFloatingError' })\n  hi('MiniClueSeparator',           { link = 'DiagnosticFloatingInfo' })\n  hi('MiniClueTitle',               { link = 'FloatTitle' })\nend\n\nH.get_config = function(config, buf_id)\n  config = config or {}\n  local buf_config = H.get_buf_var(buf_id, 'miniclue_config') or {}\n  local global_config = MiniClue.config\n\n  -- Manually reconstruct to allow array elements to be concatenated\n  local res = {\n    clues = H.list_concat(global_config.clues, buf_config.clues, config.clues),\n    triggers = H.list_concat(global_config.triggers, buf_config.triggers, config.triggers),\n    window = vim.tbl_deep_extend('force', global_config.window, buf_config.window or {}, config.window or {}),\n  }\n  return res\nend\n\nH.get_buf_var = function(buf_id, name)\n  buf_id = buf_id or vim.api.nvim_get_current_buf()\n  if not H.is_valid_buf(buf_id) then return nil end\n  return vim.b[buf_id][name]\nend\n\n-- Triggers -------------------------------------------------------------------\nH.map_buf_triggers = function(buf_id)\n  if not H.is_valid_buf(buf_id) or H.is_disabled(buf_id) then return end\n\n  for _, trigger in ipairs(H.get_config(nil, buf_id).triggers) do\n    H.map_trigger(buf_id, trigger)\n  end\nend\n\nH.unmap_buf_triggers = function(buf_id)\n  if not H.is_valid_buf(buf_id) or H.is_disabled(buf_id) then return end\n\n  for _, trigger in ipairs(H.get_config(nil, buf_id).triggers) do\n    H.unmap_trigger(buf_id, trigger)\n  end\nend\n\nH.map_trigger = function(buf_id, trigger)\n  if not H.is_valid_buf(buf_id) then return end\n\n  -- Compute mapping RHS\n  trigger.keys = H.replace_termcodes(trigger.keys)\n  local keys_trans = H.keytrans(trigger.keys)\n\n  local rhs = function()\n    -- Don't act if for some reason entered the same trigger during state exec\n    local is_in_exec = type(H.exec_trigger) == 'table'\n      and H.exec_trigger.mode == trigger.mode\n      and H.exec_trigger.keys == trigger.keys\n    if is_in_exec then\n      H.exec_trigger = nil\n      return\n    end\n\n    -- Start user query\n    H.state_set(trigger, { trigger.keys })\n\n    -- Do not advance if no other clues to query. NOTE: it is `<= 1` and not\n    -- `<= 0` because the \"init query\" mapping should match.\n    if vim.tbl_count(H.state.clues) <= 1 then return H.state_exec() end\n\n    H.state_advance()\n  end\n\n  -- Use buffer-local mappings and `nowait` to make it a primary source of\n  -- keymap execution\n  local desc = string.format('Query keys after \"%s\"', keys_trans)\n  local opts = { buffer = buf_id, nowait = true, desc = desc }\n\n  -- Create mapping. Use translated variant to make it work with <F*> keys.\n  vim.keymap.set(trigger.mode, keys_trans, rhs, opts)\nend\n\nH.unmap_trigger = function(buf_id, trigger)\n  if not H.is_valid_buf(buf_id) then return end\n  pcall(vim.keymap.del, trigger.mode, H.keytrans(trigger.keys), { buffer = buf_id })\nend\n\n-- State ----------------------------------------------------------------------\nH.state_advance = function(opts)\n  opts = opts or {}\n  local config_window = H.get_config().window\n\n  -- Show clues: delay (debounce) first show; update immediately if shown or\n  -- after postkeys (for visual feedback that extra key is needed to stop)\n  H.state.timer:stop()\n  local show_immediately = H.is_valid_win(H.state.win_id) or H.state.is_after_postkeys\n  local delay = show_immediately and 0 or config_window.delay\n  H.state.timer:start(delay, 0, function() H.window_update(opts.same_content) end)\n\n  -- Reset postkeys right now to not flicker when trying to close window during\n  -- \"not querying\" check\n  H.state.is_after_postkeys = false\n\n  -- Query user for new key\n  local key = H.getcharstr()\n\n  -- Handle key\n  if key == nil then return H.state_reset() end\n\n  if key == H.keys.cr then return H.state_exec() end\n\n  local is_window_shown = H.is_valid_win(H.state.win_id)\n  local is_scroll_down = key == H.replace_termcodes(config_window.scroll_down)\n  local is_scroll_up = key == H.replace_termcodes(config_window.scroll_up)\n  if is_window_shown and (is_scroll_down or is_scroll_up) then\n    H.window_scroll(is_scroll_down)\n    return H.state_advance({ same_content = true })\n  end\n\n  if key == H.keys.bs then\n    H.state_pop()\n  else\n    H.state_push(key)\n  end\n\n  -- Advance state\n  -- - Execute if reached single target keymap\n  if H.state_is_at_target() then return H.state_exec() end\n\n  -- - Reset if there are no keys (like after `<BS>`)\n  if #H.state.query == 0 then return H.state_reset() end\n\n  -- - Query user for more information if there is not enough\n  --   NOTE: still advance even if there is single clue because it is still not\n  --   a target but can be one.\n  if vim.tbl_count(H.state.clues) >= 1 then return H.state_advance() end\n\n  -- - Fall back for executing what user typed\n  H.state_exec()\nend\n\nH.state_set = function(trigger, query)\n  H.state.trigger = trigger\n  H.state.query = query\n  H.state.clues = H.clues_filter(H.clues_get_all(trigger.mode), query)\nend\n\nH.state_reset = function(keep_window)\n  H.state.trigger = nil\n  H.state.query = {}\n  H.state.clues = {}\n  H.state.is_after_postkeys = false\n\n  H.state.timer:stop()\n  if not keep_window then H.window_close() end\nend\n\nH.state_exec = function()\n  -- Compute keys to type\n  local keys_to_type = H.compute_exec_keys()\n\n  -- Add extra (redundant) safety flag to try to avoid infinite recursion\n  local trigger, clue = H.state.trigger, H.state_get_query_clue()\n  H.exec_trigger = trigger\n  vim.schedule(function() H.exec_trigger = nil end)\n\n  -- Reset state\n  local has_postkeys = (clue or {}).postkeys ~= nil\n  H.state_reset(has_postkeys)\n\n  -- Disable trigger !!!VERY IMPORTANT!!!\n  -- This is a workaround against infinite recursion (like if `g` is trigger\n  -- then typing `gg`/`g~` would introduce infinite recursion).\n  local buf_id = vim.api.nvim_get_current_buf()\n  H.unmap_trigger(buf_id, trigger)\n\n  -- Execute keys. The `i` flag is used to fully support Operator-pending mode.\n  -- Flag `t` imitates keys as if user typed, which is reasonable but has small\n  -- downside with edge cases of 'langmap' (like ':\\;;\\;:') as it \"inverts\" key\n  -- meaning second time (at least in Normal mode).\n  vim.api.nvim_feedkeys(keys_to_type, 'mit', false)\n\n  -- Enable trigger back after it can no longer harm\n  vim.schedule(function() H.map_trigger(buf_id, trigger) end)\n\n  -- Apply postkeys (in scheduled fashion)\n  if has_postkeys then H.state_apply_postkeys(clue.postkeys) end\nend\n\nH.state_push = function(keys)\n  table.insert(H.state.query, keys)\n  H.state.clues = H.clues_filter(H.state.clues, H.state.query)\nend\n\nH.state_pop = function()\n  H.state.query[#H.state.query] = nil\n  H.state.clues = H.clues_filter(H.clues_get_all(H.state.trigger.mode), H.state.query)\nend\n\nH.state_apply_postkeys = vim.schedule_wrap(function(postkeys)\n  -- Register that possible future querying is a result of postkeys.\n  -- This enables (keep) showing window immediately.\n  H.state.is_after_postkeys = true\n\n  -- Use `nvim_feedkeys()` because using `state_set()` and\n  -- `state_advance()` directly does not work: it doesn't guarantee to be\n  -- executed **after** keys from `nvim_feedkeys()`.\n  vim.api.nvim_feedkeys(postkeys, 'mit', false)\n\n  -- Defer check of whether postkeys resulted into window.\n  -- Could not find proper way to check this which guarantees to be executed\n  -- after `nvim_feedkeys()` takes effect **end** doesn't result into flicker\n  -- when consecutively applying \"submode\" keys.\n  vim.defer_fn(function()\n    if #H.state.query == 0 then H.window_close() end\n  end, 50)\nend)\n\nH.state_is_at_target = function()\n  return vim.tbl_count(H.state.clues) == 1 and H.state.clues[H.query_to_keys(H.state.query)] ~= nil\nend\n\nH.state_get_query_clue = function()\n  local keys = H.query_to_keys(H.state.query)\n  return H.state.clues[keys]\nend\n\nH.compute_exec_keys = function()\n  local keys_count = vim.v.count > 0 and vim.v.count or ''\n  local keys_query = H.query_to_keys(H.state.query)\n  local res = keys_count .. keys_query\n\n  local cur_mode = vim.fn.mode(1)\n\n  -- Using `feedkeys()` inside Operator-pending mode leads to its cancel into\n  -- Normal/Insert mode so extra work should be done to rebuild all keys\n  if vim.startswith(cur_mode, 'no') then\n    local operator_tweak = H.operator_tweaks[vim.v.operator] or function(x) return x end\n    res = operator_tweak(vim.v.operator .. H.get_forced_submode() .. res)\n  elseif not vim.startswith(cur_mode, 'i') and H.get_default_register() ~= vim.v.register then\n    -- Force non-default register but not in Insert mode\n    res = '\"' .. vim.v.register .. res\n  end\n\n  -- `feedkeys()` inside \"temporary\" Normal mode is executed **after** it is\n  -- already back from Normal mode. Go into it again with `<C-o>` ('\\15').\n  -- NOTE: This only works when Normal mode trigger is triggered in\n  -- \"temporary\" Normal mode. Still doesn't work when Operator-pending mode is\n  -- triggered afterwards (like in `<C-o>gUiw` with 'i' as trigger).\n  if cur_mode:find('^ni') ~= nil then res = '\\15' .. res end\n\n  return res\nend\n\n-- Some operators needs special tweaking due to their nature:\n-- - Some operators perform on register. Solution: add register explicitly.\n-- - Some operators end up changing mode which affects `feedkeys()`.\n--   Solution: explicitly exit to Normal mode with '<C-\\><C-n>'.\n-- - Some operators still perform some redundant operation before `feedkeys()`\n--   takes effect. Solution: add one-shot autocommand undoing that.\nH.operator_tweaks = {\n  ['c'] = function(keys)\n    -- Doing '<C-\\><C-n>' moves cursor one space to left (same as `i<Esc>`).\n    -- Solution: add one-shot autocommand correcting cursor position.\n    vim.cmd('au InsertLeave * ++once normal! l')\n    return H.keys.exit .. '\"' .. vim.v.register .. keys\n  end,\n  ['d'] = function(keys) return '\"' .. vim.v.register .. keys end,\n  ['y'] = function(keys) return '\"' .. vim.v.register .. keys end,\n  ['~'] = function(keys)\n    if vim.fn.col('.') == 1 then vim.cmd(H.undo_autocommand) end\n    return keys\n  end,\n  ['g~'] = function(keys)\n    if vim.fn.col('.') == 1 then vim.cmd(H.undo_autocommand) end\n    return keys\n  end,\n  ['g?'] = function(keys)\n    if vim.fn.col('.') == 1 then vim.cmd(H.undo_autocommand) end\n    return keys\n  end,\n  ['!'] = function(keys) return H.keys.exit .. keys end,\n  ['>'] = function(keys)\n    vim.cmd(H.undo_autocommand)\n    return keys\n  end,\n  ['<'] = function(keys)\n    vim.cmd(H.undo_autocommand)\n    return keys\n  end,\n  ['g@'] = function(keys)\n    -- Cancelling in-process `g@` operator seems to be particularly hard.\n    -- Not even sure why specifically this combination works, but having `x`\n    -- flag in `feedkeys()` is crucial.\n    vim.api.nvim_feedkeys(H.keys.exit, 'nx', false)\n    return H.keys.exit .. keys\n  end,\n}\n\nH.query_to_keys = function(query) return table.concat(query, '') end\n\nH.query_to_title = function(query) return H.keytrans(H.query_to_keys(query)) end\n\n-- Window ---------------------------------------------------------------------\nH.window_update = vim.schedule_wrap(function(same_content)\n  -- Make sure that outdated windows are not shown\n  if #H.state.query == 0 then return H.window_close() end\n  local win_id = H.state.win_id\n\n  -- Close window if it is not in current tabpage (as only window is tracked)\n  local is_different_tabpage = H.is_valid_win(win_id)\n    and vim.api.nvim_win_get_tabpage(win_id) ~= vim.api.nvim_get_current_tabpage()\n  if is_different_tabpage then H.window_close() end\n\n  -- Create-update buffer showing clues\n  if not same_content then H.state.buf_id = H.buffer_update() end\n\n  -- Create-update window showing buffer\n  local win_config = H.window_get_config()\n  if not H.is_valid_win(win_id) then\n    win_config.noautocmd = true\n    win_id = H.window_open(win_config)\n    H.state.win_id = win_id\n  else\n    vim.api.nvim_win_set_config(win_id, win_config)\n    vim.wo[win_id].list = true\n  end\n\n  -- Make scroll not persist. NOTE: Don't use 'normal! gg' inside target window\n  -- as it resets `v:count` and `v:register` which results into invalid keys\n  -- reproduction in Operator-pending mode.\n  if not same_content then vim.api.nvim_win_set_cursor(win_id, { 1, 0 }) end\n\n  -- Add redraw because Neovim won't do it when `getcharstr()` is active\n  vim.cmd('redraw')\nend)\n\nH.window_scroll = function(is_scroll_down)\n  local scroll_key = is_scroll_down and H.keys.ctrl_d or H.keys.ctrl_u\n  local f = function()\n    local cache_scroll, bot_line, n_lines = vim.wo.scroll, vim.fn.line('w$'), vim.api.nvim_buf_line_count(0)\n    -- Do not scroll past the end of buffer\n    local scroll_count = is_scroll_down and math.min(cache_scroll, n_lines - bot_line) or cache_scroll\n    if scroll_count > 0 then pcall(vim.cmd, 'normal! ' .. scroll_count .. scroll_key) end\n    vim.wo.scroll = cache_scroll\n  end\n  vim.api.nvim_win_call(H.state.win_id, f)\nend\n\nH.window_open = function(config)\n  local win_id = vim.api.nvim_open_win(H.state.buf_id, false, config)\n\n  vim.wo[win_id].foldenable = false\n  vim.wo[win_id].wrap = false\n  vim.wo[win_id].list = true\n  vim.wo[win_id].listchars = 'extends:…'\n\n  -- Neovim=0.7 doesn't support invalid highlight groups in 'winhighlight'\n  local win_hl = 'FloatBorder:MiniClueBorder' .. (vim.fn.has('nvim-0.8') == 1 and ',FloatTitle:MiniClueTitle' or '')\n  vim.wo[win_id].winhighlight = win_hl\n\n  return win_id\nend\n\nH.window_close = function()\n  -- Closing floating window when Command-line window is active is not allowed\n  -- on Neovim<0.10. Make sure it is closed after leaving it.\n  -- See https://github.com/neovim/neovim/issues/24452\n  local win_id = H.state.win_id\n  if vim.fn.has('nvim-0.10') == 0 and vim.fn.getcmdwintype() ~= '' then\n    vim.api.nvim_create_autocmd(\n      'CmdwinLeave',\n      { once = true, callback = function() pcall(vim.api.nvim_win_close, win_id, true) end }\n    )\n    return\n  else\n    pcall(vim.api.nvim_win_close, win_id, true)\n  end\n\n  H.state.win_id = nil\nend\n\nH.window_get_config = function()\n  local has_statusline = vim.o.laststatus > 0\n  local has_tabline = vim.o.showtabline == 2 or (vim.o.showtabline == 1 and #vim.api.nvim_list_tabpages() > 1)\n  -- Remove 2 from maximum height to account for top and bottom borders\n  local max_height = vim.o.lines - vim.o.cmdheight - (has_tabline and 1 or 0) - (has_statusline and 1 or 0) - 2\n\n  local buf_id = H.state.buf_id\n  local cur_config_fields = {\n    row = vim.o.lines - vim.o.cmdheight - (has_statusline and 1 or 0),\n    col = vim.o.columns,\n    height = math.min(vim.api.nvim_buf_line_count(buf_id), max_height),\n    title = H.query_to_title(H.state.query),\n  }\n  local user_config = H.expand_callable(H.get_config().window.config, buf_id) or {}\n  local res = vim.tbl_deep_extend('force', H.default_win_config, cur_config_fields, user_config)\n\n  -- Tweak \"auto\" fields\n  if res.width == 'auto' then res.width = H.buffer_get_width() + 1 end\n  res.width = math.min(res.width, vim.o.columns)\n\n  if res.row == 'auto' then\n    local is_on_top = res.anchor == 'NW' or res.anchor == 'NE'\n    res.row = is_on_top and (has_tabline and 1 or 0) or cur_config_fields.row\n  end\n\n  if res.col == 'auto' then\n    local is_on_left = res.anchor == 'NW' or res.anchor == 'SW'\n    res.col = is_on_left and 0 or cur_config_fields.col\n  end\n\n  -- Ensure it works on Neovim<0.9\n  if vim.fn.has('nvim-0.9') == 0 then res.title = nil end\n\n  return res\nend\n\n-- Buffer ---------------------------------------------------------------------\nH.buffer_update = function()\n  local buf_id = H.state.buf_id\n  if not H.is_valid_buf(buf_id) then buf_id = vim.api.nvim_create_buf(false, true) end\n\n  -- Compute content data\n  local keys = H.query_to_keys(H.state.query)\n  local content = H.clues_to_buffer_content(H.state.clues, keys)\n\n  -- Add lines\n  local lines = {}\n  for _, line_content in ipairs(content) do\n    table.insert(lines, string.format(' %s │ %s', line_content.next_key, line_content.desc))\n  end\n  vim.api.nvim_buf_set_lines(buf_id, 0, -1, false, lines)\n\n  -- Add highlighting\n  local ns_id = H.ns_id.highlight\n  vim.api.nvim_buf_clear_namespace(buf_id, ns_id, 0, -1)\n\n  local set_hl = function(hl_group, line_from, col_from, line_to, col_to)\n    local opts = { end_row = line_to, end_col = col_to, hl_group = hl_group, hl_eol = true }\n    vim.api.nvim_buf_set_extmark(buf_id, ns_id, line_from, col_from, opts)\n  end\n\n  for i, line_content in ipairs(content) do\n    local sep_start = line_content.next_key:len() + 3\n    local next_key_hl_group = line_content.has_postkeys and 'MiniClueNextKeyWithPostkeys' or 'MiniClueNextKey'\n    set_hl(next_key_hl_group, i - 1, 0, i - 1, sep_start - 1)\n\n    -- NOTE: Separator '│' is 3 bytes long\n    set_hl('MiniClueSeparator', i - 1, sep_start - 1, i - 1, sep_start + 2)\n\n    local desc_hl_group = line_content.is_group and 'MiniClueDescGroup' or 'MiniClueDescSingle'\n    set_hl(desc_hl_group, i - 1, sep_start + 2, i, 0)\n  end\n\n  return buf_id\nend\n\nH.buffer_get_width = function()\n  if not H.is_valid_buf(H.state.buf_id) then return end\n  local lines = vim.api.nvim_buf_get_lines(H.state.buf_id, 0, -1, false)\n  local res = 0\n  for _, l in ipairs(lines) do\n    res = math.max(res, vim.fn.strdisplaywidth(l))\n  end\n  return res\nend\n\n-- Clues ----------------------------------------------------------------------\nH.clues_get_all = function(mode)\n  local res = {}\n\n  -- Order of clue precedence: config clues < buffer mappings < global mappings\n  local config_clues = H.clues_normalize(H.get_config().clues) or {}\n  local mode_clues = vim.tbl_filter(function(x) return x.mode == mode end, config_clues)\n  for _, clue in ipairs(mode_clues) do\n    local lhsraw = H.replace_termcodes(clue.keys)\n\n    local res_data = res[lhsraw] or {}\n\n    -- - Allow callable clue description\n    local desc = H.expand_callable(clue.desc)\n    -- - Fall back to possibly already present fields to allow partial\n    --   overwrite in later clues. Like to add `postkeys` and inherit `desc`.\n    res_data.desc = desc or res_data.desc\n    res_data.postkeys = H.replace_termcodes(clue.postkeys) or res_data.postkeys\n\n    res[lhsraw] = res_data\n  end\n\n  for _, map_data in ipairs(vim.api.nvim_get_keymap(mode)) do\n    local lhsraw = H.replace_termcodes(map_data.lhs)\n    local res_data = res[lhsraw] or {}\n    res_data.desc = map_data.desc or ''\n    res[lhsraw] = res_data\n  end\n\n  for _, map_data in ipairs(vim.api.nvim_buf_get_keymap(0, mode)) do\n    local lhsraw = H.replace_termcodes(map_data.lhs)\n    local res_data = res[lhsraw] or {}\n    res_data.desc = map_data.desc or ''\n    res[lhsraw] = res_data\n  end\n\n  return res\nend\n\nH.clues_normalize = function(clues)\n  local res = {}\n  local process\n  process = function(x)\n    x = H.expand_callable(x)\n    if H.is_clue(x) then return table.insert(res, x) end\n    if not H.islist(x) then return nil end\n    for _, y in ipairs(x) do\n      process(y)\n    end\n  end\n\n  process(clues)\n  return res\nend\n\nH.clues_filter = function(clues, query)\n  local keys = H.query_to_keys(query)\n  for clue_keys, _ in pairs(clues) do\n    if not vim.startswith(clue_keys, keys) then clues[clue_keys] = nil end\n  end\n  return clues\nend\n\nH.clues_to_buffer_content = function(clues, keys)\n  -- Use translated keys to properly handle cases like `<Del>`, `<End>`, etc.\n  keys = H.keytrans(keys)\n\n  -- Gather clue data\n  local keys_len = keys:len()\n  local keys_pattern = string.format('^%s(.+)$', vim.pesc(keys))\n\n  local next_key_data, next_key_max_width = {}, 0\n  for clue_keys, clue_data in pairs(clues) do\n    local left, _, rest_keys = H.keytrans(clue_keys):find(keys_pattern)\n\n    -- Add non-trivial next key data only if clue matches current keys plus\n    -- something more\n    if left ~= nil then\n      local next_key = H.clues_get_first_key(rest_keys)\n\n      -- Update description data\n      local data = next_key_data[next_key] or {}\n      data.n_choices = (data.n_choices or 0) + 1\n\n      -- - Add description directly if it is group clue with description or\n      --   a non-group clue\n      if next_key == rest_keys then\n        data.desc = clue_data.desc or ''\n        data.has_postkeys = clue_data.postkeys ~= nil\n      end\n\n      next_key_data[next_key] = data\n\n      -- Update width data\n      local next_key_width = vim.fn.strchars(next_key)\n      data.next_key_width = next_key_width\n      next_key_max_width = math.max(next_key_max_width, next_key_width)\n    end\n  end\n\n  -- Convert to array sorted by keys and finalize content\n  local next_keys_extra = vim.tbl_map(\n    function(x) return { key = x, keytype = H.clues_get_next_key_type(x) } end,\n    vim.tbl_keys(next_key_data)\n  )\n  table.sort(next_keys_extra, H.clues_compare_next_key)\n  local next_keys = vim.tbl_map(function(x) return x.key end, next_keys_extra)\n\n  local res = {}\n  for _, key in ipairs(next_keys) do\n    local data = next_key_data[key]\n    local is_group = data.n_choices > 1\n    local desc = data.desc or string.format('+%d choice%s', data.n_choices, is_group and 's' or '')\n    local next_key = key .. string.rep(' ', next_key_max_width - data.next_key_width)\n    table.insert(res, { next_key = next_key, desc = desc, is_group = is_group, has_postkeys = data.has_postkeys })\n  end\n\n  return res\nend\n\nH.clues_get_first_key = function(keys)\n  -- `keys` are assumed to be translated\n  -- Special keys\n  local special = keys:match('^(%b<>)')\n  if special ~= nil then return special end\n\n  -- <\n  if keys:find('^<') ~= nil then return '<' end\n\n  -- Other characters\n  return vim.fn.strcharpart(keys, 0, 1)\nend\n\nH.clues_get_next_key_type = function(x)\n  if x:find('^%w$') ~= nil then return 'alphanum' end\n  if x:find('^<.*>$') ~= nil then return 'mod' end\n  return 'other'\nend\n\nH.clues_compare_next_key = function(a, b)\n  local a_type, b_type = a.keytype, b.keytype\n  if a_type == b_type then\n    local cmp = vim.stricmp(a.key, b.key)\n    return cmp == -1 or (cmp == 0 and a.key < b.key)\n  end\n\n  if a_type == 'alphanum' then return true end\n  if b_type == 'alphanum' then return false end\n\n  if a_type == 'mod' then return true end\n  if b_type == 'mod' then return false end\nend\n\n-- Clue generators ------------------------------------------------------------\nH.make_clues_with_register_contents = function(mode, prefix)\n  local make_register_desc = function(register)\n    return function()\n      local ok, value = pcall(vim.fn.getreg, register, 1)\n      if not ok or value == '' then return nil end\n      return vim.inspect(value)\n    end\n  end\n\n  local all_registers = vim.split('0123456789abcdefghijklmnopqrstuvwxyz*+\"-:.%/#', '')\n\n  local res = {}\n  for _, register in ipairs(all_registers) do\n    table.insert(res, { mode = mode, keys = prefix .. register, desc = make_register_desc(register) })\n  end\n  table.insert(res, { mode = mode, keys = prefix .. '=', desc = 'Result of expression' })\n\n  return res\nend\n\n-- Predicates -----------------------------------------------------------------\nH.is_trigger = function(x) return type(x) == 'table' and type(x.mode) == 'string' and type(x.keys) == 'string' end\n\nH.is_clue = function(x)\n  if type(x) ~= 'table' then return false end\n  local mandatory = type(x.mode) == 'string' and type(x.keys) == 'string'\n  local extra = (x.desc == nil or type(x.desc) == 'string' or vim.is_callable(x.desc))\n    and (x.postkeys == nil or type(x.postkeys) == 'string')\n  return mandatory and extra\nend\n\nH.is_array_of = function(x, predicate)\n  if not H.islist(x) then return false end\n  for _, v in ipairs(x) do\n    if not predicate(v) then return false end\n  end\n  return true\nend\n\n-- Utilities ------------------------------------------------------------------\nH.error = function(msg) error(string.format('(mini.clue) %s', msg), 0) end\n\nH.map = function(mode, lhs, rhs, opts)\n  if lhs == '' then return end\n  opts = vim.tbl_deep_extend('force', { silent = true }, opts or {})\n  vim.keymap.set(mode, lhs, rhs, opts)\nend\n\nH.replace_termcodes = function(x)\n  if x == nil then return nil end\n  -- Use `keytrans` prior replacing termcodes to work correctly on already\n  -- replaced variant of `<F*>` keys\n  return vim.api.nvim_replace_termcodes(H.keytrans(x), true, true, true)\nend\n\n-- TODO: Remove after compatibility with Neovim=0.7 is dropped\nif vim.fn.has('nvim-0.8') == 1 then\n  H.keytrans = function(x)\n    local res = vim.fn.keytrans(x):gsub('<lt>', '<')\n    return res\n  end\nelse\n  H.keytrans = function(x)\n    local res = x:gsub('<lt>', '<')\n    return res\n  end\nend\n\nH.get_forced_submode = function()\n  local mode = vim.fn.mode(1)\n  if not mode:sub(1, 2) == 'no' then return '' end\n  return mode:sub(3)\nend\n\nH.get_default_register = function()\n  local clipboard = vim.o.clipboard\n  if clipboard:find('unnamedplus') ~= nil then return '+' end\n  if clipboard:find('unnamed') ~= nil then return '*' end\n  return '\"'\nend\n\nH.is_valid_buf = function(buf_id) return type(buf_id) == 'number' and vim.api.nvim_buf_is_valid(buf_id) end\n\nH.is_valid_win = function(win_id) return type(win_id) == 'number' and vim.api.nvim_win_is_valid(win_id) end\n\nH.expand_callable = function(x, ...)\n  if vim.is_callable(x) then return x(...) end\n  return x\nend\n\nH.redraw_scheduled = vim.schedule_wrap(function() vim.cmd('redraw') end)\n\nH.getcharstr = function()\n  -- Ensure redraws still happen\n  H.timers.getcharstr:start(0, 50, H.redraw_scheduled)\n  local ok, char = pcall(vim.fn.getcharstr)\n  H.timers.getcharstr:stop()\n  -- Terminate if couldn't get input (like with <C-c>) or it is `<Esc>`\n  if not ok or char == '\\27' or char == '' then return end\n  return H.get_langmap()[char] or char\nend\n\nH.get_langmap = function()\n  if vim.o.langmap == '' then return {} end\n\n  -- Get langmap parts by splitting at \",\" not preceded by \"\\\"\n  local langmap_parts = vim.fn.split(vim.o.langmap, '[^\\\\\\\\]\\\\zs,')\n\n  -- Process each langmap part\n  local res = {}\n  for _, part in ipairs(langmap_parts) do\n    H.process_langmap_part(res, part)\n  end\n  return res\nend\n\nH.process_langmap_part = function(res, part)\n  local semicolon_byte_ind = vim.fn.match(part, '[^\\\\\\\\]\\\\zs;') + 1\n\n  -- Part is without ';', like 'aAbB'\n  if semicolon_byte_ind == 0 then\n    -- Drop backslash escapes\n    part = part:gsub('\\\\([^\\\\])', '%1')\n\n    for i = 1, vim.fn.strchars(part), 2 do\n      -- `strcharpart()` has 0-based indexes\n      local from, to = vim.fn.strcharpart(part, i - 1, 1), vim.fn.strcharpart(part, i, 1)\n      if from ~= '' and to ~= '' then res[from] = to end\n    end\n\n    return\n  end\n\n  -- Part is with ';', like 'ab;AB'\n  -- - Drop backslash escape\n  local left = part:sub(1, semicolon_byte_ind - 1):gsub('\\\\([^\\\\])', '%1')\n  local right = part:sub(semicolon_byte_ind + 1):gsub('\\\\([^\\\\])', '%1')\n\n  for i = 1, vim.fn.strchars(left) do\n    local from, to = vim.fn.strcharpart(left, i - 1, 1), vim.fn.strcharpart(right, i - 1, 1)\n    if from ~= '' and to ~= '' then res[from] = to end\n  end\nend\n\nH.list_concat = function(...)\n  local res = {}\n  for i = 1, select('#', ...) do\n    for _, x in ipairs(select(i, ...) or {}) do\n      table.insert(res, x)\n    end\n  end\n  return res\nend\n\n-- TODO: Remove after compatibility with Neovim=0.9 is dropped\nH.islist = vim.fn.has('nvim-0.10') == 1 and vim.islist or vim.tbl_islist\n\nreturn MiniClue\n"
  },
  {
    "path": "lua/blink/config.lua",
    "content": "local M = {}\n\nM.default = {\n  chartoggle = {\n    enabled = false,\n    delimiters = { ',', ';' },\n  },\n  clue = {\n    enabled = false,\n  },\n  select = {\n    enabled = false,\n  },\n  tree = {\n    enabled = false,\n  },\n}\n\nfunction M.setup(opts) M.config = vim.tbl_deep_extend('force', M.default, opts or {}) end\n\nreturn setmetatable(M, { __index = function(_, k) return M.config[k] end })\n"
  },
  {
    "path": "lua/blink/dashboard/init.lua",
    "content": "local api = vim.api\nlocal Dashboard = {}\n\nlocal function create_buf()\n  local bufnr = api.nvim_create_buf(false, true)\n\n  local opts = {\n    ['bufhidden'] = 'wipe',\n    ['colorcolumn'] = '',\n    ['foldcolumn'] = '0',\n    ['matchpairs'] = '',\n    ['buflisted'] = false,\n    ['cursorcolumn'] = false,\n    ['cursorline'] = false,\n    ['list'] = false,\n    ['number'] = false,\n    ['relativenumber'] = false,\n    ['spell'] = false,\n    ['swapfile'] = false,\n    ['readonly'] = false,\n    ['filetype'] = 'dashboard',\n    ['wrap'] = false,\n    ['signcolumn'] = 'no',\n    ['winbar'] = '',\n    ['stc'] = '',\n  }\n  for opt, val in pairs(opts) do\n    api.nvim_set_option_value(opt, val, { buf = bufnr })\n  end\n\n  return bufnr\nend\n\nfunction Dashboard.setup()\n  -- should we show the dashboard?\n  if vim.fn.argc() == 0 and api.nvim_buf_get_name(0) == '' and vim.g.read_from_stdin == nil then return end\n\n  local bufnr = create_buf()\n  local winid = api.nvim_get_current_win()\n  api.nvim_win_set_buf(winid, bufnr)\n\n  local center_line = function(line)\n    local width = api.nvim_win_get_width(0)\n    local line_width = string.len(line)\n    local padding = math.floor((width - line_width) / 2)\n    return string.rep(' ', padding) .. line\n  end\n\n  local lines = {\n    '',\n    center_line('Welcome to Tuque'),\n  }\n\n  local centered_lines = {}\n  for _, line in ipairs(lines) do\n    table.insert(centered_lines, center_line(line))\n  end\n\n  api.nvim_buf_set_lines(bufnr, 0, -1, false, centered_lines)\nend\n\nreturn Dashboard\n"
  },
  {
    "path": "lua/blink/init.lua",
    "content": "local M = {}\n\nfunction M.setup(opts)\n  local config = require('blink.config')\n  config.setup(opts)\n\n  if config.chartoggle.enabled then require('blink.chartoggle').setup(config.chartoggle) end\n  if config.clue.enabled then require('blink.clue').setup(config.clue) end\n  if config.indent and config.indent.enabled then\n    vim.notify(\n      'blink.nvim: indent.enabled has been replaced by a separate blink.indent repo. See https://github.com/saghen/blink.indent',\n      vim.log.levels.WARN\n    )\n  end\n  if config.select.enabled then require('blink.select').setup(config.select) end\n  if config.tree.enabled then require('blink.tree').setup(config.tree) end\nend\n\nreturn M\n"
  },
  {
    "path": "lua/blink/render/types.lua",
    "content": "--- 0-1 is interpretted as percentage of parent and whole numbers are the number of columns.\n--- When an array, the minimum will be taken in all cases except max_width and max_height.\n--- @alias Length number | number[]\n---\n--- @class Component\n--- @field align? 'left' | 'center' | 'right'\n--- Number of spaces or string of characters to pad between components\n--- @field gap? number | string\n--- @field space? 'between' | 'around' | 'evenly'\n--- @field direction? 'horizontal' | 'vertical'\n--- @field size? 'expand' | 'shrink'\n--- @field overflow? 'ellipsis'\n--- @field max_width? Length\n--- @field min_width? Length\n--- @field max_height? Length\n--- @field min_height? Length\n--- @field width? Length\n--- @field height? Length\n--- @field padding? Length\n--- @field margin? Length\n--- @field children Component[]\n\n--- @param comp Component\nfunction get(comp) local yo = comp.width end\n\nreturn Component\n"
  },
  {
    "path": "lua/blink/select/config.lua",
    "content": "--- @class SelectMapping\n--- @field selection string[]\n--- @field quit string[]\n--- @field next_page string[]\n--- @field prev_page string[]\n---\n--- @class SelectWindowConfig\n--- @field min_width number[]\n--- @field max_width number[]\n--- @field border 'single' | 'double' | 'rounded' | string[]\n--- @field wrap boolean\n\n--- @class SelectConfig\n--- @field mapping SelectMapping\n--- @field window SelectWindowConfig\nlocal config = {\n  mapping = {\n    selection = { 'h', 'j', 'k', 'l', 'a', 's', 'd', 'f' },\n    -- remember selection also uses the capital variants so dont interfere\n    prev_page = { '<C-h>' },\n    next_page = { '<C-l>' },\n    quit = { 'q', '<Esc>' },\n  },\n  window = {\n    min_width = { 20, 0.2 }, -- greater of 20 columns and 20% of current window width\n    max_width = { 120, 0.8 }, -- lesser of 120 columns and 80% of current window width\n    border = 'rounded',\n    wrap = false,\n    group_size = 4,\n  },\n}\n\nfunction config.setup(opts) config = vim.tbl_deep_extend('force', config, opts or {}) end\n\nreturn setmetatable({}, { __index = function(_, k) return config[k] end })\n"
  },
  {
    "path": "lua/blink/select/init.lua",
    "content": "local select = {}\n\n--- @param opts SelectConfig\nfunction select.setup(opts)\n  require('blink.select.config').setup(opts)\n  require('blink.select.providers.yank-history') -- requires autocmds setup early\nend\n\nfunction select.show(provider) require('blink.select.window').show(require('blink.select.providers.' .. provider)) end\n\nreturn select\n"
  },
  {
    "path": "lua/blink/select/providers/buffers.lua",
    "content": "--- @class SelectProvider\nlocal buffers = {\n  name = 'Buffers',\n}\n\nfunction buffers.get_items(opts, cb)\n  local idx = 1\n  local bufs = vim.api.nvim_list_bufs()\n  bufs = vim.tbl_filter(function(bufnr) return vim.api.nvim_get_option_value('buflisted', { buf = bufnr }) end, bufs)\n  -- Sort buffers by last used time\n  table.sort(bufs, function(a, b) return vim.fn.getbufinfo(a)[1].lastused > vim.fn.getbufinfo(b)[1].lastused end)\n  local devicons = require('nvim-web-devicons')\n\n  cb({\n    page_count = math.ceil(#bufs / opts.page_size),\n    next_page = function(page_cb)\n      local items = {}\n      while idx <= #bufs and #items < opts.page_size do\n        local bufnr = bufs[idx]\n        if vim.api.nvim_buf_is_valid(bufnr) then\n          local buf_path = vim.api.nvim_buf_get_name(bufnr)\n          local dirname = vim.fn.fnamemodify(buf_path, ':~:.:h')\n          local dirname_component = { dirname, highlight = 'Comment' }\n\n          local filename = vim.fn.fnamemodify(buf_path, ':t')\n          if filename == '' then filename = '[No Name]' end\n          local diagnostic_level = nil\n          for _, diagnostic in ipairs(vim.diagnostic.get(bufnr)) do\n            diagnostic_level = math.min(diagnostic_level or 999, diagnostic.severity)\n          end\n          local filename_hl = diagnostic_level == vim.diagnostic.severity.HINT and 'DiagnosticHint'\n            or diagnostic_level == vim.diagnostic.severity.INFO and 'DiagnosticInfo'\n            or diagnostic_level == vim.diagnostic.severity.WARN and 'DiagnosticWarn'\n            or diagnostic_level == vim.diagnostic.severity.ERROR and 'DiagnosticError'\n            or 'Normal'\n          local filename_component = { filename, highlight = filename_hl }\n\n          -- Modified icon\n          local modified = vim.bo[bufnr].modified\n          local modified_component = modified and { ' ● ', highlight = 'BufferCurrentMod' } or ''\n\n          local icon, icon_hl = devicons.get_icon(filename)\n          local icon_component = icon and { ' ' .. icon .. ' ', highlight = icon_hl } or ''\n\n          table.insert(items, {\n            data = { bufnr = bufnr },\n            fragments = {\n              modified_component,\n              icon_component,\n              ' ',\n              filename_component,\n              ' ',\n              dirname_component,\n              ' ',\n            },\n          })\n        end\n\n        idx = idx + 1\n      end\n\n      page_cb(items)\n    end,\n  })\nend\n\nfunction buffers.select(item) vim.api.nvim_set_current_buf(item.data.bufnr) end\n\nreturn buffers\n"
  },
  {
    "path": "lua/blink/select/providers/code-actions.lua",
    "content": "--- @class SelectProvider\nlocal code_actions = {\n  name = 'Code Actions',\n}\n\nfunction code_actions.get_items(opts, cb)\n  local idx = 1\n  local actions = {}\n\n  -- Get available code actions\n  local results = vim.lsp.buf_request_sync(0, 'textDocument/codeAction', vim.lsp.util.make_range_params(), 1000)\n  for _, result in pairs(results or {}) do\n    if result and result.result then vim.tbl_extend('force', actions, result.result) end\n  end\n\n  cb({\n    page_count = math.ceil(#actions / opts.page_size),\n    next_page = function(page_cb)\n      --- @type RenderFragment[]\n      local items = {}\n      while idx <= #actions and #items < opts.page_size do\n        local action = actions[idx]\n\n        -- Action title\n        local title_component = { action.title, highlight = 'Function' }\n\n        -- Action kind (if available)\n        --- @type string | RenderFragment\n        local kind_component = ''\n        if action.kind then kind_component = { ' [' .. action.kind .. ']', highlight = 'Comment' } end\n\n        -- Action source (if available)\n        --- @type string | RenderFragment\n        local source_component = ''\n        if action.source then source_component = { ' from ' .. action.source, highlight = 'Comment' } end\n\n        table.insert(items, {\n          data = action,\n          fragments = {\n            '  ',\n            title_component,\n            kind_component,\n            source_component,\n          },\n        })\n\n        idx = idx + 1\n      end\n\n      page_cb(items)\n    end,\n  })\nend\n\nfunction code_actions.select(item)\n  local action = item.data\n  if action.edit or type(action.command) == 'table' then\n    if action.edit then vim.lsp.util.apply_workspace_edit(action.edit, 'UTF-8') end\n    if type(action.command) == 'table' then vim.lsp.buf.execute_command(action.command) end\n  else\n    vim.lsp.buf.execute_command(action)\n  end\nend\n\nreturn code_actions\n"
  },
  {
    "path": "lua/blink/select/providers/diagnostics.lua",
    "content": "--- @class SelectProvider\nlocal diagnostics = {\n  name = 'Diagnostics',\n}\n\nfunction diagnostics.get_items(opts, cb)\n  local idx = 1\n  local items = {}\n  local all_diagnostics = vim.diagnostic.get(opts.bufnr, { severity = { min = vim.diagnostic.severity.HINT } })\n  -- sort by line number\n  table.sort(all_diagnostics, function(a, b) return a.lnum < b.lnum end)\n\n  cb({\n    page_count = math.ceil(#all_diagnostics / opts.page_size),\n    next_page = function(page_cb)\n      while idx <= #all_diagnostics and #items < opts.page_size do\n        local diag = all_diagnostics[idx]\n        local bufnr = diag.bufnr\n        if bufnr == nil then break end\n\n        -- Get the diagnostic symbol\n        local symbol = '●'\n        if diag.severity == vim.diagnostic.severity.ERROR then\n          symbol = ' '\n        elseif diag.severity == vim.diagnostic.severity.WARN then\n          symbol = ' '\n        elseif diag.severity == vim.diagnostic.severity.INFO then\n          symbol = ' '\n        elseif diag.severity == vim.diagnostic.severity.HINT then\n          symbol = ' '\n        end\n\n        -- Get the diagnostic highlight\n        local highlight = 'DiagnosticHint'\n        if diag.severity == vim.diagnostic.severity.ERROR then\n          highlight = 'DiagnosticError'\n        elseif diag.severity == vim.diagnostic.severity.WARN then\n          highlight = 'DiagnosticWarn'\n        elseif diag.severity == vim.diagnostic.severity.INFO then\n          highlight = 'DiagnosticInfo'\n        end\n\n        -- Truncate the message if it's too long\n        local short_message = vim.split(diag.message, '\\n')[1]\n        if #short_message > 80 then short_message = short_message:sub(1, 77) .. '...' end\n\n        table.insert(items, {\n          data = { bufnr = bufnr, lnum = diag.lnum, col = diag.col },\n          fragments = {\n            { symbol .. ' ', highlight = highlight },\n            { tostring(diag.lnum) .. ':', highlight = 'Comment' },\n            { tostring(diag.col) .. ' ', highlight = 'Comment' },\n            { short_message, highlight = highlight },\n          },\n        })\n\n        idx = idx + 1\n      end\n\n      page_cb(items)\n    end,\n  })\nend\n\nfunction diagnostics.select(item)\n  vim.api.nvim_set_current_buf(item.data.bufnr)\n  vim.api.nvim_win_set_cursor(0, { item.data.lnum + 1, item.data.col })\n  vim.diagnostic.open_float()\nend\n\nreturn diagnostics\n"
  },
  {
    "path": "lua/blink/select/providers/lsp/definitions.lua",
    "content": ""
  },
  {
    "path": "lua/blink/select/providers/lsp/references.lua",
    "content": ""
  },
  {
    "path": "lua/blink/select/providers/lsp/symbols.lua",
    "content": "local symbols = {}\n\nlocal function symbols.get_items(opts)\n  local symbols = {}\n  local params = vim.lsp.util.make_position_params(opts.winnr)\n"
  },
  {
    "path": "lua/blink/select/providers/recent-commands.lua",
    "content": ""
  },
  {
    "path": "lua/blink/select/providers/recent-searches.lua",
    "content": "--- @class SelectProvider\nlocal recent_searches = {\n  name = 'Recent Searches',\n}\n\nlocal function reverse(tab)\n  for i = 1, math.floor(#tab / 2), 1 do\n    tab[i], tab[#tab - i + 1] = tab[#tab - i + 1], tab[i]\n  end\n  return tab\nend\n\nfunction recent_searches.get_items(opts, cb)\n  local idx = 1\n  local search_history = vim.fn.searchcount().total > 0 and vim.fn.execute('history search') or ''\n  local searches = vim.split(search_history, '\\n')\n\n  -- Remove the header line\n  table.remove(searches, 1)\n  table.remove(searches, 1)\n  reverse(searches)\n\n  cb({\n    page_count = math.ceil(#searches / opts.page_size),\n    next_page = function(page_cb)\n      local items = {}\n      while idx <= #searches and #items < opts.page_size do\n        local search = searches[idx]\n        if search ~= '' then\n          local search_text = search:match('%s+%d+%s+(.+)$')\n          if search_text then\n            table.insert(items, {\n              data = { search = search_text },\n              fragments = {\n                { ' ', highlight = 'Normal' },\n                { search_text, highlight = 'String' },\n              },\n            })\n          end\n        end\n        idx = idx + 1\n      end\n      page_cb(items)\n    end,\n  })\nend\n\nfunction recent_searches.select(item)\n  vim.fn.setreg('/', item.data.search)\n  vim.cmd('normal! n')\nend\n\nreturn recent_searches\n"
  },
  {
    "path": "lua/blink/select/providers/smart-open.lua",
    "content": "local DbClient = require('telescope._extensions.smart_open.dbclient')\nlocal config = require('smart-open').config\nlocal get_buffer_list = require('telescope._extensions.smart_open.buffers')\nlocal weights = require('telescope._extensions.smart_open.weights')\nlocal get_finder = require('telescope._extensions.smart_open.finder.finder')\nlocal format_filepath = require('telescope._extensions.smart_open.display.format_filepath')\n\n--- @class SelectProvider\nlocal smart_open = {\n  name = 'Smart Open',\n  db = DbClient:new({ path = config.db_filename }),\n  history = require('telescope._extensions.smart_open.history'),\n}\n\nfunction smart_open.get_items(opts, callback)\n  local context = {\n    cwd = vim.fn.getcwd(),\n    current_buffer = vim.api.nvim_buf_get_name(opts.bufnr),\n    -- might be wrong if the select buffer is already open\n    alternate_buffer = opts.alternate_bufnr > 0 and vim.api.nvim_buf_get_name(opts.alternate_bufnr) or '',\n    open_buffers = get_buffer_list(),\n    weights = smart_open.db:get_weights(weights.default_weights),\n    path_display = true,\n  }\n  local finder_opts = {\n    cwd = context.cwd,\n    cwd_only = config.cwd_only,\n    ignore_patterns = config.ignore_patterns,\n    show_scores = config.show_scores,\n    match_algorithm = config.match_algorithm,\n    filename_first = true,\n  }\n  local finder = get_finder(smart_open.history, finder_opts, context)\n\n  local seen_paths = {}\n  local items = {}\n  finder('', function(entry)\n    if seen_paths[entry.path] then return end\n    seen_paths[entry.path] = true\n\n    local symbol = entry.scores.alt > 0 and config.open_buffer_indicators.previous\n      or entry.buf and config.open_buffer_indicators.others\n      or ' '\n    local icon, icon_hl = require('nvim-web-devicons').get_icon(entry.path, nil, { default = true })\n    local result, hl_group = format_filepath(entry.path, entry.virtual_name, finder_opts, 60)\n    local filename = result:sub(1, hl_group[1][1])\n    local directory = result:sub(hl_group[1][1] + 1)\n    table.insert(items, {\n      data = entry,\n      fragments = {\n        { symbol .. ' ' },\n        { icon .. '  ', highlight = icon_hl },\n        { filename },\n        { directory, highlight = 'Directory' },\n      },\n    })\n  end, function()\n    local page_count = math.ceil(#items / opts.page_size)\n    local page = 0\n    callback({\n      next_page = function(cb)\n        -- no more pages\n        if page >= page_count then return cb({}) end\n\n        local start_idx = (page * opts.page_size + 1)\n        local end_idx = (page + 1) * opts.page_size\n        local page_items = vim.list_slice(items, start_idx, end_idx)\n        page = page + 1\n        cb(page_items)\n      end,\n      page_count = page_count,\n    })\n  end)\nend\n\nfunction smart_open.select(item)\n  if item.data.bufnr ~= nil and vim.api.nvim_buf_is_valid(item.data.bufnr) then\n    vim.api.nvim_set_current_buf(item.data.bufnr)\n  else\n    vim.cmd('edit ' .. item.data.path)\n  end\n  vim.defer_fn(function() smart_open.history:record_usage(item.data.path, false) end, 10)\nend\n\nreturn smart_open\n"
  },
  {
    "path": "lua/blink/select/providers/yank-history.lua",
    "content": "--- @class SelectProvider\nlocal yank_history = {\n  name = 'Yank History',\n}\n\n-- Initialize yank history table\nlocal history = {}\nlocal max_history = 50 -- Maximum number of items to keep in history\n\n-- Function to add item to yank history\nlocal function add_to_history(text)\n  -- Remove duplicate if exists\n  for i, item in ipairs(history) do\n    if item.text == text then\n      table.remove(history, i)\n      break\n    end\n  end\n\n  -- Add new item to the beginning\n  table.insert(history, 1, { text = text, timestamp = os.time() })\n\n  -- Trim history if it exceeds max_history\n  if #history > max_history then table.remove(history) end\nend\n\n-- Set up autocmd to track yanks\nvim.api.nvim_create_autocmd('TextYankPost', {\n  callback = function()\n    local text = vim.fn.getreg('\"')\n    add_to_history(text)\n  end,\n})\n\nfunction yank_history.get_items(opts, cb)\n  local idx = 1\n\n  cb({\n    page_count = math.ceil(#history / opts.page_size),\n    next_page = function(page_cb)\n      local items = {}\n      while idx <= #history and #items < opts.page_size do\n        local item = history[idx]\n        local text = item.text:gsub('[\\n\\r]', ' ') -- Replace newlines with spaces\n        local truncated_text = #text > 50 and text:sub(1, 47) .. '...' or text\n\n        local time_diff = os.difftime(os.time(), item.timestamp)\n        local time_str = time_diff < 60 and 'just now'\n          or time_diff < 3600 and string.format('%d min ago', math.floor(time_diff / 60))\n          or time_diff < 86400 and string.format('%d hours ago', math.floor(time_diff / 3600))\n          or string.format('%d days ago', math.floor(time_diff / 86400))\n\n        table.insert(items, {\n          data = { text = item.text },\n          fragments = {\n            { truncated_text, highlight = 'Normal' },\n            ' ',\n            { time_str, highlight = 'Comment' },\n          },\n        })\n\n        idx = idx + 1\n      end\n\n      page_cb(items)\n    end,\n  })\nend\n\nfunction yank_history.select(item)\n  vim.fn.setreg('\"', item.data.text)\n  vim.api.nvim_put(vim.split(item.data.text, '\\n'), '', true, true)\nend\n\nfunction yank_history.alt_select(item)\n  vim.fn.setreg('\"', item.data.text)\n  vim.api.nvim_put(vim.split(item.data.text, '\\n'), '', false, true)\nend\n\nreturn yank_history\n"
  },
  {
    "path": "lua/blink/select/renderer.lua",
    "content": "local api = vim.api\nlocal config = require('blink.select.config')\nlocal renderer = {}\n\nfunction renderer.new(bufnr)\n  local self = setmetatable({}, { __index = renderer })\n  self.bufnr = bufnr\n  return self\nend\n\n--- @param fragments RenderFragment[]\nfunction renderer:draw_line(fragments, line_number)\n  -- render text\n  local texts = {}\n  for _, fragment in ipairs(fragments) do\n    table.insert(texts, type(fragment) == 'string' and fragment or fragment[1])\n  end\n  api.nvim_buf_set_lines(self.bufnr, line_number, line_number + 1, false, { table.concat(texts) })\n\n  -- render highlights\n  local char = 0\n  for fragment_idx, fragment in ipairs(fragments) do\n    if fragment.highlight ~= nil then\n      api.nvim_buf_add_highlight(self.bufnr, 0, fragment.highlight, line_number, char, char + #texts[fragment_idx])\n    end\n    char = char + #texts[fragment_idx]\n  end\nend\n\nfunction renderer.draw(self, items) end\n\n"
  },
  {
    "path": "lua/blink/select/types.lua",
    "content": "--- @class RenderFragment\n--- @field [1] string\n--- @field highlight? string\n---\n--- @class SelectItem\n--- @field fragments (RenderFragment | string)[]\n--- @field data any\n---\n--- @class GetItemsOptions\n--- @field winnr number\n--- @field bufnr number\n--- @field alternate_bufnr number\n--- @field page_size number\n---\n--- @class GetItemsResponse\n--- @field next_page fun(cb: fun(items: SelectItem[])): nil\n--- @field page_count number | nil\n---\n--- @class SelectProvider\n--- @field name string Human readable name of the provider\n--- @field get_items fun(opts: GetItemsOptions, cb: fun(response: GetItemsResponse)): nil\n--- @field select fun(item: SelectItem): any\n--- @field alt_select? fun(item: SelectItem): any\n"
  },
  {
    "path": "lua/blink/select/window.lua",
    "content": "local window = {\n  bufnr = -1,\n  winnr = -1,\n}\n\nlocal api = vim.api\nlocal augroup = vim.api.nvim_create_augroup('BlinkSelectWindow', { clear = true })\nlocal config = require('blink.select.config')\n\nlocal function center_text(text, width)\n  local padding = math.floor(width / 2 - vim.fn.strdisplaywidth(text) / 2)\n  return string.rep(' ', padding) .. text\nend\n\n-- hide the cursor when window is focused\nlocal prev_cursor\nlocal prev_blend\napi.nvim_create_autocmd('BufEnter', {\n  group = augroup,\n  callback = function()\n    if vim.bo.filetype == 'blink-select' and prev_cursor == nil then\n      prev_cursor = api.nvim_get_option_value('guicursor', {})\n      api.nvim_set_option_value('guicursor', 'a:Cursor/lCursor', {})\n\n      local cursor_hl = api.nvim_get_hl(0, { name = 'Cursor' })\n      prev_blend = cursor_hl.blend\n      api.nvim_set_hl(0, 'Cursor', vim.tbl_extend('force', cursor_hl, { blend = 100 }))\n    end\n  end,\n})\napi.nvim_create_autocmd('BufLeave', {\n  group = augroup,\n  callback = function()\n    if prev_cursor ~= nil then\n      api.nvim_set_option_value('guicursor', prev_cursor, {})\n      prev_cursor = nil\n\n      local cursor_hl = api.nvim_get_hl(0, { name = 'Cursor' })\n      api.nvim_set_hl(0, 'Cursor', vim.tbl_extend('force', cursor_hl, { blend = prev_blend or 0 }))\n      prev_blend = nil\n    end\n  end,\n})\n\nlocal function width_clamp(desired_width)\n  local max_width_columns = config.window.max_width[1]\n  local max_width_percent = config.window.max_width[2]\n  local max_width = math.floor(math.min(max_width_columns, vim.o.columns * max_width_percent))\n\n  local min_width_columns = config.window.min_width[1]\n  local min_width_percent = config.window.min_width[2]\n  local min_width = math.ceil(math.max(min_width_columns, vim.o.columns * min_width_percent))\n\n  return math.max(math.min(desired_width, max_width), min_width)\nend\n\nfunction window.show(provider)\n  window.prev_bufnr = vim.api.nvim_get_current_buf()\n  window.provider = provider\n\n  provider.get_items({\n    page_size = #config.mapping.selection,\n    bufnr = window.prev_bufnr,\n    alternate_bufnr = vim.fn.bufnr('#'),\n  }, function(result)\n    window.provider_next_page = result.next_page\n    window.page_count = result.page_count\n    window.pages = {}\n    window.page_idx = 0\n\n    vim.schedule(function()\n      window.open()\n      window.next_page()\n    end)\n  end)\nend\n\nfunction window.get_buf()\n  if window.bufnr ~= nil and api.nvim_buf_is_valid(window.bufnr) then return window.bufnr end\n\n  window.bufnr = api.nvim_create_buf(false, true)\n  api.nvim_set_option_value('buftype', 'nofile', { buf = window.bufnr })\n  api.nvim_set_option_value('filetype', 'blink-select', { buf = window.bufnr })\n  api.nvim_set_option_value('swapfile', false, { buf = window.bufnr })\n  api.nvim_set_option_value('modifiable', false, { buf = window.bufnr })\n\n  window.setup_mapping(window.bufnr)\n\n  return window.bufnr\nend\n\nfunction window.open()\n  if window.is_open() then return window.winnr end\n\n  -- open window\n  window.winnr = api.nvim_open_win(window.get_buf(), true, {\n    relative = 'editor',\n    width = 30,\n    height = 10,\n    row = 0,\n    col = 0,\n    style = 'minimal',\n    -- border = { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' },\n    border = 'single',\n    title = ' ' .. window.provider.name .. ' ',\n    title_pos = 'center',\n  })\n  api.nvim_set_option_value(\n    'winhighlight',\n    'Normal:Normal,FloatBorder:Normal,FloatTitle:Normal',\n    { win = window.winnr }\n  )\n  api.nvim_set_option_value('wrap', config.window.wrap, { win = window.winnr })\n\n  api.nvim_create_autocmd('WinLeave', {\n    group = augroup,\n    callback = function()\n      if window.winnr == vim.api.nvim_get_current_win() then window.close() end\n    end,\n  })\n\n  return window.winnr\nend\n\nfunction window.is_open() return window.winnr ~= nil and api.nvim_win_is_valid(window.winnr) end\n\nfunction window.setup_mapping(bufnr)\n  local mapping = config.mapping\n  local map = function(lhs, rhs)\n    api.nvim_buf_set_keymap(bufnr, 'n', lhs, '', {\n      callback = rhs,\n      nowait = true,\n    })\n  end\n  for idx, key in ipairs(mapping.selection) do\n    if key:upper() == key then error('Selection keys must be lowercase') end\n    map(key, function() window.select(idx) end)\n    map(key:upper(), function() window.alt_select(idx) end)\n  end\n  for _, key in ipairs(mapping.quit) do\n    map(key, window.close)\n  end\n  for _, key in ipairs(mapping.next_page) do\n    map(key, window.next_page)\n  end\n  for _, key in ipairs(mapping.prev_page) do\n    map(key, window.prev_page)\n  end\nend\n\nfunction window.render(items)\n  api.nvim_set_option_value('modifiable', true, { buf = window.bufnr })\n\n  local bufnr = window.get_buf()\n  api.nvim_buf_set_lines(bufnr, 0, -1, false, { '' })\n\n  for line_number, item in ipairs(items) do\n    -- add the key to the fragments to render\n    local key = config.mapping.selection[line_number]\n    local fragments = { { ' ' .. key .. ' ', highlight = 'Primary' } }\n    for _, fragment in ipairs(item.fragments) do\n      table.insert(fragments, fragment)\n    end\n\n    -- render text\n    local texts = {}\n    for _, fragment in ipairs(fragments) do\n      table.insert(texts, type(fragment) == 'string' and fragment or fragment[1])\n    end\n    api.nvim_buf_set_lines(bufnr, line_number, line_number + 1, false, { table.concat(texts) })\n\n    -- render highlights\n    local char = 0\n    for fragment_idx, fragment in ipairs(fragments) do\n      if fragment.highlight ~= nil then\n        api.nvim_buf_add_highlight(bufnr, 0, fragment.highlight, line_number, char, char + #texts[fragment_idx])\n      end\n      char = char + #texts[fragment_idx]\n    end\n  end\n\n  -- set window width to fit longest line\n  local max_width = 0\n  for _, line in ipairs(api.nvim_buf_get_lines(bufnr, 0, -1, false)) do\n    max_width = math.max(max_width, #line)\n  end\n  local width = width_clamp(max_width)\n  api.nvim_win_set_width(window.winnr, width)\n\n  -- add the group separators\n  for i = 1, math.floor(#items / config.window.group_size) - (math.fmod(#items, config.window.group_size) == 0 and 1 or 0) do\n    local line = center_text(string.rep('─', math.fmod(width, 2) == 1 and 5 or 4), width)\n    local line_number = i * config.window.group_size + i\n    api.nvim_buf_set_lines(bufnr, line_number, line_number, false, { line })\n  end\n\n  -- add the bottom line with next/prev page mapping, current page, page count\n  local page_info = center_text(string.format('Page %d/%d', window.page_idx, window.page_count), width)\n  api.nvim_buf_set_lines(bufnr, -1, -1, false, { '', page_info })\n\n  -- set window height to fit number of lines\n  api.nvim_win_set_height(window.winnr, vim.api.nvim_buf_line_count(bufnr))\n\n  -- center window on screen\n  local win_width = api.nvim_win_get_width(window.winnr)\n  local win_height = api.nvim_win_get_height(window.winnr)\n  local screen_width = vim.o.columns\n  local screen_height = vim.o.lines - vim.o.cmdheight\n  local row = math.floor((screen_height - win_height) / 2)\n  local col = math.floor((screen_width - win_width) / 2)\n  api.nvim_win_set_config(window.winnr, { relative = 'editor', row = row, col = col })\n\n  api.nvim_set_option_value('modifiable', false, { buf = window.bufnr })\nend\n\nfunction window.close() api.nvim_win_close(0, false) end\n\nfunction window.select(idx)\n  window.close()\n  window.provider.select(window.pages[window.page_idx][idx])\nend\n\nfunction window.alt_select(idx)\n  window.close()\n  local select_func = window.provider.alt_select or window.provider.select\n  select_func(window.pages[window.page_idx][idx])\nend\n\nfunction window.next_page()\n  if window.page_idx + 1 > #window.pages then\n    return window.provider_next_page(function(next_page)\n      -- no data, close if we're on the first page and notify user\n      if #next_page == 0 then\n        if window.page_idx == 0 then\n          vim.notify('No data from provider', vim.log.levels.INFO)\n          window.close()\n        end\n        return\n      end\n\n      table.insert(window.pages, next_page)\n\n      window.page_idx = window.page_idx + 1\n      window.render(window.pages[window.page_idx])\n    end)\n  end\n\n  window.page_idx = window.page_idx + 1\n  window.render(window.pages[window.page_idx])\nend\n\nfunction window.prev_page()\n  if window.page_idx - 1 < 1 then return end\n\n  window.page_idx = window.page_idx - 1\n  window.render(window.pages[window.page_idx])\nend\n\nreturn window\n"
  },
  {
    "path": "lua/blink/tree/binds/activate.lua",
    "content": "local api = vim.api\n\nlocal function activate(hovered_node, inst)\n  if hovered_node == nil then return end\n\n  -- todo: use existing buffer if available\n\n  -- dir: toggled expanded\n  if hovered_node.is_dir then\n    if hovered_node.expanded then\n      inst.tree:collapse(hovered_node)\n    else\n      inst.tree:expand(hovered_node)\n    end\n  -- file: open\n  else\n    local winnr = require('blink.tree.lib.utils').pick_or_create_non_special_window()\n    api.nvim_set_current_win(winnr)\n    local bufnr = vim.fn.bufnr(hovered_node.path)\n    if bufnr ~= -1 then\n      api.nvim_set_current_buf(bufnr)\n    else\n      vim.cmd('edit ' .. hovered_node.path)\n    end\n  end\nend\n\nreturn activate\n"
  },
  {
    "path": "lua/blink/tree/binds/basic.lua",
    "content": "local Basic = {}\n\nfunction Basic.new_file(hovered_node, inst)\n  while hovered_node ~= nil and hovered_node.is_dir == false do\n    hovered_node = hovered_node.parent\n  end\n  if hovered_node == nil then return end\n\n  local popup = require('blink.tree.popup')\n  local fs = require('blink.tree.lib.fs')\n\n  popup.new_input({ title = 'New File (append / for dir)', title_pos = 'center' }, function(input)\n    if input == nil then return end\n    local final_path = fs.create_file(hovered_node.path, input)\n    inst.tree:expand_path(final_path, function() inst.renderer:select_path(final_path) end)\n  end)\nend\n\nfunction Basic.delete_file(hovered_node)\n  local uv = require('blink.tree.lib.uv')\n  uv.exec_async({ command = { 'trash', hovered_node.path } }, function(code)\n    if code ~= 0 then print('Failed to delete: ' .. hovered_node.path) end\n  end)\nend\n\nfunction Basic.rename_file(hovered_node, inst)\n  local popup = require('blink.tree.popup')\n  local fs = require('blink.tree.lib.fs')\n\n  if hovered_node == inst.tree.root then vim.print('Cannot rename root') end\n\n  popup.new_input({ title = 'Rename', title_pos = 'center', initial_text = hovered_node.filename }, function(input)\n    if input == nil then return end\n    local new_path = hovered_node.parent.path .. '/' .. input\n    -- FIXME: would break if they rename the top level dir\n    fs.rename(hovered_node.path, new_path)\n    inst.tree:expand_path(new_path, function() inst.renderer:select_path(new_path) end)\n  end)\nend\n\nreturn Basic\n"
  },
  {
    "path": "lua/blink/tree/binds/expand.lua",
    "content": "local function expand(hovered_node, inst)\n  if hovered_node == nil then return end\n\n  if hovered_node.is_dir then\n    if hovered_node.expanded then\n      inst.tree:collapse(hovered_node)\n    else\n      inst.tree:expand(hovered_node)\n    end\n  else\n    return\n  end\nend\n\nreturn expand\n"
  },
  {
    "path": "lua/blink/tree/binds/init.lua",
    "content": "local api = vim.api\n\nlocal Binds = {}\n\nfunction Binds.attach_to_instance(inst)\n  local function map(mode, lhs, callback, opts)\n    opts = opts or {}\n    opts.callback = function() callback(inst.renderer:get_hovered_node(), inst) end\n    api.nvim_buf_set_keymap(inst.bufnr, mode, lhs, '', opts)\n  end\n\n  map('n', 'q', function() inst:close() end)\n  map('n', 'R', function() inst.tree:refresh() end)\n\n  local activate = require('blink.tree.binds.activate')\n  local expand = require('blink.tree.binds.expand')\n  map('n', '<CR>', activate)\n  map('n', '<2-LeftMouse>', activate)\n  map('n', '<Tab>', expand)\n\n  local basic = require('blink.tree.binds.basic')\n  map('n', 'a', basic.new_file)\n  map('n', 'd', basic.delete_file)\n  map('n', 'r', basic.rename_file)\n\n  local move = require('blink.tree.binds.move')\n  map('n', 'x', move.cut)\n  map('n', 'y', move.copy)\n  map('n', 'p', move.paste)\nend\n\nreturn Binds\n"
  },
  {
    "path": "lua/blink/tree/binds/move.lua",
    "content": "local Move = {}\n\nfunction Move.cut(node, inst)\n  node.flags.copy = false\n  node.flags.cut = not node.flags.cut\n  inst.renderer:redraw()\nend\n\nfunction Move.copy(node, inst)\n  node.flags.cut = false\n  node.flags.copy = not node.flags.copy\n  inst.renderer:redraw()\nend\n\nfunction Move.paste(hovered_node, inst)\n  while hovered_node ~= nil and hovered_node.is_dir == false do\n    hovered_node = hovered_node.parent\n  end\n  if hovered_node == nil then return end\n\n  local lib_tree = require('blink.tree.lib.tree')\n  local cut_nodes = {}\n  local copied_nodes = {}\n  lib_tree.traverse(inst.tree.root, function(node)\n    if node.flags.cut then\n      table.insert(cut_nodes, node)\n    elseif node.flags.copy then\n      table.insert(copied_nodes, node)\n    end\n  end)\n\n  local fs = require('blink.tree.lib.fs')\n  for _, cut_node in ipairs(cut_nodes) do\n    fs.rename(cut_node.path, hovered_node.path .. '/' .. cut_node.filename)\n    cut_node.flags.cut = false\n  end\n  for _, copied_node in ipairs(copied_nodes) do\n    fs.copy_file(copied_node.path, hovered_node.path .. '/' .. copied_node.filename)\n    copied_node.flags.copy = false\n  end\n\n  inst.renderer:redraw()\nend\n\nreturn Move\n"
  },
  {
    "path": "lua/blink/tree/config.lua",
    "content": "local M = {}\n\nM.default = {\n  hidden_by_default = true,\n  hide_dotfiles = false,\n  hide = {\n    '.direnv',\n    '.devenv',\n  },\n  never_show = {\n    '.git',\n    '.cache',\n    'node_modules',\n  },\n}\n\nfunction M.setup(opts)\n  opts = vim.tbl_deep_extend('force', M.default, opts or {})\n  M.config = opts\nend\n\nreturn setmetatable(M, { __index = function(_, k) return M.config[k] end })\n"
  },
  {
    "path": "lua/blink/tree/git/git2.lua",
    "content": "-- Made by SuperBo in Fugit2\n-- https://github.com/SuperBo/fugit2.nvim/tree/70662d529fe98790d7b2104b4dd67dd229332194\n-- Licensed under MIT\n\nlocal ffi = require \"ffi\"\nlocal libgit2 = require \"blink.tree.git.libgit2\"\nlocal stat = require \"blink.tree.git.stat\"\nlocal table_new = require \"table.new\"\nlocal uv = vim.uv or vim.loop\n\n--- Libgit2 init counter\nlocal libgit2_init_count = 0\n\n---@type string?\nlocal libgit2_library_path\n\n-- ========================\n-- | Libgit2 Enum section |\n-- ========================\n\nlocal GIT_REFERENCE_STRING = {\n  \"INVALID\",\n  \"DIRECT\",\n  \"SYMBOLIC\",\n  \"DIRECT/SYMBOLIC\",\n}\n\n---@enum GIT_REFERENCE_NAMESPACE\nlocal GIT_REFERENCE_NAMESPACE = {\n  NONE = 0, -- Normal ref, no namespace\n  BRANCH = 1, -- Reference is in Branch namespace\n  TAG = 2, -- Reference is in Tag namespace\n  REMOTE = 3, -- Reference is in Remote namespace\n  NOTE = 4, -- Reference is in Note namespace\n}\n\nlocal GIT_REFERENCE_PREFIX = {\n  [GIT_REFERENCE_NAMESPACE.BRANCH] = string.len \"refs/heads/\" + 1,\n  [GIT_REFERENCE_NAMESPACE.TAG] = string.len \"refs/tags/\" + 1,\n  [GIT_REFERENCE_NAMESPACE.REMOTE] = string.len \"refs/remotes/\" + 1,\n  [GIT_REFERENCE_NAMESPACE.NOTE] = string.len \"refs/notes/\" + 1,\n}\n\nlocal GIT_DELTA_STRING = {\n  \"UNMODIFIED\",\n  \"ADDED\",\n  \"DELETED\",\n  \"MODIFIED\",\n  \"RENAMED\",\n  \"COPIED\",\n  \"IGNORED\",\n  \"UNTRACKED\",\n  \"TYPECHANGE\",\n  \"UNREADABLE\",\n  \"CONFLICTED\",\n}\n\n-- ===================\n-- | Macro functions |\n-- ===================\n\n---@param mode integer\n---@return boolean\nlocal function GIT_PERMS_IS_EXEC(mode)\n  return bit.band(mode, 64) ~= 0\nend\n\n---@param mode integer\n---@return integer\nlocal function GIT_PERMS_CANONICAL(mode)\n  return GIT_PERMS_IS_EXEC(mode) and 493 or 420\nend\n\n---@param mode integer\n---@return integer\nlocal function GIT_PERMS_FOR_WRITE(mode)\n  return GIT_PERMS_IS_EXEC(mode) and 511 or 438\nend\n\n-- =====================\n-- | Class definitions |\n-- =====================\n\n---@class GitConfig\n---@field config ffi.cdata* libgit2 struct git_config*\nlocal Config = {}\nConfig.__index = Config\n\n---@class GitConfigEntry\n---@field name string\n---@field value string\n---@field include_depth integer\n---@field level GIT_CONFIG_LEVEL\n\n---@class GitRepository\n---@field repo ffi.cdata* libgit2 struct git_repository*\n---@field path string git repository path\nlocal Repository = {}\nRepository.__index = Repository\n\n---@class GitObject\n---@field obj ffi.cdata* libgit2 struct git_object*\nlocal Object = {}\nObject.__index = Object\n\n---@class GitObjectId\n---@field oid ffi.cdata* libgit2 git_oid struct\nlocal ObjectId = {}\nObjectId.__index = ObjectId\n\n---@class GitBlob\n---@field blob ffi.cdata* libgit2 struct git_blob**\nlocal Blob = {}\nBlob.__index = Blob\n\n---@class GitTree\n---@field tree ffi.cdata* libgit2.git_tree_pointer\nlocal Tree = {}\nTree.__index = Tree\n\n---@class GitTreeEntry\n---@field entry ffi.cdata* libgit2 git_tree_entry*\nlocal TreeEntry = {}\nTreeEntry.__index = TreeEntry\n\n---@class GitBlame\n---@field blame ffi.cdata* libgit2 git_blame *\nlocal Blame = {}\nBlame.__index = Blame\n\n---@class GitBlameHunk\n---@field hunk ffi.cdata* libgit2 git_blame_hunk *\n---@field num_lines integer\n---@field final_start_line_number integer\n---@field orig_start_line_number integer\n---@field boundary boolean true iff the hunk has been tracked to a boundary commit.\nlocal BlameHunk = {}\nBlameHunk.__index = BlameHunk\n\n---@class GitBlameOptions\n---@field first_parent boolean? reachable following only the first parents.\n---@field use_mailmap boolean? use mailmap file to map author and committer names and email.\n---@field ignore_whitespace boolean? ignore whitespace differences\n---@field min_match_characters integer? default value is 20\n---@field newest_commit GitObjectId?  id of the newest commit to consider.\n---@field oldest_commit GitObjectId? id of the oldest commit to consider.\n---@field min_line integer? The first line in the file to blame, 1-th based.\n---@field max_line integer? last line in the file to blame. The default is the last line of the file.\n\n---@class GitCommit\n---@field commit ffi.cdata* libgit2 git_commit pointer\nlocal Commit = {}\nCommit.__index = Commit\n\n---@class GitAnnotatedCommit\n---@field commit ffi.cdata* libgit2 git_annotated_commit pointer\nlocal AnnotatedCommit = {}\nAnnotatedCommit.__index = AnnotatedCommit\n\n---@class GitTag\n---@field tag ffi.cdata* libgit2 git_tag pointer\n---@field name string Git Tag name\nlocal Tag = {}\nTag.__index = Tag\n\n---@class GitReference\n---@field ref ffi.cdata* libgit2 git_reference type\n---@field name string Reference Refs full name\n---@field type GIT_REFERENCE Reference type\n---@field namespace GIT_REFERENCE_NAMESPACE Reference namespace if available\nlocal Reference = {}\nReference.__index = Reference\n\n---@class GitIndexEntry\n---@field entry ffi.cdata* libgit2 git_index_entry *\nlocal IndexEntry = {}\nIndexEntry.__index = IndexEntry\n\n---@class GitIndex\n---@field index ffi.cdata* libgit2 struct git_index*[1]\nlocal Index = {}\nIndex.__index = Index\n\n---@class GitRemote\n---@field remote ffi.cdata* libgit2 struct git_remote*[1]\n---@field name string\n---@field url string\n---@field push_url string?\nlocal Remote = {}\nRemote.__index = Remote\n\n---@class GitRevisionWalker\n---@field repo ffi.cdata* libgit2 struct git_repository*\n---@field revwalk ffi.cdata* libgit2 struct git_revwalk*[1]\nlocal RevisionWalker = {}\nRevisionWalker.__index = RevisionWalker\n\n---@class GitSignature\n---@field sign ffi.cdata* libgit2.git_signature_pointer\nlocal Signature = {}\nSignature.__index = Signature\n\n---@class GitPatch\n---@field patch ffi.cdata* libgit2 git_patch*\nlocal Patch = {}\nPatch.__index = Patch\n\n---@class GitDiff\n---@field diff ffi.cdata* libgit2 git_diff*\nlocal Diff = {}\nDiff.__index = Diff\n\n---@class GitDiffHunk\n---@field num_lines integer\n---@field old_start integer\n---@field old_lines integer\n---@field new_start integer\n---@field new_lines integer\n---@field header string\nlocal DiffHunk = {}\n\n---@class GitDiffLine\n---@field origin string\n---@field old_lineno integer\n---@field new_lineno integer\n---@field num_lines integer\n---@field content string\n\n---@class GitRebase\n---@field rebase ffi.cdata* libgit2 git_rebase* pointer\nlocal Rebase = {}\nRebase.__index = Rebase\n\n---@class GitRebaseOperation\n---@field operation ffi.cdata* libgit2 git_rebase_operation pointer\nlocal RebaseOperation = {}\nRebaseOperation.__index = RebaseOperation\n\n-- ========================\n-- | Git config functions |\n-- ========================\n\n---Inits git config\nfunction Config.new(git_config)\n  local config = { config = libgit2.git_config_pointer(git_config) }\n  setmetatable(config, Config)\n\n  ffi.gc(config.config, libgit2.C.git_config_free)\n\n  return config\nend\n\n---Open the global, XDG and system configuration files\n---@return GitConfig?\n---@return GIT_ERROR\nfunction Config.open_default()\n  local git_config = libgit2.git_config_double_pointer()\n\n  local err = libgit2.C.git_config_open_default(git_config)\n  if err ~= 0 then\n    return nil, err\n  end\n\n  return Config.new(git_config[0]), 0\nend\n\n---Build a single-level focused config object from a multi-level one\n---@param level GIT_CONFIG_LEVEL\n---@return GitConfig?\n---@return GIT_ERROR\nfunction Config:open_level(level)\n  local git_config = libgit2.git_config_double_pointer()\n\n  local err = libgit2.C.git_config_open_level(git_config, self.config, level)\n  if err ~= 0 then\n    return nil, err\n  end\n\n  return Config.new(git_config[0]), 0\nend\n\n---Get the value of a long integer config variable.\n---@param name string config name\n---@return integer?\n---@return GIT_ERROR\nfunction Config:get_int(name)\n  local out = libgit2.int64_array(1)\n  local err = libgit2.C.git_config_get_int64(out, self.config, name)\n  if err ~= 0 then\n    return nil, 0\n  end\n\n  return tonumber(out[0]), 0\nend\n\n---Get the value of a boolean config variable.\n---@param name string config name\n---@return boolean?\n---@return GIT_ERROR\nfunction Config:get_bool(name)\n  local out = libgit2.int_array(1)\n  local err = libgit2.C.git_config_get_bool(out, self.config, name)\n  if err ~= 0 then\n    return nil, 0\n  end\n\n  return (out ~= 0), 0\nend\n\n---Get the value of a string config variable.\n---@param name string config name\n---@return string?\n---@return GIT_ERROR\nfunction Config:get_string(name)\n  local buf = libgit2.git_buf()\n\n  local err = libgit2.C.git_config_get_string_buf(buf, self.config, name)\n  if err ~= 0 then\n    libgit2.C.git_buf_dispose(buf)\n    return nil, err\n  end\n\n  local str = ffi.string(buf[0].ptr, buf[0].size)\n  libgit2.C.git_buf_dispose(buf)\n  return str, 0\nend\n\n---Get all entries\n---@return GitConfigEntry[]?\n---@return GIT_ERROR\nfunction Config:entries()\n  local iter = libgit2.git_config_iterator_double_pointer()\n  local err = libgit2.C.git_config_iterator_new(iter, self.config)\n  if err ~= 0 then\n    return nil, err\n  end\n\n  local git_config_entry = libgit2.git_config_entry_double_pointer()\n  local entries = {}\n\n  while libgit2.C.git_config_next(git_config_entry, iter[0]) == 0 do\n    ---@type GitConfigEntry\n    local entry = {\n      name = ffi.string(git_config_entry[0].name),\n      value = ffi.string(git_config_entry[0].value),\n      include_depth = tonumber(git_config_entry[0].include_depth) or -1,\n      level = tonumber(git_config_entry[0].level) or -1,\n    }\n    entries[#entries + 1] = entry\n\n    -- libgit2.C.git_config_entry_free(git_config_entry[0])\n  end\n\n  libgit2.C.git_config_iterator_free(iter[0])\n\n  return entries, 0\nend\n\n-- ========================\n-- | Git Object functions |\n-- ========================\n\n---@param git_object ffi.cdata* libgit2.git_object_pointer, own cdata.\n---@return GitObject\nfunction Object.new(git_object)\n  local object = { obj = libgit2.git_object_pointer(git_object) }\n  setmetatable(object, Object)\n\n  ffi.gc(object.obj, libgit2.C.git_object_free)\n  return object\nend\n\nfunction Object.borrow(git_object)\n  local object = { obj = libgit2.git_object_pointer(git_object) }\n  setmetatable(object, Object)\n  return object\nend\n\n-- Get the id (SHA1) of a repository object.\n---@return GitObjectId\nfunction Object:id()\n  local oid = libgit2.C.git_object_id(self.obj[0])\n  return ObjectId.borrow(oid)\nend\n\n-- Cast GitObject to GitBlob, the reference still be owned by main GitObject.\n---@return GitBlob\nfunction Object:as_blob()\n  return Blob.borrow(ffi.cast(libgit2.git_blob_pointer, self.obj))\nend\n\n-- Lookups an object that represents a tree entry from this treeish object.\n---@param path string relative path from the root object to the desired object.\n---@param object_type GIT_OBJECT type of object desired.\n---@return GitObject?\n---@return GIT_ERROR\nfunction Object:lookup_by_path(path, object_type)\n  local obj_out = libgit2.git_object_double_pointer()\n  local err = libgit2.C.git_object_lookup_bypath(obj_out, self.obj, path, object_type)\n  if err ~= 0 then\n    return nil, err\n  end\n\n  return Object.new(obj_out[0]), 0\nend\n\n-- ======================\n-- | ObjectId functions |\n-- ======================\n\n---Creates new libgit2 oid, then copy value from old oid.\n---@param oid GitObjectId\n---@return GitObjectId?\n---@return GIT_ERROR\nfunction ObjectId.from(oid)\n  local git_object_id = libgit2.git_oid()\n\n  local err = libgit2.C.git_oid_cpy(git_object_id, oid.oid)\n  if err ~= 0 then\n    return nil, err\n  end\n\n  return ObjectId.borrow(git_object_id), 0\nend\n\n---Creates new libgit2 oid, then copy value from old oid.\n---@param git_oid ffi.cdata*\n---@return GitObjectId? Objectid\n---@return GIT_ERROR\nfunction ObjectId.from_git_oid(git_oid)\n  local git_object_id = libgit2.git_oid()\n\n  local err = libgit2.C.git_oid_cpy(git_object_id, git_oid)\n  if err ~= 0 then\n    return nil, err\n  end\n\n  return ObjectId.borrow(git_object_id), 0\nend\n\n-- Creates new ObjectId from string\n---@param oid_str string oid hex string\n---@return GitObjectId?\n---@return GIT_ERROR\nfunction ObjectId.from_string(oid_str)\n  local git_object_id = libgit2.git_oid()\n  local err = libgit2.C.git_oid_fromstrn(git_object_id, oid_str, oid_str:len())\n  if err ~= 0 then\n    return nil, err\n  end\n  return ObjectId.borrow(git_object_id), 0\nend\n\n---@param oid ffi.cdata* libgit2 git_oid*, borrow data\nfunction ObjectId.borrow(oid)\n  local object_id = { oid = ffi.cast(libgit2.git_oid_pointer, oid) }\n  setmetatable(object_id, ObjectId)\n  return object_id\nend\n\n---Creates a new ObjectId with the same value of the old one.\n---@return GitObjectId?\n---@return GIT_ERROR\nfunction ObjectId:clone()\n  return ObjectId.from(self)\nend\n\n---Sets this oid the same value as the given oid\n---@param oid GitObjectId\n---@return GIT_ERROR\nfunction ObjectId:copy_from(oid)\n  return libgit2.C.git_oid_cpy(self.oid, oid.oid)\nend\n\n---Copies this oid to target oid.\n---@param oid GitObjectId\n---@return GIT_ERROR\nfunction ObjectId:copy_to(oid)\n  return libgit2.C.git_oid_cpy(oid.oid, self.oid)\nend\n\n---@param n integer? number of git id\n---@return string\nfunction ObjectId:tostring(n)\n  if not n or n < 0 or n > 40 then\n    n = 40\n  end\n\n  local c_buf = libgit2.char_array(n + 1)\n  libgit2.C.git_oid_tostr(c_buf, n + 1, self.oid)\n  return ffi.string(c_buf, n)\nend\n\n---@param oid_str string hex formatted object id.\n---@return boolean\nfunction ObjectId:streq(oid_str)\n  return (libgit2.C.git_oid_streq(self.oid, oid_str) == 0)\nend\n\n---@return string\nfunction ObjectId:__tostring()\n  return self:tostring(8)\nend\n\n---@param a GitObjectId\n---@param b GitObjectId | string\n---@return boolean\nfunction ObjectId.__eq(a, b)\n  if type(b) == \"string\" then\n    return (libgit2.C.git_oid_streq(a.oid, b) == 0)\n  end\n  return (libgit2.C.git_oid_equal(a.oid, b.oid) ~= 0)\nend\n\n-- ===================\n-- | Git Blob object |\n-- ===================\n\n---@param git_blob ffi.cdata* libgit2.git_blob_pointer, own cdata\n---@return GitBlob\nfunction Blob.new(git_blob)\n  local blob = { blob = libgit2.git_blob_pointer(git_blob) }\n  setmetatable(blob, Blob)\n\n  ffi.gc(blob.blob, libgit2.C.git_blob_free)\n  return blob\nend\n\n---@param git_blob ffi.cdata* libgit2.git_blob_pointer, doesn't own data\nfunction Blob.borrow(git_blob)\n  local blob = { blob = libgit2.git_blob_pointer(git_blob) }\n  setmetatable(blob, Blob)\n  return blob\nend\n\n---@return GitObjectId\nfunction Blob:id()\n  local oid = libgit2.C.git_blob_id(self.blob)\n  return ObjectId.borrow(oid)\nend\n\n---@return boolean\nfunction Blob:is_binary()\n  local ret = libgit2.C.git_blob_is_binary(self.blob)\n  return ret == 1\nend\n\n---Gets a raw content of a blob.\n---@return string\nfunction Blob:content()\n  local content = libgit2.C.git_blob_rawcontent(self.blob)\n  local len = libgit2.C.git_blob_rawsize(self.blob)\n  return ffi.string(content, len)\nend\n\n-- ============================\n-- | Git Tree Entry functions |\n-- ============================\n\n---@param git_entry ffi.cdata* libgit2.git_tree_entry_pointer, own cdata\n---@return GitTreeEntry\nfunction TreeEntry.new(git_entry)\n  local tree_entry = { entry = libgit2.git_tree_entry_pointer(git_entry) }\n  setmetatable(tree_entry, TreeEntry)\n\n  ffi.gc(tree_entry.entry, libgit2.C.git_tree_entry_free)\n  return tree_entry\nend\n\n---@param entry ffi.cdata* libgit2.git_tree_entry_pointer, just borrow data, didn't own\n---@return GitTreeEntry\nfunction TreeEntry.borrow(entry)\n  local git_tree_entry = { entry = entry }\n  setmetatable(git_tree_entry, TreeEntry)\n  return git_tree_entry\nend\n\n---Gets the id of the object pointed by the entry\n---@return GitObjectId\nfunction TreeEntry:id()\n  local oid = libgit2.C.git_tree_entry_id(self.entry)\n  return ObjectId.borrow(oid)\nend\n\n---Gets the filename of a tree entry.\n---@return string\nfunction TreeEntry:name()\n  local c_name = libgit2.C.git_tree_entry_name(self.entry)\n  return ffi.string(c_name)\nend\n\n---@return GIT_OBJECT\nfunction TreeEntry:type()\n  return libgit2.C.git_tree_entry_type(self.entry)\nend\n\n---@param repo GitRepository\n---@return GitObject?\n---@return GIT_ERROR\nfunction TreeEntry:to_object(repo)\n  local git_object = libgit2.git_object_double_pointer()\n  local err = libgit2.C.git_tree_entry_to_object(git_object, repo.repo, self.entry)\n  if err ~= 0 then\n    return nil, err\n  end\n\n  return Object.new(git_object[0]), 0\nend\n\n-- ===================\n-- | Git Tree object |\n-- ===================\n\n---@param git_tree ffi.cdata* libgit2.git_tree_pointer, own cdata\n---@return GitTree\nfunction Tree.new(git_tree)\n  local tree = { tree = libgit2.git_tree_pointer(git_tree) }\n  setmetatable(tree, Tree)\n\n  ffi.gc(tree.tree, libgit2.C.git_tree_free)\n  return tree\nend\n\n-- Cast Tree to GitObject, the reference still be owned by main Tree.\n---@return GitObject\nfunction Tree:as_object()\n  return Object.borrow(ffi.cast(libgit2.git_object_pointer, self.tree))\nend\n\n-- Gets the id of a tree.\nfunction Tree:id()\n  local oid = libgit2.C.git_tree_id(self.tree)\n  return ObjectId.borrow(oid)\nend\n\nfunction Tree:nentries()\n  return libgit2.C.git_tree_entrycount(self.tree)\nend\n\n---Retrieves a tree entry contained in a tree\n---or in any of its subtrees, given its relative path.\n---@param path string\n---@return GitTreeEntry?\n---@return GIT_ERROR\nfunction Tree:entry_bypath(path)\n  local entry = libgit2.git_tree_entry_double_pointer()\n  local err = libgit2.C.git_tree_entry_bypath(entry, self.tree, path)\n  if err ~= 0 then\n    return nil, err\n  end\n  return TreeEntry.new(entry[0]), 0\nend\n\n---Lookup a tree entry by its filename\n---@param filename string\n---@return GitTreeEntry?\nfunction Tree:entry_byname(filename)\n  local entry = libgit2.C.git_tree_entry_byname(self.tree, filename)\n  if entry == nil then\n    return nil\n  end\n  return TreeEntry.borrow(entry)\nend\n\n---Lookup a tree entry by SHA value.\n---@param id GitObjectId\n---@return GitTreeEntry?\nfunction Tree:entry_byid(id)\n  local entry = libgit2.C.git_tree_entry_byid(self.tree, id.oid)\n  if entry == nil then\n    return nil\n  end\n  return TreeEntry.borrow(entry)\nend\n\n-- ==================\n-- | Git Tag object |\n-- ==================\n\n---@param git_tag ffi.cdata* libgit2.git_tag_pointer, own cdata\n---@return GitTag\nfunction Tag.new(git_tag)\n  local tag = { tag = libgit2.git_tag_pointer(git_tag) }\n  setmetatable(tag, Tag)\n\n  tag.name = ffi.string(libgit2.C.git_tag_name(git_tag))\n  ffi.gc(tag.tag, libgit2.C.git_tag_free)\n\n  return tag\nend\n\n---Get the name of a tag\nfunction Tag:__tostring()\n  return self.name\nend\n\n-- =============================\n-- | AnnotatedCommit functions |\n-- =============================\n\n---Init GitAnnotatedCommit\n---@param git_commit ffi.cdata* libgit2.git_annotated_commit_pointer, this owns cdata.\n---@return GitAnnotatedCommit\nfunction AnnotatedCommit.new(git_commit)\n  local commit = { commit = libgit2.git_annotated_commit_pointer(git_commit) }\n  setmetatable(commit, AnnotatedCommit)\n\n  ffi.gc(commit.commit, libgit2.C.git_annotated_commit_free)\n  return commit\nend\n\n-- =================\n-- | Blame methods |\n-- =================\n\n-- Inits GitBlame object\nfunction Blame.new(git_blame)\n  local blame = { blame = libgit2.git_blame_pointer(git_blame) }\n  setmetatable(blame, Blame)\n\n  ffi.gc(blame.blame, libgit2.C.git_blame_free)\n  return blame\nend\n\n-- Gets blame data for a file that has been modified in memory\n-- Self is the pre-calculated blame for the in-odb history of the file.\n---@param buf string\n---@return GitBlame?\n---@return GIT_ERROR\nfunction Blame:blame_buffer(buf)\n  local git_blame = libgit2.git_blame_double_pointer()\n  local err = libgit2.C.git_blame_buffer(git_blame, self.blame, buf, buf:len())\n  if err ~= 0 then\n    return nil, err\n  end\n\n  return Blame.new(git_blame[0]), 0\nend\n\n---@return integer\nfunction Blame:nhunks()\n  local count = libgit2.C.git_blame_get_hunk_count(self.blame)\n  return tonumber(count) or 0\nend\n\n---@param i integer hunk index\n---@return GitBlameHunk?\nfunction Blame:hunk(i)\n  local blame = libgit2.C.git_blame_get_hunk_byindex(self.blame, i)\n  return blame ~= nil and BlameHunk.borrow(blame) or nil\nend\n\n---@param line integer line number, 1-th based index\n---@return GitBlameHunk?\nfunction Blame:hunk_byline(line)\n  local blame = libgit2.C.git_blame_get_hunk_byline(self.blame, line)\n  return blame ~= nil and BlameHunk.borrow(blame) or nil\nend\n\n-- =====================\n-- | BlameHunk methods |\n-- =====================\n\n---@return GitBlameHunk\nfunction BlameHunk.borrow(git_blame_hunk)\n  ---@type GitBlameHunk\n  local hunk = {\n    hunk = libgit2.git_blame_hunk_pointer(git_blame_hunk),\n    num_lines = tonumber(git_blame_hunk[\"lines_in_hunk\"]) or 0,\n    final_start_line_number = tonumber(git_blame_hunk[\"final_start_line_number\"]) or 0,\n    orig_start_line_number = tonumber(git_blame_hunk[\"orig_start_line_number\"]) or 0,\n    boundary = (git_blame_hunk[\"boundary\"] == 1),\n  }\n  setmetatable(hunk, BlameHunk)\n  return hunk\nend\n\n---@return GitSignature?\nfunction BlameHunk:final_signature()\n  local sig = self.hunk[\"final_signature\"]\n  return sig ~= nil and Signature.borrow(sig) or nil\nend\n\n---@return GitSignature?\nfunction BlameHunk:orig_signature()\n  local sig = self.hunk[\"orig_signature\"]\n  return sig ~= nil and Signature.borrow(sig) or nil\nend\n\n---@return GitObjectId\nfunction BlameHunk:final_commit_id()\n  return ObjectId.borrow(self.hunk[\"final_commit_id\"])\nend\n\n---@return GitObjectId\nfunction BlameHunk:orig_commit_id()\n  return ObjectId.borrow(self.hunk[\"orig_commit_id\"])\nend\n\n---@return string\nfunction BlameHunk:orig_path()\n  return ffi.string(self.hunk[\"orig_path\"])\nend\n\n-- ====================\n-- | Commit functions |\n-- ====================\n\n-- Init GitCommit.\n---@param git_commit ffi.cdata* libgit2.git_commit_pointer, this owns the data.\n---@return GitCommit\nfunction Commit.new(git_commit)\n  local commit = { commit = libgit2.git_commit_pointer(git_commit) }\n  setmetatable(commit, Commit)\n\n  -- ffi garbage collector\n  ffi.gc(commit.commit, libgit2.C.git_commit_free)\n\n  return commit\nend\n\n-- Gets the id of a commit.\n---@return GitObjectId\nfunction Commit:id()\n  local git_oid = libgit2.C.git_commit_id(self.commit)\n  return ObjectId.borrow(git_oid)\nend\n\n---@return osdate\nfunction Commit:time()\n  local time = tonumber(libgit2.C.git_commit_time(self.commit))\n  return os.date(\"*t\", time) --[[@as osdate>]]\nend\n\n-- Gets GitCommit messages.\n---@return string message\nfunction Commit:message()\n  local c_char = libgit2.C.git_commit_message(self.commit)\n  return vim.trim(ffi.string(c_char))\nend\n\n-- Gets the short \"summary\" of the git commit message.\n---@return string summary\nfunction Commit:summary()\n  local c_char = libgit2.C.git_commit_summary(self.commit)\n  return ffi.string(c_char)\nend\n\n-- Gets the body of the git commit message.\n---@return string body\nfunction Commit:body()\n  local c_char = libgit2.C.git_commit_body(self.commit)\n  if c_char == nil then\n    return \"\"\n  end\n  return ffi.string(c_char)\nend\n\n---@return string\nfunction Commit:author()\n  local author = libgit2.C.git_commit_author(self.commit)\n  return ffi.string(author.name)\nend\n\n---@return string\nfunction Commit:committer()\n  local committer = libgit2.C.git_commit_committer(self.commit)\n  return ffi.string(committer.name)\nend\n\n---@return GitSignature\nfunction Commit:author_signature()\n  local author = libgit2.C.git_commit_author(self.commit)\n  return Signature.borrow(ffi.cast(libgit2.git_signature_pointer, author))\nend\n\n---@return GitSignature\nfunction Commit:committer_signature()\n  local committer = libgit2.C.git_commit_committer(self.commit)\n  return Signature.borrow(ffi.cast(libgit2.git_signature_pointer, committer))\nend\n\n-- Gets the number of parents of this commit\n---@return integer parentcount\nfunction Commit:nparents()\n  return libgit2.C.git_commit_parentcount(self.commit)\nend\n\n-- Gets the specified parent of the commit.\n---@param i integer Parent index (0-based)\n---@return GitCommit?\n---@return GIT_ERROR\nfunction Commit:parent(i)\n  local c_commit = libgit2.git_commit_double_pointer()\n  local err = libgit2.C.git_commit_parent(c_commit, self.commit, i)\n  if err ~= 0 then\n    return nil, err\n  end\n\n  return Commit.new(c_commit[0]), 0\nend\n\n-- Gets the oids of all parents\n---@return GitObjectId[]\nfunction Commit:parent_oids()\n  local nparents = self:nparents()\n  local parents = table_new(nparents, 0)\n  if nparents < 1 then\n    return parents\n  end\n\n  for i = 0, nparents - 1 do\n    local oid = libgit2.C.git_commit_parent_id(self.commit, i)\n    parents[i + 1] = ObjectId.borrow(oid)\n  end\n\n  return parents\nend\n\n-- Gets the tree pointed to by a commit.\n---@return GitTree?\n---@return GIT_ERROR\nfunction Commit:tree()\n  local git_tree = libgit2.git_tree_double_pointer()\n  local err = libgit2.C.git_commit_tree(git_tree, self.commit)\n  if err ~= 0 then\n    return nil, err\n  end\n\n  return Tree.new(git_tree[0]), 0\nend\n\n-- =======================\n-- | Reference functions |\n-- =======================\n\n---@param refname string\n---@return GIT_REFERENCE_NAMESPACE\nlocal function reference_name_namespace(refname)\n  if vim.startswith(refname, \"refs/\") then\n    local namespace = string.sub(refname, string.len \"refs/\" + 1)\n    if vim.startswith(namespace, \"heads/\") then\n      return GIT_REFERENCE_NAMESPACE.BRANCH\n    elseif vim.startswith(namespace, \"tags/\") then\n      return GIT_REFERENCE_NAMESPACE.TAG\n    elseif vim.startswith(namespace, \"remotes/\") then\n      return GIT_REFERENCE_NAMESPACE.REMOTE\n    elseif vim.startswith(namespace, \"notes/\") then\n      return GIT_REFERENCE_NAMESPACE.NOTE\n    end\n  end\n\n  return GIT_REFERENCE_NAMESPACE.NONE\nend\n\n---@param refname string full refname\n---@return string\nlocal function reference_name_shorthand(refname)\n  local namespace = reference_name_namespace(refname)\n  if namespace ~= GIT_REFERENCE_NAMESPACE.NONE then\n    return refname:sub(GIT_REFERENCE_PREFIX[namespace])\n  end\n  return refname\nend\n\n---@param refname string\n---@return string?\nlocal function reference_name_remote(refname)\n  return refname:match \"refs/remotes/(%a+)/\"\nend\n\n---Creates new Reference object\n---@param git_reference ffi.cdata* libgit2.git_reference_pointer, own cdata\n---@return GitReference\nfunction Reference.new(git_reference)\n  local ref = {\n    ref = libgit2.git_reference_pointer(git_reference),\n    namespace = GIT_REFERENCE_NAMESPACE.NONE,\n  }\n  setmetatable(ref, Reference)\n\n  local c_name = libgit2.C.git_reference_name(ref.ref)\n  ref.name = ffi.string(c_name)\n\n  ref.namespace = reference_name_namespace(ref.name)\n  ref.type = libgit2.C.git_reference_type(ref.ref)\n\n  -- ffi garbage collector\n  ffi.gc(ref.ref, libgit2.C.git_reference_free)\n\n  return ref\nend\n\nfunction Reference:__tostring()\n  return string.format(\"Git Ref (%s): %s\", GIT_REFERENCE_STRING[self.type + 1], self.name)\nend\n\n-- Transforms the reference name into a name \"human-readable\" version.\n---@return string # Shorthand for ref\nfunction Reference:shorthand()\n  local c_name = libgit2.C.git_reference_shorthand(self.ref)\n  return ffi.string(c_name)\nend\n\n-- Gets target for a GitReference\n---@return GitObjectId?\n---@return GIT_ERROR\nfunction Reference:target()\n  if self.type == libgit2.GIT_REFERENCE.SYMBOLIC then\n    local resolved = libgit2.git_reference_double_pointer()\n\n    local err = libgit2.C.git_reference_resolve(resolved, self.ref)\n    if err ~= 0 then\n      return nil, err\n    end\n\n    local oid = libgit2.C.git_reference_target(resolved)\n    libgit2.C.git_reference_free(resolved)\n\n    return ObjectId.borrow(oid), 0\n  elseif self.type ~= 0 then\n    local oid = libgit2.C.git_reference_target(self.ref)\n    return ObjectId.borrow(oid), 0\n  end\n\n  return nil, 0\nend\n\n---Conditionally creates a new reference\n---with the same name as the given reference.\n---@param oid GitObjectId\n---@param message string\n---@return GitReference?\n---@return GIT_ERROR\nfunction Reference:set_target(oid, message)\n  local ref = libgit2.git_reference_double_pointer()\n  local err = libgit2.C.git_reference_set_target(ref, self.ref, oid.oid, message)\n  if err ~= 0 then\n    return nil, err\n  end\n\n  return Reference.new(ref[0]), 0\nend\n\n---Resolves a symbolic reference to a direct reference.\n---@return GitReference? ref git reference\n---@return GIT_ERROR err error code\nfunction Reference:resolve()\n  local ref = libgit2.git_reference_double_pointer()\n  local err = libgit2.C.git_reference_resolve(ref, self.ref)\n  if err ~= 0 then\n    return nil, err\n  end\n\n  return Reference.new(ref[0]), 0\nend\n\n---Recursively peel reference until object of the specified type is found.\n---@param type GIT_OBJECT\n---@return GitObject?\n---@return integer Git Error code\nfunction Reference:peel(type)\n  local c_object = libgit2.git_object_double_pointer()\n\n  local err = libgit2.C.git_reference_peel(c_object, self.ref, type)\n  if err ~= 0 then\n    return nil, err\n  end\n\n  return Object.new(c_object[0]), 0\nend\n\n-- Recursively peel reference until commit object is found.\n---@return GitCommit?\n---@return GIT_ERROR err libgit2 Error code\nfunction Reference:peel_commit()\n  local c_object = libgit2.git_object_double_pointer()\n\n  local err = libgit2.C.git_reference_peel(c_object, self.ref, libgit2.GIT_OBJECT.COMMIT)\n  if err ~= 0 then\n    return nil, err\n  end\n\n  return Commit.new(ffi.cast(libgit2.git_commit_pointer, c_object[0])), 0\nend\n\n---Recursively peel reference until tree object is found.\n---@return GitTree?\n---@return GIT_ERROR err libgit2 Error code\nfunction Reference:peel_tree()\n  local c_object = libgit2.git_object_double_pointer()\n\n  local err = libgit2.C.git_reference_peel(c_object, self.ref, libgit2.GIT_OBJECT.TREE)\n  if err ~= 0 then\n    return nil, err\n  end\n\n  return Tree.new(ffi.cast(libgit2.git_tree_pointer, c_object[0])), 0\nend\n\n---Gets upstream for a branch.\n---@return GitReference? Reference git upstream reference\n---@return GIT_ERROR\nfunction Reference:branch_upstream()\n  if self.namespace ~= GIT_REFERENCE_NAMESPACE.BRANCH then\n    return nil, 0\n  end\n\n  local c_ref = libgit2.git_reference_double_pointer()\n  local err = libgit2.C.git_branch_upstream(c_ref, self.ref)\n  if err ~= 0 then\n    return nil, err\n  end\n\n  return Reference.new(c_ref[0]), 0\nend\n\n---Retrieves the upstream remote name of a remote_reference.\n---@return string?\nfunction Reference:remote_name()\n  if self.namespace == GIT_REFERENCE_NAMESPACE.REMOTE then\n    return self.name:match(\"remotes/([^/]+)/\", 6)\n  end\nend\n\n---Get full name to the reference pointed to by a symbolic reference.\n---@return string?\nfunction Reference:symbolic_target()\n  if bit.band(self.type, libgit2.GIT_REFERENCE.SYMBOLIC) ~= 0 then\n    return ffi.string(libgit2.C.git_reference_symbolic_target(self.ref))\n  end\nend\n\n-- ============================\n-- | RevisionWalker functions |\n-- ============================\n\n-- Inits new GitRevisionWalker object.\n---@param repo ffi.cdata* libgit2.git_respository_pointer, don't own data\n---@param revwalk ffi.cdata* libgit2.git_revwalk_pointer, own cdata\n---@return GitRevisionWalker\nfunction RevisionWalker.new(repo, revwalk)\n  local git_walker = {\n    repo = libgit2.git_repository_pointer(repo),\n    revwalk = libgit2.git_revwalk_pointer(revwalk),\n  }\n  setmetatable(git_walker, RevisionWalker)\n\n  ffi.gc(git_walker.revwalk, libgit2.C.git_revwalk_free)\n  return git_walker\nend\n\n---@return GIT_ERROR\nfunction RevisionWalker:reset()\n  return libgit2.C.git_revwalk_reset(self.revwalk)\nend\n\n---@param topo boolean sort in topo order\n---@param time boolean sort by time\n---@param reverse boolean reverse\n---@return GIT_ERROR\nfunction RevisionWalker:sort(topo, time, reverse)\n  if not (topo or time or reverse) then\n    return 0\n  end\n\n  local mode = 0ULL\n  if topo then\n    mode = bit.bor(mode, libgit2.GIT_SORT.TOPOLOGICAL)\n  end\n  if time then\n    mode = bit.bor(mode, libgit2.GIT_SORT.TIME)\n  end\n  if reverse then\n    mode = bit.bor(mode, libgit2.GIT_SORT.REVERSE)\n  end\n\n  return libgit2.C.git_revwalk_sorting(self.revwalk, mode)\nend\n\n---@param oid GitObjectId\n---@return GIT_ERROR\nfunction RevisionWalker:push(oid)\n  return libgit2.C.git_revwalk_push(self.revwalk, oid.oid)\nend\n\n---@return GIT_ERROR\nfunction RevisionWalker:push_head()\n  return libgit2.C.git_revwalk_push_head(self.revwalk)\nend\n\n---Push matching references\n---@param glob string\n---@return GIT_ERROR\nfunction RevisionWalker:push_glob(glob)\n  return libgit2.C.git_revwalk_push_glob(self.revwalk, glob)\nend\n\n---Push the OID pointed to by a reference\n---@param refname string\n---@return GIT_ERROR\nfunction RevisionWalker:push_ref(refname)\n  return libgit2.C.git_revwalk_push_ref(self.revwalk, refname)\nend\n\n---@param oid GitObjectId\n---@return GIT_ERROR\nfunction RevisionWalker:hide(oid)\n  return libgit2.C.git_revwalk_hide(self.revwalk, oid.oid)\nend\n\n---Gets next oid, commit in the walker\n---@return GitObjectId?\n---@return GitCommit?\n---@return GIT_ERROR err error codd\nfunction RevisionWalker:next()\n  local git_oid = libgit2.git_oid()\n  local err = libgit2.C.git_revwalk_next(git_oid, self.revwalk)\n  if err ~= 0 then\n    return nil, nil, err\n  end\n\n  local c_commit = libgit2.git_commit_double_pointer()\n  err = libgit2.C.git_commit_lookup(c_commit, self.repo, git_oid)\n  if err ~= 0 then\n    return nil, nil, err\n  end\n\n  return ObjectId.borrow(git_oid), Commit.new(c_commit[0]), 0\nend\n\n---Iterates through git_oid revisions.\n---@return fun(): GitObjectId?, GitCommit?\nfunction RevisionWalker:iter()\n  local git_oid = libgit2.git_oid()\n\n  return function()\n    local err = libgit2.C.git_revwalk_next(git_oid, self.revwalk)\n    if err ~= 0 then\n      return nil, nil\n    end\n\n    local c_commit = libgit2.git_commit_double_pointer()\n    err = libgit2.C.git_commit_lookup(c_commit, self.repo, git_oid)\n    if err ~= 0 then\n      return nil, nil\n    end\n\n    return ObjectId.borrow(git_oid), Commit.new(c_commit[0])\n  end\nend\n\n-- ====================\n-- | Remote functions |\n-- ====================\n\n-- Inits new GitRemote object.\n---@param git_remote ffi.cdata* libgit2.git_remote_pointer, own data\n---@return GitRemote\nfunction Remote.new(git_remote)\n  local remote = { remote = libgit2.git_remote_pointer(git_remote) }\n  setmetatable(remote, Remote)\n\n  remote.name = ffi.string(libgit2.C.git_remote_name(remote.remote))\n  remote.url = ffi.string(libgit2.C.git_remote_url(remote.remote))\n  local push_url = libgit2.C.git_remote_pushurl(remote.remote)\n  if push_url ~= nil then\n    remote.push_url = ffi.string(push_url)\n  end\n\n  ffi.gc(remote.remote, libgit2.C.git_remote_free)\n\n  return remote\nend\n\n-- =======================\n-- | Signature functions |\n-- =======================\n\n---@param git_signature ffi.cdata* libgit2.git_signature_pointer, own data\nfunction Signature.new(git_signature)\n  local signature = { sign = libgit2.git_signature_pointer(git_signature) }\n  setmetatable(signature, Signature)\n\n  ffi.gc(signature.sign, libgit2.C.git_signature_free)\n  return signature\nend\n\nfunction Signature.borrow(git_signature)\n  local signature = { sign = libgit2.git_signature_pointer(git_signature) }\n  setmetatable(signature, Signature)\n  return signature\nend\n\n---@return string\nfunction Signature:name()\n  return ffi.string(self.sign[\"name\"])\nend\n\n---@return string\nfunction Signature:email()\n  return ffi.string(self.sign[\"email\"])\nend\n\nfunction Signature:__tostring()\n  return string.format(\"%s <%s>\", self:name(), self:email())\nend\n\n-- ===========================\n-- | GitIndexEntry functions |\n-- ===========================\n\n-- Inits new GitIndexEntry object.\n---@param git_index_entry ffi.cdata* libgit2.git_index_entry_pointer, don't own data\n---@return GitIndexEntry\nfunction IndexEntry.borrow(git_index_entry)\n  local entry = { entry = libgit2.git_index_entry_pointer(git_index_entry) }\n  setmetatable(entry, IndexEntry)\n  return entry\nend\n\n---@return integer\nlocal function git_index_create_mode(mode)\n  if stat.S_ISLNK(mode) then\n    return stat.S_IFLNK\n  end\n\n  local link_or_dir = bit.bor(stat.S_IFLNK, stat.S_IFDIR)\n  if stat.S_ISDIR(mode) or bit.band(mode, stat.S_IFMT) == link_or_dir then\n    return link_or_dir\n  end\n\n  return bit.bor(stat.S_IFREG, GIT_PERMS_CANONICAL(mode))\nend\n\n---@param fs_stat uv.fs_stat.result\n---@param path string file path\n---@param distrust boolean index distrust mode\n---@return GitIndexEntry\nfunction IndexEntry.from_stat(fs_stat, path, distrust)\n  local git_index_entry = libgit2.git_index_entry()\n  local entry = git_index_entry[0]\n  entry.ctime.seconds = fs_stat.ctime.sec\n  entry.ctime.nanoseconds = fs_stat.ctime.nsec\n  entry.mtime.seconds = fs_stat.mtime.sec\n  entry.mtime.nanoseconds = fs_stat.mtime.nsec\n  entry.dev = fs_stat.rdev\n  entry.ino = fs_stat.ino\n\n  if distrust and stat.S_ISREG(fs_stat.mode) then\n    entry.mode = git_index_create_mode(438) -- 0666\n  else\n    entry.mode = git_index_create_mode(fs_stat.mode)\n  end\n\n  entry.uid = fs_stat.uid\n  entry.gid = fs_stat.gid\n  entry.file_size = fs_stat.size\n  entry.path = path\n\n  return IndexEntry.borrow(git_index_entry)\nend\n\n-- Whether the given index entry is a conflict.\n---@return boolean is_conflict\nfunction IndexEntry:is_conflict()\n  local ret = libgit2.C.git_index_entry_is_conflict(self.entry)\n  return ret == 1\nend\n\n-- Get file_size of IndexEntry.\n---@return integer\nfunction IndexEntry:file_size()\n  return tonumber(self.entry[\"file_size\"]) or -1\nend\n\n-- Gets Git oid of IndexEntry.\n---@return GitObjectId\nfunction IndexEntry:id()\n  return ObjectId.borrow(self.entry[\"id\"])\nend\n\n-- Get path of IndexEntry.\n---@return string\nfunction IndexEntry:path()\n  return ffi.string(self.entry[\"path\"])\nend\n\n-- Get flags of IndexEntry.\n---@return integer\nfunction IndexEntry:flags()\n  return self.entry[\"flags\"]\nend\n\n-- ===================\n-- | Index functions |\n-- ===================\n\n-- Inits new GitIndex object.\n---@param git_index ffi.cdata* libgit2.git_index_pointer, own cdata\n---@return GitIndex\nfunction Index.new(git_index)\n  local index = { index = libgit2.git_index_pointer(git_index) }\n  setmetatable(index, Index)\n\n  ffi.gc(index.index, libgit2.C.git_index_free)\n\n  return index\nend\n\n-- Gets the count of entries currently in the index\n---@return integer\nfunction Index:nentries()\n  local entrycount = libgit2.C.git_index_entrycount(self.index)\n  return math.floor(tonumber(entrycount) or -1)\nend\n\n-- Updates the contents of an existing index object.\n---@param force boolean Performs hard read or not?\n---@return GIT_ERROR\nfunction Index:read(force)\n  return libgit2.C.git_index_read(self.index, force and 1 or 0)\nend\n\n-- Writes index from memory to file.\n---@return GIT_ERROR\nfunction Index:write()\n  return libgit2.C.git_index_write(self.index)\nend\n\n-- Write the index as a tree\n---@return GitObjectId?\n---@return GIT_ERROR\nfunction Index:write_tree()\n  local tree_oid = libgit2.git_oid()\n  local err = libgit2.C.git_index_write_tree(tree_oid, self.index)\n  if err ~= 0 then\n    return nil, err\n  end\n  return ObjectId.borrow(tree_oid), 0\nend\n\n-- Get the full path to the index file on disk.\n---@return string? path to index file or NULL for in-memory index\nfunction Index:path()\n  local path = libgit2.C.git_index_path(self.index)\n  if path ~= nil then\n    return ffi.string(path)\n  end\n  return nil\nend\n\n-- Checks index in-memory or not\n---return boolean inmemory Index in memory\nfunction Index:in_memory()\n  local path = libgit2.C.git_index_path(self.index)\n  return path == nil\nend\n\n-- Adds path to index.\n---@param path string File path to be added.\n---@return GIT_ERROR\nfunction Index:add_bypath(path)\n  return libgit2.C.git_index_add_bypath(self.index, path)\nend\n\n-- Adds or update an index entry from a buffer in memory\n---@param entry GitIndexEntry\n---@param buffer string String buffer\n---@return GIT_ERROR\nfunction Index:add_from_buffer(entry, buffer)\n  return libgit2.C.git_index_add_from_buffer(self.index, entry.entry, buffer, buffer:len())\nend\n\n-- Removes path from index.\n---@param path string File path to be removed.\n---@return GIT_ERROR\nfunction Index:remove_bypath(path)\n  return libgit2.C.git_index_remove_bypath(self.index, path)\nend\n\n-- Determine if the index contains entries representing file conflicts.\n---@return boolean has_conflicts\nfunction Index:has_conflicts()\n  return (libgit2.C.git_index_has_conflicts(self.index) > 0)\nend\n\n-- Gets index entry by path\n---@param path string\n---@param stage_number GIT_INDEX_STAGE\n---@return GitIndexEntry?\nfunction Index:get_bypath(path, stage_number)\n  local entry = libgit2.C.git_index_get_bypath(self.index, path, stage_number)\n  if entry == nil then\n    return nil\n  end\n\n  return IndexEntry.borrow(entry)\nend\n\n-- Iterates through entries in the index.\n---@return (fun(): GitIndexEntry?)?\nfunction Index:iter()\n  local entry = libgit2.git_index_entry_double_pointer()\n  local iterator = libgit2.git_index_iterator_double_pointer()\n  local err = libgit2.C.git_index_iterator_new(iterator, self.index)\n  if err ~= 0 then\n    return nil\n  end\n\n  return function()\n    err = libgit2.C.git_index_iterator_next(entry, iterator[0])\n    if err ~= 0 then\n      libgit2.C.git_index_iterator_free(iterator[0])\n      return nil\n    end\n\n    return IndexEntry.borrow(entry[0])\n  end\nend\n\n-- Gets conflicst entries\n---@param path string path to get conflict\n---@return GitIndexEntry? ancestor\n---@return GitIndexEntry? our side\n---@return GitIndexEntry? their side\n---@return GIT_ERROR\nfunction Index:get_conflict(path)\n  local entries = libgit2.git_index_entry_pointer_array(3)\n\n  local err = libgit2.C.git_index_conflict_get(entries, entries + 1, entries + 2, self.index, path)\n  if err ~= 0 then\n    return nil, nil, nil, err\n  end\n\n  local ancestor_entry = IndexEntry.borrow(entries[0])\n  local our_entry = IndexEntry.borrow(entries[1])\n  local their_entry = IndexEntry.borrow(entries[2])\n\n  return ancestor_entry, our_entry, their_entry, 0\nend\n\n-- ==================\n-- | Diff functions |\n-- ==================\n\n---Create new GitDiff object\n---@param git_diff ffi.cdata* libgit2.git_diff_pointer, own cdata\n---@return GitDiff\nfunction Diff.new(git_diff)\n  local diff = { diff = libgit2.git_diff_pointer(git_diff) }\n  setmetatable(diff, Diff)\n\n  ffi.gc(diff.diff, libgit2.C.git_diff_free)\n\n  return diff\nend\n\n---@parm diff_str string\n---@return GitDiff?\n---@return GIT_ERROR\nfunction Diff.from_buffer(diff_str)\n  local diff = libgit2.git_diff_double_pointer()\n\n  local err = libgit2.C.git_diff_from_buffer(diff, diff_str, diff_str:len())\n  if err ~= 0 then\n    return nil, err\n  end\n\n  return Diff.new(diff[0]), 0\nend\n\n---@param format GIT_DIFF_FORMAT\n---@return string\nfunction Diff:tostring(format)\n  local buf = libgit2.git_buf()\n  local err = libgit2.C.git_diff_to_buf(buf, self.diff, format)\n  if err ~= 0 then\n    libgit2.C.git_buf_dispose(buf)\n    return \"\"\n  end\n\n  local diff = ffi.string(buf[0].ptr, buf[0].size)\n  libgit2.C.git_buf_dispose(buf)\n  return diff\nend\n\nfunction Diff:__tostring()\n  return self:tostring(libgit2.GIT_DIFF_FORMAT.PATCH)\nend\n\n---Gets accumulate diff statistics for all patches.\n---@return GitDiffStats?\n---@return GIT_ERROR\nfunction Diff:stats()\n  local diff_stats = libgit2.git_diff_stats_double_pointer()\n  local err = libgit2.C.git_diff_get_stats(diff_stats, self.diff)\n  if err ~= 0 then\n    return nil, err\n  end\n  ---@type GitDiffStats\n  local stats = {\n    changed = tonumber(libgit2.C.git_diff_stats_files_changed(diff_stats[0])) or 0,\n    insertions = tonumber(libgit2.C.git_diff_stats_insertions(diff_stats[0])) or 0,\n    deletions = tonumber(libgit2.C.git_diff_stats_deletions(diff_stats[0])) or 0,\n  }\n\n  libgit2.C.git_diff_stats_free(diff_stats[0])\n  return stats, 0\nend\n\n---Gets patches from diff as a list\n---@param sort_case_sensitive boolean\n---@return GitDiffPatchItem[]\n---@return GIT_ERROR\nfunction Diff:patches(sort_case_sensitive)\n  local num_deltas = tonumber(libgit2.C.git_diff_num_deltas(self.diff)) or 0\n  local patches = table_new(num_deltas, 0)\n  local err = 0\n\n  for i = 0, num_deltas - 1 do\n    local delta = libgit2.C.git_diff_get_delta(self.diff, i)\n\n    local c_patch = libgit2.git_patch_double_pointer()\n    err = libgit2.C.git_patch_from_diff(c_patch, self.diff, i)\n    if err ~= 0 then\n      break\n    end\n\n    local patch = Patch.new(c_patch[0])\n\n    ---@type GitDiffPatchItem\n    local patch_item = {\n      status = delta.status,\n      path = ffi.string(delta.old_file.path),\n      new_path = ffi.string(delta.new_file.path),\n      num_hunks = patch:nhunks(),\n      patch = patch,\n    }\n\n    table.insert(patches, patch_item)\n  end\n\n  if #patches > 0 and sort_case_sensitive then\n    -- sort patches by name\n    table.sort(patches, function(a, b)\n      return a.path < b.path\n    end)\n  end\n\n  return patches, err\nend\n\n-- ===================\n-- | Patch functions |\n-- ===================\n\n---Creates new GitPatch object\n---@param git_patch ffi.cdata* libgit2.git_patch_pointer, own cdata\n---@return GitPatch\nfunction Patch.new(git_patch)\n  local patch = { patch = libgit2.git_patch_pointer(git_patch) }\n  setmetatable(patch, Patch)\n\n  ffi.gc(patch.patch, libgit2.C.git_patch_free)\n\n  return patch\nend\n\n---Gets the content of a patch as a single diff text.\n---@return string\nfunction Patch:__tostring()\n  local buf = libgit2.git_buf()\n  local err = libgit2.C.git_patch_to_buf(buf, self.patch)\n  if err ~= 0 then\n    libgit2.C.git_buf_dispose(buf)\n    return \"\"\n  end\n\n  local patch = ffi.string(buf[0].ptr, buf[0].size)\n  libgit2.C.git_buf_dispose(buf)\n  return patch\nend\n\n---@return GitDiffStats?\n---@return GIT_ERROR\nfunction Patch:stats()\n  local number = libgit2.size_t_array(2)\n  local err = libgit2.C.git_patch_line_stats(nil, number, number + 1, self.patch)\n  if err ~= 0 then\n    return nil, err\n  end\n\n  return {\n    changed = 1,\n    insertions = tonumber(number[0]),\n    deletions = tonumber(number[1]),\n  }, 0\nend\n\n---Gets the number of hunks in a patch\n---@return integer\nfunction Patch:nhunks()\n  return tonumber(libgit2.C.git_patch_num_hunks(self.patch)) or 0\nend\n\n---@param idx integer Hunk index 0-based\n---@return GitDiffHunk?\n---@return GIT_ERROR\nfunction Patch:hunk(idx)\n  local num_lines = libgit2.size_t_array(1)\n  local hunk = libgit2.git_diff_hunk_double_pointer()\n\n  local err = libgit2.C.git_patch_get_hunk(hunk, num_lines, self.patch, idx)\n  if err ~= 0 then\n    return nil, err\n  end\n\n  ---@type GitDiffHunk\n  local diff_hunk = {\n    num_lines = tonumber(num_lines[0]) or 0,\n    old_start = hunk[0].old_start,\n    old_lines = hunk[0].old_lines,\n    new_start = hunk[0].new_start,\n    new_lines = hunk[0].new_lines,\n    header = ffi.string(hunk[0].header, hunk[0].header_len),\n  }\n\n  return diff_hunk, 0\nend\n\n---@param hunk_idx integer Hunk index 0-based\n---@param line_idx integer Line index in hunk, 0-based\n---@return GitDiffLine?\n---@return GIT_ERROR\nfunction Patch:hunk_line(hunk_idx, line_idx)\n  local diff_line = libgit2.git_diff_line_double_pointer()\n\n  local err = libgit2.C.git_patch_get_line_in_hunk(diff_line, self.patch, hunk_idx, line_idx)\n  if err ~= 0 then\n    return nil, err\n  end\n\n  ---@type GitDiffLine\n  local ret = {\n    origin = string.char(diff_line[0].origin),\n    old_lineno = diff_line[0].old_lineno,\n    new_lineno = diff_line[0].new_lineno,\n    num_lines = diff_line[0].num_lines,\n    content = ffi.string(diff_line[0].content, diff_line[0].content_len),\n  }\n  return ret, 0\nend\n\n---Gets the number of lines in a hunk.\n---@param i integer hunk index 0-th based.\n---@return integer num_lines number of lines in i-th hunk.\nfunction Patch:hunk_num_lines(i)\n  return libgit2.C.git_patch_num_lines_in_hunk(self.patch, i)\nend\n\n--- ============================\n-- | RebaseOperation functions |\n-- =============================\n\n---Borrow new RebaseOperation\n---@param operation_ptr ffi.cdata* libgit2 git_rebase_operation pointer\n---@return GitRebaseOperation\nfunction RebaseOperation.borrow(operation_ptr)\n  local op = {\n    operation = libgit2.git_rebase_operation_pointer(operation_ptr),\n  }\n  setmetatable(op, RebaseOperation)\n  return op\nend\n\n---Gets type of a rebase operation\n---@return GIT_REBASE_OPERATION\nfunction RebaseOperation:type()\n  return self.operation[\"type\"]\nend\n\n---Changes type of a rebase operation.\n---@param type GIT_REBASE_OPERATION\nfunction RebaseOperation:set_type(type)\n  self.operation[\"type\"] = type\nend\n\n---Gets rebase operation exec.\n---@return string\nfunction RebaseOperation:exec()\n  local str_ptr = self.operation[\"exec\"]\n  return str_ptr ~= nil and ffi.string(str_ptr) or \"\"\nend\n\n---Sets rebase operation exec string\n---@param exec string?\nfunction RebaseOperation:set_exec(exec)\n  self.operation[\"exec\"] = exec\nend\n\n---Gets ObjectId of rebase operation.\n---@return GitObjectId\nfunction RebaseOperation:id()\n  return ObjectId.borrow(ffi.cast(libgit2.git_oid_pointer, self.operation[\"id\"]))\nend\n\n---Copies another git_oid to this RebaseOperation oid.\n---@param oid GitObjectId\n---@return GIT_ERROR err 0 on success or error code\nfunction RebaseOperation:set_id(oid)\n  local op_id_ptr = ffi.cast(libgit2.git_oid_pointer, self.operation[\"id\"])\n  return libgit2.C.git_oid_cpy(op_id_ptr, oid.oid)\nend\n\n-- ====================\n-- | Rebase functions |\n-- ====================\n\n---Init new Gitrebase\n---@param git_rebase_ptr ffi.cdata* libgit2 git_rebase*, own cdata\n---@return GitRebase\nfunction Rebase.new(git_rebase_ptr)\n  local rebase = { rebase = libgit2.git_rebase_pointer(git_rebase_ptr) }\n  setmetatable(rebase, Rebase)\n\n  ffi.gc(rebase.rebase, libgit2.C.git_rebase_free)\n  return rebase\nend\n\n---Pretty print current rebase operation\nfunction Rebase:__tostring()\n  local str = \"Rebase \"\n  local onto_id = libgit2.C.git_rebase_onto_id(self.rebase)\n  local org_id = libgit2.C.git_rebase_orig_head_id(self.rebase)\n\n  local org_str = libgit2.C.git_oid_tostr_s(org_id)\n  if org_str ~= nil then\n    str = str .. string.sub(ffi.string(org_str), 1, 8)\n  end\n\n  local onto_str = libgit2.C.git_oid_tostr_s(onto_id)\n  if onto_str ~= nil then\n    str = str .. \" onto \" .. string.sub(ffi.string(onto_str), 1, 8)\n  end\n\n  return str\nend\n\n---Performs the next rebase operation and returns the information about it.\n---@return GitRebaseOperation?\n---@return GIT_ERROR\nfunction Rebase:next()\n  local operation = libgit2.git_rebase_operation_double_pointer()\n  local err = libgit2.C.git_rebase_next(operation, self.rebase)\n  if err ~= 0 then\n    return nil, err\n  end\n\n  return RebaseOperation.borrow(operation[0]), 0\nend\n\n---Gets the count of rebase operations that are to be applied.\n---@return integer\nfunction Rebase:noperations()\n  return tonumber(libgit2.C.git_rebase_operation_entrycount(self.rebase)) or -1\nend\n\n---Gets the index of the rebase operation that is currently being applied.\n---@return integer index If the first operation has not yet been applied, returns GIT_REBASE_NO_OPERATION\nfunction Rebase:operation_current()\n  return libgit2.C.git_rebase_operation_current(self.rebase)\nend\n\n---Gets the rebase operation specified by the given index.\n---@return GitRebaseOperation? The rebase operation or NULL if `idx` was out of bounds.\nfunction Rebase:operation_byindex(idx)\n  local operation = libgit2.C.git_rebase_operation_byindex(self.rebase, idx)\n  if operation == nil then\n    return nil\n  end\n\n  return RebaseOperation.borrow(operation)\nend\n\n---Gets the onto ref name for merge rebases.\n---@return string\nfunction Rebase:onto_name()\n  local name_ptr = libgit2.C.git_rebase_onto_name(self.rebase)\n  if name_ptr == nil then\n    return \"\"\n  end\n  return ffi.string(name_ptr)\nend\n\n---Gets the onto id for merge rebases.\nfunction Rebase:onto_id()\n  local oid_ptr = libgit2.C.git_rebase_onto_id(self.rebase)\n  return ObjectId.borrow(oid_ptr)\nend\n\n---Gets the original HEAD ref name for merge rebases.\n---@return string\nfunction Rebase:orig_head_name()\n  local name_ptr = libgit2.C.git_rebase_orig_head_name(self.rebase)\n  if name_ptr == nil then\n    return \"\"\n  end\n  return ffi.string(name_ptr)\nend\n\n---Gets the original HEAD id for merge rebases.\n---@return GitObjectId\nfunction Rebase:orig_head_id()\n  local oid_ptr = libgit2.C.git_rebase_orig_head_id(self.rebase)\n  return ObjectId.borrow(oid_ptr)\nend\n\n---Aborts a rebase that is currently in progress,\n---resetting the repository and working directory to their state before rebase began.\n---@return GIT_ERROR\nfunction Rebase:abort()\n  return libgit2.C.git_rebase_abort(self.rebase)\nend\n\n---Commits the current patch. You must have resolved any conflicts.\n---@param author GitSignature? The author of the updated commit, or NULL to keep the author from the original commit\n---@param commiter GitSignature The committer of the rebase\n---@param message string? The message for this commit, or NULL to use the message from the original commit.\n---@return GitObjectId?\n---@return GIT_ERROR err Zero on success, GIT_EUNMERGED if there are unmerged changes in the index, GIT_EAPPLIED if the current commit has already been applied to the upstream and there is nothing to commit, -1 on failure.\nfunction Rebase:commit(author, commiter, message)\n  local new_oid = libgit2.git_oid()\n\n  local err =\n    libgit2.C.git_rebase_commit(new_oid, self.rebase, author and author.sign or nil, commiter.sign, \"UTF-8\", message)\n  if err ~= 0 then\n    return nil, err\n  end\n\n  return ObjectId.borrow(new_oid), 0\nend\n\n---Finishes a rebase that is currently in progress once all patches have been applied.\n---@param signature GitSignature\n---@return GIT_ERROR err Zero on success; -1 on error\nfunction Rebase:finish(signature)\n  return libgit2.C.git_rebase_finish(self.rebase, signature.sign)\nend\n\n---Gets the index produced by the last operation,\n---which is the result of git_rebase_next and which will be committed\n---by the next invocation of git_rebase_commit\n---@return GitIndex? The result index of the last operation.\n---@return GIT_ERROR\nfunction Rebase:inmemory_index()\n  local index = libgit2.git_index_double_pointer()\n  local err = libgit2.C.git_rebase_inmemory_index(index, self.rebase)\n  if err ~= 0 then\n    return nil, err\n  end\n\n  return Index.new(index[0]), 0\nend\n\n-- ========================\n-- | Repository functions |\n-- ========================\n\n---@class GitStatusItem\n---@field path string File path\n---@field new_path string? New file path in case of rename\n---@field worktree_status GIT_DELTA Git status in worktree to index\n---@field index_status GIT_DELTA Git status in index to head\n---@field renamed boolean Extra flag to indicate whether item is renamed\n\n---@class GitStatusUpstream\n---@field name string\n---@field oid GitObjectId?\n---@field message string\n---@field author string\n---@field ahead integer\n---@field behind integer\n---@field remote string\n---@field remote_url string\n\n---@class GitStatusHead\n---@field name string\n---@field oid GitObjectId?\n---@field message string\n---@field author string\n---@field is_detached boolean\n---@field namespace GIT_REFERENCE_NAMESPACE\n---@field refname string\n\n---@class GitStatusResult\n---@field head GitStatusHead?\n---@field upstream GitStatusUpstream?\n---@field status GitStatusItem[]\n\n---@class GitBranch\n---@field name string\n---@field shorthand string\n---@field type GIT_BRANCH\n\n---@alias GitDiffStats {changed: integer, insertions: integer, deletions: integer} Diff stats\n\n---@alias GitDiffPatchItem {status: GIT_DELTA, path: string, new_path:string, num_hunks: integer, patch: GitPatch}\n\nlocal DEFAULT_STATUS_FLAGS = bit.bor(\n  libgit2.GIT_STATUS_OPT.INCLUDE_UNTRACKED,\n  libgit2.GIT_STATUS_OPT.RENAMES_HEAD_TO_INDEX,\n  libgit2.GIT_STATUS_OPT.RENAMES_INDEX_TO_WORKDIR,\n  libgit2.GIT_STATUS_OPT.RECURSE_UNTRACKED_DIRS,\n  libgit2.GIT_STATUS_OPT.SORT_CASE_SENSITIVELY\n)\n\n---Inits new Repository object\n---@param git_repository ffi.cdata* libgit2.git_repository_pointer, own cdata\n---@return GitRepository\nfunction Repository.new(git_repository)\n  local repo = { repo = libgit2.git_repository_pointer(git_repository) }\n  setmetatable(repo, Repository)\n\n  local c_path = libgit2.C.git_repository_path(repo.repo)\n  repo.path = ffi.string(c_path)\n\n  ffi.gc(repo.repo, libgit2.C.git_repository_free)\n\n  return repo\nend\n\n-- New Repository object, only borrow cdata\n---@param git_repo ffi.cdata* libgit2.git_repository_pointer, don't own cdata\n---@return GitRepository\nfunction Repository.borrow(git_repo)\n  local repo = { repo = libgit2.git_repository_pointer(git_repo) }\n  setmetatable(repo, Repository)\n\n  local c_path = libgit2.C.git_repository_path(repo.repo)\n  repo.path = ffi.string(c_path)\n\n  return repo\nend\n\nfunction Repository:__tostring()\n  return string.format(\"Git Repository: %s\", self.path)\nend\n\n---Opens Git repository\n---@param path string Path to repository\n---@param search boolean Whether to search parent directories.\n---@return GitRepository?\n---@return GIT_ERROR\nfunction Repository.open(path, search)\n  local git_repo = libgit2.git_repository_double_pointer()\n\n  local open_flag = 0\n  if not search then\n    open_flag = bit.bor(open_flag, libgit2.GIT_REPOSITORY_OPEN.NO_SEARCH)\n  end\n\n  local err = libgit2.C.git_repository_open_ext(git_repo, path, open_flag, nil)\n  if err ~= 0 then\n    return nil, err\n  end\n\n  return Repository.new(git_repo[0]), 0\nend\n\n---Checks a Repository is empty or not\n---@return boolean is_empty Whether this git repo is empty\nfunction Repository:is_empty()\n  local ret = libgit2.C.git_repository_is_empty(self.repo)\n  if ret == 1 then\n    return true\n  elseif ret == 0 then\n    return false\n  else\n    error(\"Repository is corrupted, code\" .. ret)\n  end\nend\n\n-- Checks a Repository is bare or not\n---@return boolean is_bare Whether this git repo is bare repository\nfunction Repository:is_bare()\n  local ret = libgit2.C.git_repository_is_bare(self.repo)\n  return ret == 1\nend\n\n-- Checks a Repository HEAD is detached or not\n---@return boolean is_head_detached Whether this git repo head detached\nfunction Repository:is_head_detached()\n  local ret = libgit2.C.git_repository_head_detached(self.repo)\n  return ret == 1\nend\n\n---Get the path of this repository\nfunction Repository:repo_path()\n  return ffi.string(libgit2.C.git_repository_path(self.repo))\nend\n\n---Get the configuration file for this repository\n---@return GitConfig?\n---@return GIT_ERROR\nfunction Repository:config()\n  local git_config = libgit2.git_config_double_pointer()\n\n  local err = libgit2.C.git_repository_config(git_config, self.repo)\n  if err ~= 0 then\n    return nil, err\n  end\n\n  return Config.new(git_config[0]), 0\nend\n\n-- Creates a git_annotated_commit from the given reference.\n---@param ref GitReference\n---@return GitAnnotatedCommit?\n---@return GIT_ERROR\nfunction Repository:annotated_commit_from_ref(ref)\n  local git_commit = libgit2.git_annotated_commit_double_pointer()\n\n  local err = libgit2.C.git_annotated_commit_from_ref(git_commit, self.repo, ref.ref)\n  if err ~= 0 then\n    return nil, 0\n  end\n\n  return AnnotatedCommit.new(git_commit[0]), 0\nend\n\n-- Creates a git_annotated_commit from a revision string.\n---@param revspec string\n---@return GitAnnotatedCommit?\n---@return GIT_ERROR\nfunction Repository:annotated_commit_from_revspec(revspec)\n  local git_commit = libgit2.git_annotated_commit_double_pointer()\n\n  local err = libgit2.C.git_annotated_commit_from_revspec(git_commit, self.repo, revspec)\n  if err ~= 0 then\n    return nil, 0\n  end\n\n  return AnnotatedCommit.new(git_commit[0]), 0\nend\n\n---@param opts GitBlameOptions\n---@return ffi.cdata*? blame_opts libgit2 git_blame_options[1]\n---@return GIT_ERROR\nlocal function init_blame_options(opts)\n  local blame_opts = libgit2.git_blame_options()\n  local err = libgit2.C.git_blame_options_init(blame_opts, libgit2.GIT_BLAME_OPTIONS_VERSION)\n  if err ~= 0 then\n    return nil, err\n  end\n\n  local flags = 0\n\n  if opts.first_parent then\n    flags = bit.bor(flags, libgit2.GIT_BLAME.FIRST_PARENT)\n  end\n\n  if opts.use_mailmap then\n    flags = bit.bor(flags, libgit2.GIT_BLAME.USE_MAILMAP)\n  end\n\n  if opts.ignore_whitespace then\n    flags = bit.bor(flags, libgit2.GIT_BLAME.IGNORE_WHITESPACE)\n  end\n\n  blame_opts[0].flags = flags\n\n  if opts.min_match_characters then\n    blame_opts[0].min_match_characters = opts.min_match_characters\n  end\n\n  if opts.newest_commit then\n    err = libgit2.C.git_oid_cpy(blame_opts[0].newest_commit, opts.newest_commit.oid)\n    if err ~= 0 then\n      return nil, err\n    end\n  end\n\n  if opts.oldest_commit then\n    err = libgit2.C.git_oid_cpy(blame_opts[0].oldest_commit, opts.oldest_commit.oid)\n    if err ~= 0 then\n      return nil, err\n    end\n  end\n\n  if opts.min_line then\n    blame_opts[0].min_line = opts.min_line\n  end\n\n  if opts.max_line then\n    blame_opts[0].max_line = opts.max_line\n  end\n\n  return blame_opts, 0\nend\n\n-- Gets the blame for a single file.\n---@param path string git path\n---@param opts GitBlameOptions\n---@return GitBlame? GitBlame info\n---@return GIT_ERROR err error code\nfunction Repository:blame_file(path, opts)\n  local blame_opts, err = init_blame_options(opts)\n  if not blame_opts then\n    return nil, err\n  end\n\n  local git_blame = libgit2.git_blame_double_pointer()\n\n  err = libgit2.C.git_blame_file(git_blame, self.repo, path, blame_opts)\n  if err ~= 0 then\n    return nil, err\n  end\n\n  return Blame.new(git_blame[0]), 0\nend\n\n---Retrieves reference pointed at by HEAD.\n---@return GitReference?\n---@return GIT_ERROR\nfunction Repository:head()\n  local c_ref = libgit2.git_reference_double_pointer()\n  local err = libgit2.C.git_repository_head(c_ref, self.repo)\n  if err ~= 0 then\n    return nil, err\n  end\n\n  return Reference.new(c_ref[0]), 0\nend\n\n---@return GitCommit?\n---@return GIT_ERROR\nfunction Repository:head_commit()\n  local c_ref = libgit2.git_reference_double_pointer()\n  local err = libgit2.C.git_repository_head(c_ref, self.repo)\n  if err ~= 0 then\n    return nil, err\n  end\n\n  local git_object = libgit2.git_object_double_pointer()\n  err = libgit2.C.git_reference_peel(git_object, c_ref[0], libgit2.GIT_OBJECT.COMMIT)\n  libgit2.C.git_reference_free(c_ref[0])\n\n  if err ~= 0 then\n    return nil, err\n  end\n\n  return Commit.new(ffi.cast(libgit2.git_commit_pointer, git_object[0])), 0\nend\n\n-- Makes the repository HEAD point to the specified reference.\n---@param refname string\n---@return GIT_ERROR\nfunction Repository:set_head(refname)\n  local err = libgit2.C.git_repository_set_head(self.repo, refname)\n  return err\nend\n\n-- Makes the repository HEAD directly point to the Commit.\n---@param oid GitObjectId\n---@return GIT_ERROR\nfunction Repository:set_head_detached(oid)\n  local err = libgit2.C.git_repository_set_head_detached(self.repo, oid.oid)\n  return err\nend\n\n-- Gets GitCommit signature\n---@param oid GitObjectId\n---@param field string? GPG sign field\nfunction Repository:commit_signature(oid, field)\n  local buf_signature = libgit2.git_buf()\n  local buf_signed_data = libgit2.git_buf()\n\n  local err = libgit2.C.git_commit_extract_signature(buf_signature, buf_signed_data, self.repo, oid.oid, field)\n  if err ~= 0 then\n    return nil, nil, 0\n  end\n\n  local signature = ffi.string(buf_signature[0].ptr, buf_signature[0].size)\n  local signed_data = ffi.string(buf_signed_data[0].ptr, buf_signed_data[0].size)\n\n  libgit2.C.git_buf_dispose(buf_signature[0])\n  libgit2.C.git_buf_dispose(buf_signed_data[0])\n\n  return signature, signed_data, 0\nend\n\n---@return GitTree?\n---@return GIT_ERROR\nfunction Repository:head_tree()\n  local c_ref = libgit2.git_reference_double_pointer()\n  local err = libgit2.C.git_repository_head(c_ref, self.repo)\n  if err ~= 0 then\n    return nil, err\n  end\n\n  local git_object = libgit2.git_object_double_pointer()\n  err = libgit2.C.git_reference_peel(git_object, c_ref[0], libgit2.GIT_OBJECT.TREE)\n  libgit2.C.git_reference_free(c_ref[0])\n\n  if err ~= 0 then\n    return nil, err\n  end\n\n  return Tree.new(ffi.cast(libgit2.git_tree_pointer, git_object[0])), 0\nend\n\n-- Creates a new branch pointing at a target commit\n---@param name string\n---@param target GitCommit\n---@param force boolean\n---@return GitReference? Reference to created branch\n---@return GIT_ERROR\nfunction Repository:create_branch(name, target, force)\n  local git_ref = libgit2.git_reference_double_pointer()\n  local is_force = force and 1 or 0\n\n  local err = libgit2.C.git_branch_create(git_ref, self.repo, name, target.commit, is_force)\n  if err ~= 0 then\n    return nil, err\n  end\n\n  return Reference.new(git_ref[0]), 0\nend\n\n-- Listings branches of a repo.\n---@param locals boolean Includes local branches.\n---@param remotes boolean Include remote branches.\n---@return GitBranch[]?\n---@return GIT_ERROR\nfunction Repository:branches(locals, remotes)\n  if not locals and not remotes then\n    return {}, 0\n  end\n\n  local branch_flags = 0\n  if locals then\n    branch_flags = libgit2.GIT_BRANCH.LOCAL\n  end\n  if remotes then\n    branch_flags = bit.bor(branch_flags, libgit2.GIT_BRANCH.REMOTE)\n  end\n\n  local c_branch_iter = libgit2.git_branch_iterator_double_pointer()\n  local err = libgit2.C.git_branch_iterator_new(c_branch_iter, self.repo, branch_flags)\n  if err ~= 0 then\n    return nil, err\n  end\n\n  ---@type GitBranch[]\n  local branches = {}\n  local c_ref = libgit2.git_reference_double_pointer()\n  local c_branch_type = libgit2.unsigned_int_array(1)\n  while libgit2.C.git_branch_next(c_ref, c_branch_type, c_branch_iter[0]) == 0 do\n    ---@type GitBranch\n    local br = {\n      name = ffi.string(libgit2.C.git_reference_name(c_ref[0])),\n      shorthand = ffi.string(libgit2.C.git_reference_shorthand(c_ref[0])),\n      type = math.floor(tonumber(c_branch_type[0]) or 0),\n    }\n    table.insert(branches, br)\n\n    libgit2.C.git_reference_free(c_ref[0])\n  end\n\n  libgit2.C.git_branch_iterator_free(c_branch_iter[0])\n\n  return branches, 0\nend\n\n-- Listings tags of a repo.\n---@return string[]?\n---@return GIT_ERROR\nfunction Repository:tag_list()\n  local tag_names = libgit2.git_strarray()\n\n  local err = libgit2.C.git_tag_list(tag_names, self.repo)\n  if err ~= 0 then\n    return nil, err\n  end\n\n  local ntags = tonumber(tag_names[0].count) or 0\n  local tags = table_new(ntags, 0)\n\n  for i = 0, ntags - 1 do\n    tags[i + 1] = ffi.string(tag_names[0].strings[i])\n  end\n\n  libgit2.C.git_strarray_dispose(tag_names)\n\n  return tags, 0\nend\n\n-- Calculates ahead and behind information.\n---@param local_commit GitObjectId The commit which is considered the local or current state.\n---@param upstream_commit GitObjectId The commit which is considered upstream.\n---@return number? ahead Unique ahead commits.\n---@return number? behind Unique behind commits.\n---@return GIT_ERROR err Error code.\nfunction Repository:ahead_behind(local_commit, upstream_commit)\n  local c_ahead = libgit2.size_t_array(2)\n\n  local err = libgit2.C.git_graph_ahead_behind(c_ahead, c_ahead + 1, self.repo, local_commit.oid, upstream_commit.oid)\n\n  if err ~= 0 then\n    return nil, nil, err\n  end\n\n  return tonumber(c_ahead[0]), tonumber(c_ahead[1]), 0\nend\n\n---Lookup a reference by name in a repository\n---@param refname string Long name for the reference (e.g. HEAD, refs/heads/master, refs/tags/v0.1.0).\n---@return GitReference?\n---@return GIT_ERROR\nfunction Repository:reference_lookup(refname)\n  local ref = libgit2.git_reference_double_pointer()\n\n  local err = libgit2.C.git_reference_lookup(ref, self.repo, refname)\n  if err ~= 0 then\n    return nil, 0\n  end\n\n  return Reference.new(ref[0]), 0\nend\n\n---Lookup a reference by name and resolve immediately to OID.\n---@param refname string Long name for the reference (e.g. HEAD, refs/heads/master, refs/tags/v0.1.0).\n---@return GitObjectId?\n---@return GIT_ERROR\nfunction Repository:reference_name_to_id(refname)\n  local oid = libgit2.git_oid()\n  local err = libgit2.C.git_reference_name_to_id(oid, self.repo, refname)\n  if err ~= 0 then\n    return nil, err\n  end\n\n  return ObjectId.borrow(oid), 0\nend\n\n---Creates a new direct reference.\n---@param name string name of the reference.\n---@param oid GitObjectId object id pointed to by the reference.\n---@param is_force boolean is force.\n---@param log_message string line long message to be appended to the reflog.\n---@return GitReference? ref direct reference\n---@return GIT_ERROR err error code\nfunction Repository:create_reference(name, oid, is_force, log_message)\n  local git_ref = libgit2.git_reference_double_pointer()\n  local force = is_force and 1 or 0\n\n  local err = libgit2.C.git_reference_create(git_ref, self.repo, name, oid.oid, force, log_message)\n  if err ~= 0 then\n    return nil, err\n  end\n\n  return Reference.new(git_ref[0]), 0\nend\n\n---Gets commit from a reference.\n---@param oid GitObjectId\n---@return GitCommit?\n---@return GIT_ERROR\nfunction Repository:commit_lookup(oid)\n  local c_commit = libgit2.git_commit_double_pointer()\n\n  local err = libgit2.C.git_commit_lookup(c_commit, self.repo, oid.oid)\n  if err ~= 0 then\n    return nil, err\n  end\n\n  return Commit.new(c_commit[0]), 0\nend\n\n-- Gets repository index.\n---@return GitIndex?\n---@return GIT_ERROR\nfunction Repository:index()\n  local c_index = libgit2.git_index_double_pointer()\n\n  local err = libgit2.C.git_repository_index(c_index, self.repo)\n  if err ~= 0 then\n    return nil, err\n  end\n\n  return Index.new(c_index[0]), 0\nend\n\n-- Updates some entries in the index from the target commit tree.\n---@param paths string[]\n---@return GIT_ERROR\nfunction Repository:reset_default(paths)\n  local head, ret = self:head()\n  if head == nil then\n    return ret\n  else\n    local commit, err = head:peel(libgit2.GIT_OBJECT.COMMIT)\n    if commit == nil then\n      return err\n    elseif #paths > 0 then\n      local c_paths = libgit2.const_char_pointer_array(#paths, paths)\n      local strarray = libgit2.git_strarray_readonly()\n\n      -- for i, p in ipairs(paths) do\n      --   c_paths[i-1] = p\n      -- end\n\n      strarray[0].strings = c_paths\n      strarray[0].count = #paths\n\n      return libgit2.C.git_reset_default(self.repo, commit.obj, strarray)\n    end\n    return 0\n  end\nend\n\n---@param strategy GIT_CHECKOUT? The default strategy is SAFE | RECREATE_MISSING.\n---@param paths string[]? file paths to be checkout\n---@return ffi.cdata* opts GIT_CHECKOUT_OPTION\n---@return ffi.cdata* c_paths GIT_STR_ARRAY\nlocal function prepare_checkout_opts(strategy, paths)\n  local c_paths\n  local opts = libgit2.git_checkout_options(libgit2.GIT_CHECKOUT_OPTIONS_INIT)\n\n  if strategy ~= nil then\n    opts[0].checkout_strategy = strategy\n  else\n    opts[0].checkout_strategy = bit.bor(libgit2.GIT_CHECKOUT.SAFE, libgit2.GIT_CHECKOUT.RECREATE_MISSING)\n  end\n\n  if paths and #paths > 0 then\n    c_paths = libgit2.const_char_pointer_array(#paths, paths)\n    opts[0].paths.strings = c_paths\n    opts[0].paths.count = #paths\n  end\n\n  return opts, c_paths\nend\n\n-- Updates files in the working tree to match the content of the index.\n---@param index GitIndex? Repository index, can be null\n---@param strategy GIT_CHECKOUT?\n---@param paths string[]? file paths to be checkout\n---@return GIT_ERROR\nfunction Repository:checkout_index(index, strategy, paths)\n  local opts, c_paths = prepare_checkout_opts(strategy, paths)\n\n  local err = libgit2.C.git_checkout_index(self.repo, index and index.index or nil, opts)\n  return err\nend\n\n-- Updates files in the index and the working tree to match\n-- the content of the commit pointed at by HEAD.\n---@param strategy GIT_CHECKOUT?\n---@param paths string[]? files paths to be checkout\n---@return GIT_ERROR\nfunction Repository:checkout_head(strategy, paths)\n  local opts, c_paths = prepare_checkout_opts(strategy, paths)\n\n  local err = libgit2.C.git_checkout_head(self.repo, opts)\n  return err\nend\n\n-- Updates files in the index and working tree to match\n-- the content of the tree pointed at by the treeish.\n---@param tree GitObject Tree like object\n---@param strategy GIT_CHECKOUT?\n---@param paths string[]? files paths to be checkout\n---@return GIT_ERROR\nfunction Repository:checkout_tree(tree, strategy, paths)\n  local opts, c_paths = prepare_checkout_opts(strategy, paths)\n\n  local err = libgit2.C.git_checkout_tree(self.repo, tree.obj, opts)\n  return err\nend\n\n-- Checkout the given reference using the given strategy, and update the HEAD.\n-- The default strategy is SAFE | RECREATE_MISSING.\n-- If no reference is given, checkout from the index.\n---@param refname string\n---@param strategy GIT_CHECKOUT?\n---@param paths string[]? files paths to be checkout\n---@return GIT_ERROR\nfunction Repository:checkout(refname, strategy, paths)\n  -- Case 1: Checkout index\n  if not refname then\n    return self:checkout_index(nil, strategy, paths)\n  end\n\n  -- Case 2: Checkout head\n  if refname == \"HEAD\" then\n    return self:checkout_head(strategy, paths)\n  end\n\n  -- Case 3: Reference name\n  local ref, err = self:reference_lookup(refname)\n  if not ref then\n    return err\n  end\n\n  local tree\n  tree, err = ref:peel_tree()\n  if not tree then\n    return err\n  end\n\n  err = self:checkout_tree(tree:as_object(), strategy, paths)\n  if err ~= 0 then\n    return err\n  end\n\n  if not paths or #paths == 0 then\n    return self:set_head(refname)\n  end\n\n  return 0\nend\n\n-- Finds the remote name of a remote-tracking branch.\n---@param ref string Ref name\n---@return string? remote Git remote name\n---@return GIT_ERROR\nfunction Repository:branch_remote_name(ref)\n  local c_buf = libgit2.git_buf()\n\n  local err = libgit2.C.git_branch_remote_name(c_buf, self.repo, ref)\n  if err ~= 0 then\n    libgit2.C.git_buf_dispose(c_buf)\n    return nil, err\n  end\n\n  local remote = ffi.string(c_buf[0].ptr, c_buf[0].size)\n  libgit2.C.git_buf_dispose(c_buf)\n\n  return remote, 0\nend\n\n---Retrieves the remote of upstream of a local branch.\n---@param ref string Ref name\n---@return string? remote Git remote name\n---@return GIT_ERROR\nfunction Repository:branch_upstream_remote_name(ref)\n  local c_buf = libgit2.git_buf()\n\n  local err = libgit2.C.git_branch_upstream_remote(c_buf, self.repo, ref)\n  if err ~= 0 then\n    libgit2.C.git_buf_dispose(c_buf)\n    return nil, err\n  end\n\n  local remote = ffi.string(c_buf[0].ptr, c_buf[0].size)\n  libgit2.C.git_buf_dispose(c_buf)\n\n  return remote, 0\nend\n\n---@param refname string refname\n---@return string?\n---@return GIT_ERROR\nfunction Repository:branch_upstream_name(refname)\n  local git_buf = libgit2.git_buf()\n  local err = libgit2.C.git_branch_upstream_name(git_buf, self.repo, refname)\n  if err ~= 0 then\n    libgit2.C.git_buf_dispose(git_buf)\n    return nil, err\n  end\n\n  local name = ffi.string(git_buf[0].ptr, git_buf[0].size)\n  libgit2.C.git_buf_dispose(git_buf)\n\n  return name, 0\nend\n\n-- Gets the information for a particular remote.\n---@param remote string\n---@return GitRemote?\n---@return GIT_ERROR\nfunction Repository:remote_lookup(remote)\n  local c_remote = libgit2.git_remote_double_pointer()\n\n  local err = libgit2.C.git_remote_lookup(c_remote, self.repo, remote)\n  if err ~= 0 then\n    return nil, err\n  end\n\n  return Remote.new(c_remote[0]), 0\nend\n\n---Gets a list of the configured remotes for a repo\n---@return string[]?\n---@return GIT_ERROR\nfunction Repository:remote_list()\n  local strarr = libgit2.git_strarray()\n\n  local err = libgit2.C.git_remote_list(strarr, self.repo)\n  if err ~= 0 then\n    return nil, err\n  end\n\n  local num_remotes = tonumber(strarr[0].count) or 0\n  local remotes = table_new(num_remotes, 0)\n\n  for i = 0, num_remotes - 1 do\n    remotes[i + 1] = ffi.string(strarr[0].strings[i])\n  end\n  libgit2.C.git_strarray_dispose(strarr)\n\n  return remotes, 0\nend\n\n---Reads status of a given file path.\n---this can't detect a rename.\n---@param path string Git file path.\n---@return GIT_DELTA worktree_status Git Status in worktree.\n---@return GIT_DELTA index_status Git Status in index.\n---@return GIT_ERROR return_code Git return code.\nfunction Repository:status_file(path)\n  local worktree_status, index_status = libgit2.GIT_DELTA.UNMODIFIED, libgit2.GIT_DELTA.UNMODIFIED\n  local c_status = libgit2.unsigned_int_array(1)\n\n  local err = libgit2.C.git_status_file(c_status, self.repo, path)\n  if err ~= 0 then\n    return worktree_status, index_status, err\n  end\n\n  local status = tonumber(c_status[0])\n  if status ~= nil then\n    if bit.band(status, libgit2.GIT_STATUS.WT_NEW) ~= 0 then\n      worktree_status = libgit2.GIT_DELTA.UNTRACKED\n      index_status = libgit2.GIT_DELTA.UNTRACKED\n    elseif bit.band(status, libgit2.GIT_STATUS.WT_MODIFIED) ~= 0 then\n      worktree_status = libgit2.GIT_DELTA.MODIFIED\n    elseif bit.band(status, libgit2.GIT_STATUS.WT_DELETED) ~= 0 then\n      worktree_status = libgit2.GIT_DELTA.DELETED\n    elseif bit.band(status, libgit2.GIT_STATUS.WT_TYPECHANGE) ~= 0 then\n      worktree_status = libgit2.GIT_DELTA.TYPECHANGE\n    elseif bit.band(status, libgit2.GIT_STATUS.WT_UNREADABLE) ~= 0 then\n      worktree_status = libgit2.GIT_DELTA.UNREADABLE\n    elseif bit.band(status, libgit2.GIT_STATUS.IGNORED) ~= 0 then\n      worktree_status = libgit2.GIT_DELTA.IGNORED\n    elseif bit.band(status, libgit2.GIT_STATUS.CONFLICTED) ~= 0 then\n      worktree_status = libgit2.GIT_DELTA.CONFLICTED\n    end\n\n    if bit.band(status, libgit2.GIT_STATUS.INDEX_NEW) ~= 0 then\n      index_status = libgit2.GIT_DELTA.ADDED\n    elseif bit.band(status, libgit2.GIT_STATUS.INDEX_MODIFIED) ~= 0 then\n      index_status = libgit2.GIT_DELTA.MODIFIED\n    elseif bit.band(status, libgit2.GIT_STATUS.INDEX_DELETED) ~= 0 then\n      index_status = libgit2.GIT_DELTA.DELETED\n    elseif bit.band(status, libgit2.GIT_STATUS.INDEX_TYPECHANGE) ~= 0 then\n      index_status = libgit2.GIT_DELTA.TYPECHANGE\n    end\n  end\n\n  return worktree_status, index_status, 0\nend\n\n-- Reads head and upstream status.\n---@return GitStatusHead?\n---@return GitStatusUpstream?\n---@return GIT_ERROR\nfunction Repository:status_head_upstream()\n  local err\n\n  -- Get Head information\n  local repo_head, repo_head_oid, head_status\n  repo_head, err = self:head()\n  if not repo_head then\n    return nil, nil, err\n  end\n\n  local repo_head_commit, _ = repo_head:peel_commit()\n  repo_head_oid = repo_head_commit and repo_head_commit:id() or nil\n\n  head_status = {\n    name = repo_head:shorthand(),\n    oid = repo_head_oid,\n    author = repo_head_commit and repo_head_commit:author() or \"\",\n    message = repo_head_commit and repo_head_commit:message() or \"\",\n    is_detached = self:is_head_detached(),\n    namespace = repo_head.namespace,\n    refname = repo_head.name,\n  }\n\n  -- Get upstream information\n  local repo_upstream, upstream_status\n  repo_upstream, err = repo_head and repo_head:branch_upstream() or nil, 0\n  if repo_upstream then\n    ---@type number\n    local ahead, behind = 0, 0\n    local commit_local = repo_head_oid\n    -- local commit_upstream, _ = repo_upstream:target()\n    local commit_upstream, _ = repo_upstream:peel_commit()\n    local commit_upstream_oid = commit_upstream and commit_upstream:id() or nil\n\n    if commit_upstream_oid and commit_local then\n      local nilable_ahead, nilable_behind, _ = self:ahead_behind(commit_local, commit_upstream_oid)\n      if nilable_ahead ~= nil and nilable_behind ~= nil then\n        ahead, behind = nilable_ahead, nilable_behind\n      end\n    end\n\n    local remote\n    local remote_name = repo_upstream:remote_name()\n    if remote_name then\n      remote, _ = self:remote_lookup(remote_name)\n    end\n\n    upstream_status = {\n      name = repo_upstream:shorthand(),\n      oid = commit_upstream_oid,\n      message = commit_upstream and commit_upstream:message() or \"\",\n      author = commit_upstream and commit_upstream:author() or \"\",\n      ahead = ahead,\n      behind = behind,\n      remote = remote and remote.name or \"\",\n      remote_url = remote and remote.url or \"\",\n    }\n  end\n\n  return head_status, upstream_status, 0\nend\n\n-- Parse information from git_status_list and convert to GitStatusItem[]\n---@param git_status_list ffi.cdata* git_status_list pointer\n---@return GitStatusItem[]\nlocal function git_status_list_to_items(git_status_list)\n  local n_entry = tonumber(libgit2.C.git_status_list_entrycount(git_status_list)) or 0\n  ---@type GitStatusItem[]\n  local status_list = table_new(n_entry, 0)\n\n  -- Iterate through git status list\n  for i = 0, n_entry - 1 do\n    local entry = libgit2.C.git_status_byindex(git_status_list, i)\n    if entry == nil or entry.status == libgit2.GIT_STATUS.CURRENT then\n      goto git_status_list_continue\n    end\n\n    ---@type GitStatusItem\n    local status_item = {\n      path = \"\",\n      worktree_status = libgit2.GIT_DELTA.UNMODIFIED,\n      index_status = libgit2.GIT_DELTA.UNMODIFIED,\n      renamed = false,\n    }\n    ---@type string\n    local old_path, new_path\n\n    if entry.index_to_workdir ~= nil then\n      old_path = ffi.string(entry.index_to_workdir.old_file.path)\n      new_path = ffi.string(entry.index_to_workdir.new_file.path)\n\n      status_item.path = old_path\n      status_item.worktree_status = entry.index_to_workdir.status\n\n      if bit.band(entry.status, libgit2.GIT_STATUS.WT_NEW) ~= 0 then\n        status_item.worktree_status = libgit2.GIT_DELTA.UNTRACKED\n        status_item.index_status = libgit2.GIT_DELTA.UNTRACKED\n      end\n\n      if bit.band(entry.status, libgit2.GIT_STATUS.WT_RENAMED) ~= 0 then\n        status_item.renamed = true\n        status_item.new_path = new_path\n      end\n    end\n\n    if entry.head_to_index ~= nil then\n      old_path = ffi.string(entry.head_to_index.old_file.path)\n      new_path = ffi.string(entry.head_to_index.new_file.path)\n\n      status_item.path = old_path\n      status_item.index_status = entry.head_to_index.status\n\n      if bit.band(entry.status, libgit2.GIT_STATUS.INDEX_RENAMED) ~= 0 then\n        status_item.renamed = true\n        status_item.new_path = new_path\n      end\n    end\n\n    status_list[#status_list + 1] = status_item\n    ::git_status_list_continue::\n  end\n\n  return status_list\nend\n\n-- Reads the status of the repository and returns a dictionary.\n-- with file paths as keys and status flags as values.\n---@return GitStatusItem[]? status_result git status result.\n---@return integer return_code Return code.\nfunction Repository:status()\n  ---@type GIT_ERROR\n  local err\n\n  local opts = libgit2.git_status_options(libgit2.GIT_STATUS_OPTIONS_INIT)\n  -- libgit2.C.git_status_options_init(opts, libgit2.GIT_STATUS_OPTIONS_VERSION)\n  opts[0].show = libgit2.GIT_STATUS_SHOW.INDEX_AND_WORKDIR\n  opts[0].flags = DEFAULT_STATUS_FLAGS\n\n  local status = libgit2.git_status_list_double_pointer()\n  err = libgit2.C.git_status_list_new(status, self.repo, opts)\n  if err ~= 0 then\n    return nil, err\n  end\n\n  local status_list = git_status_list_to_items(status[0])\n\n  -- free C resources\n  libgit2.C.git_status_list_free(status[0])\n\n  return status_list, 0\nend\n\n---Get default remote\n---@return GitRemote?\n---@return GIT_ERROR\nfunction Repository:remote_default()\n  local remote, remotes, err\n\n  remote, err = self:remote_lookup \"origin\"\n  if err ~= 0 then\n    remotes, err = self:remote_list()\n    if err ~= 0 then\n      return nil, 0\n    end\n\n    if remotes and #remotes > 0 then\n      -- get other remote as default if origin is not found\n      remote, err = self:remote_lookup(remotes[1])\n    else\n      return nil, 0\n    end\n  end\n  return remote, err\nend\n\n---Retrieve pushremote for a branch, if not found\n---returns remote from config.\n---@param name string branch name\n---@return string?\nfunction Repository:branch_push_remote(name)\n  if name == \"\" then\n    return nil\n  end\n\n  local config, _ = self:config()\n  if not config then\n    return nil\n  end\n\n  local config_prefix = \"branch.\" .. name\n  return config:get_string(config_prefix .. \".pushremote\") or config:get_string(config_prefix .. \".remote\")\nend\n\n---Default signature user and now timestamp.\n---@return GitSignature?\n---@return GIT_ERROR\nfunction Repository:signature_default()\n  local git_signature = libgit2.git_signature_double_pointer()\n\n  local err = libgit2.C.git_signature_default(git_signature, self.repo)\n  if err ~= 0 then\n    return nil, err\n  end\n\n  return Signature.new(git_signature[0]), 0\nend\n\n-- Creates tree and parents object used in\n-- create_commit and create_commmit_object funcionts.\n---@param index GitIndex\n---@return ffi.cdata*?\n---@return ffi.cdata*?\n---@return GIT_ERROR\nfunction Repository:_create_commit_head_tree_parents(index)\n  local parents = nil\n\n  -- get head as parent commit\n  local head, err = self:head()\n  if err ~= 0 and err ~= libgit2.GIT_ERROR.GIT_ENOTFOUND and err ~= libgit2.GIT_ERROR.GIT_EUNBORNBRANCH then\n    return nil, parents, err\n  end\n\n  local parent = nil\n  if head then\n    parent, err = head:peel_commit()\n    if err ~= 0 then\n      return nil, parents, err\n    end\n\n    if parent then\n      parents = libgit2.git_commit_double_pointer()\n      parents[0] = parent.commit\n    end\n  end\n\n  local tree_id\n  tree_id, err = index:write_tree()\n  if not tree_id then\n    return nil, nil, err\n  end\n\n  local tree = libgit2.git_tree_double_pointer()\n  err = libgit2.C.git_tree_lookup(tree, self.repo, tree_id.oid)\n  if err ~= 0 then\n    return nil, nil, err\n  end\n\n  return tree, parents, 0\nend\n\n-- Creates new commit in the repository.\n---@param index GitIndex\n---@param signature GitSignature\n---@param message string\n---@return GitObjectId?\n---@return GIT_ERROR\nfunction Repository:create_commit(index, signature, message)\n  local tree, parents, err = self:_create_commit_head_tree_parents(index)\n  if not tree or err ~= 0 then\n    return nil, err\n  end\n\n  local git_oid = libgit2.git_oid()\n  err = libgit2.C.git_commit_create(\n    git_oid,\n    self.repo,\n    \"HEAD\",\n    signature.sign,\n    signature.sign,\n    nil,\n    message,\n    tree[0],\n    parents and 1 or 0,\n    parents and ffi.cast(libgit2.git_commit_const_double_pointer, parents) or nil\n  )\n  libgit2.C.git_tree_free(tree[0])\n  if err ~= 0 then\n    return nil, err\n  end\n\n  return ObjectId.borrow(git_oid), 0\nend\n\n-- Creates new commit object as string.\n---@param index GitIndex\n---@param signature GitSignature\n---@param message string\n---@return string?\n---@return GIT_ERROR\nfunction Repository:create_commit_content(index, signature, message)\n  local tree, parents, err = self:_create_commit_head_tree_parents(index)\n  if not tree or err ~= 0 then\n    return nil, err\n  end\n\n  local buf = libgit2.git_buf()\n  err = libgit2.C.git_commit_create_buffer(\n    buf,\n    self.repo,\n    signature.sign,\n    signature.sign,\n    nil,\n    message,\n    tree[0],\n    parents and 1 or 0,\n    parents and ffi.cast(libgit2.git_commit_const_double_pointer, parents) or nil\n  )\n  libgit2.C.git_tree_free(tree[0])\n\n  if err ~= 0 then\n    libgit2.C.git_buf_dispose(buf)\n    return nil, err\n  end\n\n  local commit_str = ffi.string(buf[0].ptr, buf[0].size)\n  libgit2.C.git_buf_dispose(buf)\n  return commit_str, 0\nend\n\n-- Creates a commit object from the given content and signature\n---@param commit_content string Gitcommit object as string\n---@param signature string Gitcommit content signature\n---@param signature_field string? signature field\n---@return GitObjectId?\n---@return GIT_ERROR\nfunction Repository:create_commit_with_signature(commit_content, signature, signature_field)\n  local git_oid = libgit2.git_oid()\n  local err = libgit2.C.git_commit_create_with_signature(git_oid, self.repo, commit_content, signature, signature_field)\n  if err ~= 0 then\n    return nil, 0\n  end\n\n  return ObjectId.borrow(git_oid), 0\nend\n\n-- Lookups a blob object from a repository.\n---@param id GitObjectId\n---@return GitBlob?\n---@return GIT_ERROR\nfunction Repository:blob_lookup(id)\n  local blob = libgit2.git_blob_double_pointer()\n  local err = libgit2.C.git_blob_lookup(blob, self.repo, id.oid)\n  if err ~= 0 then\n    return nil, err\n  end\n  return Blob.new(blob[0]), 0\nend\n\n-- Rewords HEAD commit.\n---@param signature GitSignature\n---@param message string\n---@return GitObjectId?\n---@return GIT_ERROR\nfunction Repository:amend_reword(signature, message)\n  return self:amend(nil, signature, message)\nend\n\n---Extends new index to HEAD commit.\n---@param index GitIndex\n---@return GitObjectId?\n---@return GIT_ERROR\nfunction Repository:amend_extend(index)\n  return self:amend(index, nil, nil)\nend\n\n---@param index GitIndex?\n---@param no_tree boolean don't find tree\n---@return GitCommit?\n---@return ffi.cdata*? tree tree used in ammend\n---@return GIT_ERROR\nfunction Repository:_amend_commit_head_tree_parents(index, no_tree)\n  -- get head as parent commit\n  local head, head_commit, err\n  head, err = self:head()\n  if not head then\n    return nil, nil, err\n  end\n  head_commit, err = head:peel_commit()\n  if not head_commit then\n    return nil, nil, err\n  end\n\n  if no_tree then\n    return head_commit, nil, 0\n  end\n\n  local tree = nil\n  if index then\n    local tree_id\n    tree_id, err = index:write_tree()\n    if not tree_id then\n      return nil, nil, err\n    end\n\n    tree = libgit2.git_tree_double_pointer()\n    err = libgit2.C.git_tree_lookup(tree, self.repo, tree_id.oid)\n    if err ~= 0 then\n      return nil, nil, err\n    end\n  else\n    tree = libgit2.git_tree_double_pointer()\n    err = libgit2.C.git_commit_tree(tree, head_commit.commit)\n    if err ~= 0 then\n      return nil, nil, err\n    end\n  end\n\n  return head_commit, tree, 0\nend\n\n---Amends an existing commit by replacing only non-NULL values.\n---@param index GitIndex?\n---@param signature GitSignature?\n---@param message string?\n---@return GitObjectId?\n---@return GIT_ERROR\nfunction Repository:amend(index, signature, message)\n  local no_tree = not (index or signature or message)\n\n  local head_commit, tree, err = self:_amend_commit_head_tree_parents(index, no_tree)\n  if not head_commit then\n    return nil, err\n  end\n\n  if no_tree then\n    return head_commit:id(), 0\n  end\n\n  local sig = signature and signature.sign or nil\n\n  local git_oid = libgit2.git_oid()\n  err = libgit2.C.git_commit_amend(\n    git_oid,\n    head_commit.commit,\n    \"HEAD\",\n    sig,\n    sig,\n    nil,\n    message,\n    tree ~= nil and tree[0] or nil\n  )\n\n  if tree ~= nil then\n    libgit2.C.git_tree_free(tree[0])\n  end\n\n  if err ~= 0 then\n    return nil, err\n  end\n\n  return ObjectId.borrow(git_oid), 0\nend\n\n-- Creates amend commit as string\n---@param index GitIndex?\n---@param author GitSignature?\n---@param committer GitSignature?\n---@param message string?\n---@return string? git_commit_content\n---@return string? git_commit_message\n---@return GIT_ERROR err\nfunction Repository:amend_commit_content(index, author, committer, message)\n  local head_commit, tree, err = self:_amend_commit_head_tree_parents(index, false)\n  if not head_commit then\n    return nil, nil, err\n  end\n\n  author = author or head_commit:author_signature()\n  committer = committer or head_commit:committer_signature()\n  message = message or head_commit:message()\n\n  local parents\n  local nparents = head_commit:nparents()\n  if nparents > 0 then\n    parents = libgit2.git_commit_pointer_array(nparents)\n    for i = 0, nparents - 1 do\n      parents[i] = nil\n      err = libgit2.C.git_commit_parent(parents + i, head_commit.commit, i)\n    end\n  end\n\n  local buf = libgit2.git_buf()\n  err = libgit2.C.git_commit_create_buffer(\n    buf,\n    self.repo,\n    author.sign,\n    committer.sign,\n    nil,\n    message,\n    tree ~= nil and tree[0] or nil,\n    nparents,\n    ffi.cast(libgit2.git_commit_const_double_pointer, parents)\n  )\n\n  if tree ~= nil then\n    libgit2.C.git_tree_free(tree[0])\n  end\n\n  if parents ~= nil then\n    for i = 0, nparents - 1 do\n      if parents[i] ~= nil then\n        libgit2.C.git_commit_free(parents[i])\n      end\n    end\n  end\n\n  if err ~= 0 then\n    libgit2.C.git_buf_dispose(buf)\n    return nil, nil, err\n  end\n\n  local commit_str = ffi.string(buf[0].ptr, buf[0].size)\n  libgit2.C.git_buf_dispose(buf)\n  return commit_str, message, 0\nend\n\n---Returns a GitRevisionWalker, cached it for the repo if possible.\n---@return GitRevisionWalker?\n---@return GIT_ERROR\nfunction Repository:walker()\n  if self._walker then\n    return self._walker, 0\n  end\n\n  local walker = libgit2.git_revwalk_double_pointer()\n  local err = libgit2.C.git_revwalk_new(walker, self.repo)\n  if err ~= 0 then\n    return nil, err\n  end\n\n  self._walker = RevisionWalker.new(self.repo, walker[0])\n  return self._walker, 0\nend\n\n---Frees a cached GitRevisionWalker\nfunction Repository:free_walker()\n  if self._walker then\n    self._walker = nil\n  end\nend\n\n---Helper function to keep consistency between\n---git_status_options and git_diff_options\n---@param status_flag GIT_STATUS_OPT\n---@return GIT_DIFF diff_flag\n---@return GIT_DIFF_FIND find_flag\nlocal function git_status_flags_to_diff_flags(status_flag)\n  local diff_flag = libgit2.GIT_DIFF.INCLUDE_TYPECHANGE\n  local find_flag = libgit2.GIT_DIFF_FIND.FIND_FOR_UNTRACKED\n\n  if bit.band(status_flag, libgit2.GIT_STATUS_OPT.INCLUDE_UNTRACKED) ~= 0 then\n    diff_flag = bit.bor(diff_flag, libgit2.GIT_DIFF.INCLUDE_UNTRACKED)\n  end\n  if bit.band(status_flag, libgit2.GIT_STATUS_OPT.INCLUDE_IGNORED) ~= 0 then\n    diff_flag = bit.bor(diff_flag, libgit2.GIT_DIFF.INCLUDE_IGNORED)\n  end\n  if bit.band(status_flag, libgit2.GIT_STATUS_OPT.INCLUDE_UNMODIFIED) ~= 0 then\n    diff_flag = bit.bor(diff_flag, libgit2.GIT_DIFF.INCLUDE_UNMODIFIED)\n  end\n  if bit.band(status_flag, libgit2.GIT_STATUS_OPT.RECURSE_UNTRACKED_DIRS) ~= 0 then\n    diff_flag = bit.bor(diff_flag, libgit2.GIT_DIFF.RECURSE_UNTRACKED_DIRS, libgit2.GIT_DIFF.SHOW_UNTRACKED_CONTENT)\n  end\n  if bit.band(status_flag, libgit2.GIT_STATUS_OPT.DISABLE_PATHSPEC_MATCH) ~= 0 then\n    diff_flag = bit.bor(diff_flag, libgit2.GIT_DIFF.DISABLE_PATHSPEC_MATCH)\n  end\n  if bit.band(status_flag, libgit2.GIT_STATUS_OPT.RECURSE_IGNORED_DIRS) ~= 0 then\n    diff_flag = bit.bor(diff_flag, libgit2.GIT_DIFF.RECURSE_IGNORED_DIRS)\n  end\n  if bit.band(status_flag, libgit2.GIT_STATUS_OPT.EXCLUDE_SUBMODULES) ~= 0 then\n    diff_flag = bit.bor(diff_flag, libgit2.GIT_DIFF.IGNORE_SUBMODULES)\n  end\n  if bit.band(status_flag, libgit2.GIT_STATUS_OPT.UPDATE_INDEX) ~= 0 then\n    diff_flag = bit.bor(diff_flag, libgit2.GIT_DIFF.UPDATE_INDEX)\n  end\n  if bit.band(status_flag, libgit2.GIT_STATUS_OPT.INCLUDE_UNREADABLE) ~= 0 then\n    diff_flag = bit.bor(diff_flag, libgit2.GIT_DIFF.INCLUDE_UNREADABLE)\n  end\n  if bit.band(status_flag, libgit2.GIT_STATUS_OPT.INCLUDE_UNREADABLE_AS_UNTRACKED) ~= 0 then\n    diff_flag = bit.bor(diff_flag, libgit2.GIT_DIFF.INCLUDE_UNREADABLE_AS_UNTRACKED)\n  end\n\n  if bit.band(status_flag, libgit2.GIT_STATUS_OPT.RENAMES_FROM_REWRITES) ~= 0 then\n    find_flag = bit.bor(\n      find_flag,\n      libgit2.GIT_DIFF_FIND.FIND_AND_BREAK_REWRITES,\n      libgit2.GIT_DIFF_FIND.FIND_RENAMES_FROM_REWRITES,\n      libgit2.GIT_DIFF_FIND.BREAK_REWRITES_FOR_RENAMES_ONLY\n    )\n  end\n\n  return diff_flag, find_flag\nend\n\n-- Gets diff from index to workdir\n---@param index GitIndex? Repository index, can be null\n---@param paths string[]? Git paths, can be null\n---@param reverse? boolean whether to reverse the diff\n---@param context_lines integer? number of context lines\n---@return GitDiff?\n---@return GIT_ERROR\nfunction Repository:diff_index_to_workdir(index, paths, reverse, context_lines)\n  return self:diff_helper(true, true, index, paths, reverse, context_lines)\nend\n\n-- Gets diff from head to index\n---@param index GitIndex? Repository index, can be null\n---@param paths string[]? Git paths, can be null\n---@param reverse? boolean whether to reverse the diff\n---@param context_lines integer? number of context lines\n---@return GitDiff?\n---@return GIT_ERROR\nfunction Repository:diff_head_to_index(index, paths, reverse, context_lines)\n  return self:diff_helper(false, true, index, paths, reverse, context_lines)\nend\n\n-- Gets diff from head to workdir\n---@param index GitIndex? Repository index, can be null\n---@param paths string[]? Git paths, can be null\n---@param reverse? boolean whether to reverse the diff\n---@param context_lines integer? number of context lines\n---@return GitDiff?\n---@return GIT_ERROR\nfunction Repository:diff_head_to_workdir(index, paths, reverse, context_lines)\n  return self:diff_helper(true, false, index, paths, reverse, context_lines)\nend\n\n---@param include_workdir boolean Whether to do include workd_dir in diff target\n---@param include_index boolean Wheter to include index in diff target\n---@param index GitIndex? Repository index, can be null\n---@param paths string[]? Git paths, can be null\n---@param reverse boolean? Reverse diff\n---@param context_lines integer? number of context lines\n---@return GitDiff?\n---@return GIT_ERROR\nfunction Repository:diff_helper(include_workdir, include_index, index, paths, reverse, context_lines)\n  local c_paths, err\n  local opts = libgit2.git_diff_options(libgit2.GIT_DIFF_OPTIONS_INIT)\n  local find_opts = libgit2.git_diff_find_options(libgit2.GIT_DIFF_FIND_OPTIONS_INIT)\n  local diff = libgit2.git_diff_double_pointer()\n\n  opts[0].id_abbrev = 8\n  opts[0].flags, find_opts[0].flags = git_status_flags_to_diff_flags(DEFAULT_STATUS_FLAGS)\n\n  if reverse then\n    opts[0].flags = bit.bor(opts[0].flags, libgit2.GIT_DIFF.REVERSE)\n  end\n\n  if paths and #paths > 0 then\n    c_paths = libgit2.const_char_pointer_array(#paths, paths)\n    opts[0].pathspec.strings = c_paths\n    opts[0].pathspec.count = #paths\n  end\n\n  if context_lines then\n    opts[0].context_lines = context_lines\n  end\n\n  if include_workdir and include_index then\n    -- diff workdir to index\n    err = libgit2.C.git_diff_index_to_workdir(diff, self.repo, index and index.index or nil, opts)\n  elseif include_workdir or include_index then\n    -- diff workd_dir to head or index to head\n\n    local head, head_tree\n    head, err = self:head()\n    -- if there is no HEAD, that's okay - we'll make an empty iterator\n    if err ~= 0 and err ~= libgit2.GIT_ERROR.GIT_ENOTFOUND and err ~= libgit2.GIT_ERROR.GIT_EUNBORNBRANCH then\n      return nil, err\n    end\n    if head then\n      head_tree, err = head:peel(libgit2.GIT_OBJECT.TREE)\n      if err ~= 0 then\n        return nil, err\n      end\n    end\n\n    if include_index then\n      -- diff index to head\n      err = libgit2.C.git_diff_tree_to_index(\n        diff,\n        self.repo,\n        head_tree and ffi.cast(libgit2.git_tree_pointer, head_tree.obj) or nil,\n        index and index.index or nil,\n        opts\n      )\n    else\n      -- diff workd_dir to head\n      err = libgit2.C.git_diff_tree_to_workdir(\n        diff,\n        self.repo,\n        head_tree and ffi.cast(libgit2.git_tree_pointer, head_tree.obj) or nil,\n        opts\n      )\n    end\n  else\n    return nil, 0\n  end\n\n  if err ~= 0 then\n    return nil, err\n  end\n\n  -- call this to detect rename\n  if\n    include_workdir and bit.band(DEFAULT_STATUS_FLAGS, libgit2.GIT_STATUS.WT_RENAMED) ~= 0\n    or (include_index and bit.band(DEFAULT_STATUS_FLAGS, libgit2.GIT_STATUS.INDEX_RENAMED) ~= 0)\n  then\n    err = libgit2.C.git_diff_find_similar(diff[0], find_opts)\n    if err ~= 0 then\n      libgit2.C.git_diff_free(diff[0])\n      return nil, err\n    end\n  end\n\n  return Diff.new(diff[0]), 0\nend\n\n-- Gets diff from tree to tree\n---@param old_tree GitTree? A git_tree object to diff from.\n---@param new_tree GitTree? A git_tree object to diff to.\n---@param paths string[]? Git paths, can be null\n---@param context_lines integer? number of context lines\n---@return GitDiff?\n---@return GIT_ERROR\nfunction Repository:diff_tree_to_tree(old_tree, new_tree, paths, context_lines)\n  local c_paths, err\n  local opts = libgit2.git_diff_options(libgit2.GIT_DIFF_OPTIONS_INIT)\n  local find_opts = libgit2.git_diff_find_options(libgit2.GIT_DIFF_FIND_OPTIONS_INIT)\n  local diff = libgit2.git_diff_double_pointer()\n\n  opts[0].id_abbrev = 8\n  opts[0].flags, find_opts[0].flags = git_status_flags_to_diff_flags(DEFAULT_STATUS_FLAGS)\n\n  if paths and #paths > 0 then\n    c_paths = libgit2.const_char_pointer_array(#paths, paths)\n    opts[0].pathspec.strings = c_paths\n    opts[0].pathspec.count = #paths\n  end\n\n  if context_lines then\n    opts[0].context_lines = context_lines\n  end\n\n  err = libgit2.C.git_diff_tree_to_tree(\n    diff,\n    self.repo,\n    old_tree and old_tree.tree or nil,\n    new_tree and new_tree.tree or nil,\n    opts\n  )\n  if err ~= 0 then\n    return nil, err\n  end\n\n  return Diff.new(diff[0]), 0\nend\n\n-- Gets diff from commit to commit.\n---@param from_commit GitCommit? A git_tree object to diff from.\n---@param to_commit GitCommit? A git_tree object to diff to.\n---@param paths string[]? Git paths, can be null\n---@param context_lines integer? number of context lines\n---@return GitDiff?\n---@return GIT_ERROR\nfunction Repository:diff_commit_to_commit(from_commit, to_commit, paths, context_lines)\n  local old_tree, new_tree, diff, err\n  if from_commit then\n    old_tree, err = from_commit:tree()\n    if not old_tree then\n      return nil, err\n    end\n  end\n\n  if to_commit then\n    new_tree, err = to_commit:tree()\n    if not new_tree then\n      return nil, err\n    end\n  end\n\n  diff, err = self:diff_tree_to_tree(old_tree, new_tree, paths, context_lines)\n  return diff, err\nend\n\n---Applies a diff into workdir\n---@param diff GitDiff\n---@return GIT_ERROR\nfunction Repository:apply_workdir(diff)\n  return self:apply(diff, true, false)\nend\n\n---Applies a diff into index\n---@param diff GitDiff\n---@return GIT_ERROR\nfunction Repository:apply_index(diff)\n  return self:apply(diff, false, true)\nend\n\n---Applies a diff into index and workdir\n---@param diff GitDiff\n---@return GIT_ERROR\nfunction Repository:apply_workdir_index(diff)\n  return self:apply(diff, true, true)\nend\n\n---Applies a diff into workdir or index\n---@param diff GitDiff\n---@param workdir boolean Apply to workdir\n---@param index boolean Apply to index\n---@return GIT_ERROR\nfunction Repository:apply(diff, workdir, index)\n  if not (workdir or index) then\n    return 0\n  end\n\n  local opts = libgit2.git_apply_options()\n  libgit2.C.git_apply_options_init(opts, libgit2.GIT_APPLY_OPTIONS_VERSION)\n  local location = 0\n  if workdir then\n    location = bit.bor(location, libgit2.GIT_APPLY_LOCATION.WORKDIR)\n  end\n  if index then\n    location = bit.bor(location, libgit2.GIT_APPLY_LOCATION.INDEX)\n  end\n\n  return libgit2.C.git_apply(self.repo, diff.diff, location, opts)\nend\n\n---@param oid GitObjectId\n---@return GitTag?\n---@return GIT_ERROR\nfunction Repository:tag_lookup(oid)\n  local git_tag = libgit2.git_tag_double_pointer()\n\n  local err = libgit2.C.git_tag_lookup(git_tag, self.repo, oid.oid)\n  if err ~= 0 then\n    return nil, err\n  end\n\n  return Tag.new(git_tag[0]), 0\nend\n\n---Lookup a branch by its name in a repository.\n---@param branch_name string branch name\n---@param branch_type GIT_BRANCH\n---@return GitReference?\n---@return GIT_ERROR\nfunction Repository:branch_lookup(branch_name, branch_type)\n  local ref = libgit2.git_reference_double_pointer()\n\n  local err = libgit2.C.git_branch_lookup(ref, self.repo, branch_name, branch_type)\n  if err ~= 0 then\n    return nil, err\n  end\n\n  return Reference.new(ref[0]), 0\nend\n\n---@param remote_name string remote name\n---@return string?\n---@return GIT_ERROR\nfunction Repository:remote_default_branch(remote_name)\n  local remote_head = string.format(\"refs/remotes/%s/HEAD\", remote_name)\n  local ref, err = self:reference_lookup(remote_head)\n  if not ref then\n    return nil, err\n  end\n\n  local target = ref:symbolic_target()\n  if not target then\n    return nil, err\n  end\n\n  return target, 0\nend\n\n---Initializes a rebase operation\n---@param branch GitAnnotatedCommit?\n---@param upstream GitAnnotatedCommit?\n---@param onto GitAnnotatedCommit?\n---@param opts { inmemory: boolean? }\n---@return GitRebase?\n---@return GIT_ERROR\nfunction Repository:rebase_init(branch, upstream, onto, opts)\n  local git_rebase = libgit2.git_rebase_double_pointer()\n  local rebase_opts = libgit2.git_rebase_options(libgit2.GIT_REBASE_OPTIONS_INIT)\n\n  if opts.inmemory then\n    rebase_opts[0].inmemory = 1\n  end\n\n  local err = libgit2.C.git_rebase_init(\n    git_rebase,\n    self.repo,\n    branch and branch.commit,\n    upstream and upstream.commit,\n    onto and onto.commit,\n    rebase_opts\n  )\n  if err ~= 0 then\n    return nil, 0\n  end\n\n  return Rebase.new(git_rebase[0]), 0\nend\n\n---Opens an existing rebase.\n---@return GitRebase?\n---@return GIT_ERROR\nfunction Repository:rebase_open()\n  local git_rebase = libgit2.git_rebase_double_pointer()\n  local opts = libgit2.git_rebase_options(libgit2.GIT_REBASE_OPTIONS_INIT)\n\n  local err = libgit2.C.git_rebase_open(git_rebase, self.repo, opts)\n  if err ~= 0 then\n    return nil, 0\n  end\n\n  return Rebase.new(git_rebase[0]), 0\nend\n\n-- ==============================\n-- | Repository async functions |\n-- ==============================\n\n-- Gets blame for single file but run in async\n---@param callback fun(b: GitBlame?, err: GIT_ERROR)\nfunction Repository:blame_file_async(path, opts, callback)\n  local blame_opts, err = init_blame_options(opts)\n  if not blame_opts then\n    return callback(nil, err)\n  end\n\n  local work_fn = function(lib, repo_ptr, p, opts_ptr)\n    local lg2 = require \"blink.tree.git.libgit2\"\n    lg2.load_library(lib)\n    local ffi_ = require \"ffi\"\n\n    local repo = ffi_.cast(lg2.git_repository_pointer, repo_ptr)\n    local o = ffi_.cast(\"git_blame_options*\", opts_ptr)\n    local git_blame = lg2.git_blame_double_pointer()\n\n    local e = lg2.C.git_blame_file(git_blame, repo, p, o)\n    if e ~= 0 then\n      return nil, e\n    end\n\n    return tonumber(ffi_.cast(lg2.pointer_t, git_blame[0])), 0\n  end\n\n  local after_work_fn = function(ptr, e)\n    if not ptr or ptr == 0 then\n      return callback(nil, err)\n    end\n\n    local blame = Blame.new(ffi.cast(libgit2.git_blame_pointer, ptr))\n    callback(blame, 0)\n  end\n\n  local work = uv.new_work(work_fn, after_work_fn)\n  work:queue(\n    libgit2_library_path,\n    tonumber(ffi.cast(libgit2.pointer_t, self.repo)),\n    path,\n    tonumber(ffi.cast(libgit2.pointer_t, blame_opts))\n  )\nend\n\n-- Reads repo status but in async.\n---@param callback fun(result: GitStatusItem[]?, err: GIT_ERROR)\nfunction Repository:status_async(callback)\n  ---@param lib string?\n  ---@param repo_ptr integer\n  ---@param flags integer\n  ---@return integer?\n  ---@return GIT_ERROR\n  local function new_git_status_list(lib, repo_ptr, flags)\n    local lg2 = require \"blink.tree.git.libgit2\"\n    lg2.load_library(lib)\n    local ffi_ = require \"ffi\"\n\n    local opts = lg2.git_status_options(lg2.GIT_STATUS_OPTIONS_INIT)\n    opts[0].show = lg2.GIT_STATUS_SHOW.INDEX_AND_WORKDIR\n    opts[0].flags = flags\n\n    local repo = ffi_.cast(lg2.git_repository_pointer, repo_ptr)\n    local status = lg2.git_status_list_double_pointer()\n    local err = lg2.C.git_status_list_new(status, repo, opts)\n    if err ~= 0 then\n      return nil, err\n    end\n    return tonumber(ffi_.cast(lg2.pointer_t, status[0])), 0\n  end\n\n  ---@param git_status_list_ptr integer?\n  ---@param err GIT_ERROR\n  local function handle_git_status_list(git_status_list_ptr, err)\n    if not git_status_list_ptr or git_status_list_ptr == 0 then\n      return callback(nil, err)\n    end\n\n    local status = ffi.cast(libgit2.git_status_list_pointer, git_status_list_ptr)\n    local status_list = git_status_list_to_items(status)\n\n    -- free C resources\n    libgit2.C.git_status_list_free(status)\n\n    callback(status_list, err)\n  end\n\n  local work = uv.new_work(new_git_status_list, handle_git_status_list)\n  work:queue(libgit2_library_path, tonumber(ffi.cast(libgit2.pointer_t, self.repo)), DEFAULT_STATUS_FLAGS)\nend\n\n-- ===================\n-- | Utils functions |\n-- ===================\n\n---Set a library global option\n---@param option GIT_OPT\n---@param value integer\n---@return GIT_ERROR\nlocal function libgit2_set_opts(option, value)\n  return libgit2.C.git_libgit2_opts(option, value)\nend\n\n---@param delta GIT_DELTA\n---@return string char Git status char such as M, A, D.\nlocal function status_char(delta)\n  local c = libgit2.C.git_diff_status_char(delta)\n  return string.char(c)\nend\n\n---Same as status_char but replace \" \" by \"-\"\n---@param delta GIT_DELTA\n---@return string Git status char such as M, A, D.\nlocal function status_char_dash(delta)\n  if delta == libgit2.GIT_DELTA.CONFLICTED then\n    return \"U\"\n  end\n\n  local c = libgit2.C.git_diff_status_char(delta)\n  if c == 32 then\n    return \"-\"\n  end\n  return string.char(c)\nend\n\n---@param delta GIT_DELTA\n---@return string status status full string such as \"UNTRACKED\"\nlocal function status_string(delta)\n  return GIT_DELTA_STRING[delta + 1]\nend\n\n---Prettifiy git message\n---@param msg string\nlocal function message_prettify(msg)\n  local c_buf = libgit2.git_buf()\n\n  -- local err = libgit2.C.git_buf_grow(c_buf, msg:len() + 1)\n  -- if err ~= 0 then\n  --   return nil, err\n  -- end\n\n  local err = libgit2.C.git_message_prettify(c_buf, msg, 1, string.byte \"#\")\n  if err ~= 0 then\n    libgit2.C.git_buf_dispose(c_buf)\n    return nil, err\n  end\n\n  local prettified = ffi.string(c_buf[0].ptr, c_buf[0].size)\n  libgit2.C.git_buf_dispose(c_buf)\n\n  return prettified, 0\nend\n\n-- ==================\n-- | Git2Module     |\n-- ==================\n\n---@class Git2Module\nlocal M = {}\n\n-- Inits luajit-git2 lib\n---@param path string? optional path to libgit2 lib\nfunction M.init(path)\n  libgit2.load_library(path)\n  libgit2_library_path = path\n  if libgit2_init_count == 0 then\n    -- TODO: defer this\n    libgit2_init_count = libgit2.C.git_libgit2_init()\n  end\nend\n\nM.Config = Config\nM.Diff = Diff\nM.IndexEntry = IndexEntry\nM.ObjectId = ObjectId\nM.Reference = Reference\nM.Repository = Repository\n\nM.GIT_BRANCH = libgit2.GIT_BRANCH\nM.GIT_CHECKOUT = libgit2.GIT_CHECKOUT\nM.GIT_DELTA = libgit2.GIT_DELTA\nM.GIT_ERROR = libgit2.GIT_ERROR\nM.GIT_INDEX_STAGE = libgit2.GIT_INDEX_STAGE\nM.GIT_OBJECT = libgit2.GIT_OBJECT\nM.GIT_OPT = libgit2.GIT_OPT\nM.GIT_REBASE_NO_OPERATION = libgit2.GIT_REBASE_NO_OPERATION\nM.GIT_REBASE_OPERATION = libgit2.GIT_REBASE_OPERATION\nM.GIT_REFERENCE = libgit2.GIT_REFERENCE\nM.GIT_REFERENCE_NAMESPACE = GIT_REFERENCE_NAMESPACE\n\nM.head = Repository.head\nM.init_blame_options = init_blame_options\nM.message_prettify = message_prettify\nM.reference_name_namespace = reference_name_namespace\nM.reference_name_remote = reference_name_remote\nM.reference_name_shorthand = reference_name_shorthand\nM.set_opts = libgit2_set_opts\nM.status = Repository.status\nM.status_char = status_char\nM.status_char_dash = status_char_dash\nM.status_string = status_string\n\nfunction M.destroy()\n  libgit2_init_count = libgit2.C.git_libgit2_shutdown()\nend\n\nreturn M\n"
  },
  {
    "path": "lua/blink/tree/git/ignore.lua",
    "content": "local uv = vim.uv\nlocal ignore = {}\n\nfunction ignore.new(path)\n  local self = setmetatable({}, { __index = ignore })\n  self.path = path\n  self.ignore = {}\n\n  return self\nend\n\nfunction ignore:read()\n  local fd = uv.fs_open(self.path, 'r', 438)\n  if not fd then return end\n\n  local data = uv.fs_read(fd, uv.fs_stat(self.path).size, 0)\n  uv.fs_close(fd)\n\n  if not data then return end\n  self.ignore = vim.split(data, '\\n')\nend\n\nfunction ignore.rule_to_matcher(rule)\n  local pattern = rule:gsub('\\\\', '/')\n  if rule:sub(1, 1) == '/' then pattern = '^' .. pattern end\n  if rule:sub(-1, -1) == '/' then pattern = pattern .. '$' end\n\n  return function(path)\n    path = path:gsub('\\\\', '/')\n    return vim.startswith(path, pattern)\n  end\nend\n\nfunction ignore:is_ignored(path)\n  path = path:gsub('\\\\', '/')\n  for _, pattern in ipairs(self.ignore) do\n    if vim.startswith(path, pattern) then return true end\n  end\n  return false\nend\n\nreturn ignore\n"
  },
  {
    "path": "lua/blink/tree/git/init.lua",
    "content": "local Git = {}\n\nlocal function debounce(func, wait)\n  local timer\n  return function(...)\n    local args = { ... }\n    if timer and not timer:is_closing() then timer:stop() end\n    timer = vim.loop.new_timer()\n    timer:start(\n      wait,\n      0,\n      vim.schedule_wrap(function()\n        timer:stop()\n        if not timer:is_closing() then timer:close() end\n        func(unpack(args))\n      end)\n    )\n  end\nend\n\nfunction Git.new(path, on_change)\n  local self = setmetatable({}, { __index = Git })\n  self.path = path\n  self.status = {}\n\n  local git2 = require('blink.tree.git.git2')\n  git2.init()\n\n  local err\n  self.repository, err = git2.Repository.open(path, false)\n  if err > 0 then\n    print('Failed to open repository: ' .. err)\n    return\n  end\n\n  local debounced_update_status = debounce(function() self:update_status(on_change) end, 10)\n\n  -- watch .git for changes\n  self.watch_unsubscribe = require('blink.tree.lib.fs').watch_dir(path .. '/.git', debounced_update_status)\n\n  -- poll every 5s\n  vim.loop.new_timer():start(5000, 5000, function() debounced_update_status() end)\n\n  return self\nend\n\nfunction Git:set_status(path, status)\n\tlocal parts = vim.split(path, '/')\n\tlocal current = self.status\n\tfor i = 1, #parts do\n\t\tlocal part = parts[i]\n\t\tif i == #parts then\n\t\t\tcurrent[part] = status\n\t\telseif current[part] == nil then\n\t\t\tcurrent[part] = {}\n\t\tend\n\t\tcurrent = current[part]\n\tend\nend\n\nfunction Git:get_status(path)\n\tlocal parts = vim.split(path, '/')\n\tlocal current = self.status\n\tfor i = 1, #parts do\n\t\tlocal part = parts[i]\n\t\tif current[part] == nil then\n\t\t\treturn nil\n\t\tend\n\t\tcurrent = current[part]\n\tend\n\treturn current\nend\n\nfunction Git:update_status(callback)\n  callback = callback or function() end\n  self.repository:status_async(function(status, err)\n    if err > 0 then\n      print('Failed to get status' .. err)\n      callback()\n    end\n\n    self.status = {}\n    for _, item in ipairs(status) do\n      local path = self.path .. '/' .. (item.new_path or item.path)\n\t\t\tself:set_status(path, item)\n    end\n    -- print(vim.inspect(self.status))\n    if callback then callback() end\n  end)\nend\n\nlocal function flat_status(node)\n    local leaves = {}\n\n    local function traverse(current_node)\n        if type(current_node) ~= \"table\" then\n            return\n        end\n\n        if current_node.index_status ~= nil then\n            table.insert(leaves, current_node)\n        else\n            for _, child in pairs(current_node) do\n                traverse(child)\n            end\n        end\n    end\n\n    traverse(node)\n    return leaves\nend\n\nfunction Git.get_hl_for_status(status, is_dir)\n  local git = require('blink.tree.git.git2')\n  local DELTA = git.GIT_DELTA\n\n\tif is_dir then\n\t\tlocal flat = flat_status(status)\n\t\tlocal worktree_statuses = {}\n\t\tlocal has_staged = false\n\n\t\tfor _, node_status in ipairs(flat) do\n\t\t\tlocal idx = node_status.index_status\n\t\t\tlocal wt = node_status.worktree_status\n\n\t\t\tif idx ~= DELTA.UNMODIFIED and idx ~= DELTA.UNTRACKED then has_staged = true end\n\t\t\tif not worktree_statuses[wt] then worktree_statuses[wt] = true end\n\t\tend\n\n\t\tif has_staged then return 'BlinkTreeGitStaged'\n\t\telseif worktree_statuses[DELTA.MODIFIED] then return 'BlinkTreeGitModified'\n\t\telseif worktree_statuses[DELTA.RENAMED] then return 'BlinkTreeGitRenamed'\n\t\telseif worktree_statuses[DELTA.ADDED] then return 'BlinkTreeGitAdded'\n\t\telseif worktree_statuses[DELTA.CONFLICTED] then return 'BlinkTreeGitConflict'\n\t\telseif worktree_statuses[DELTA.UNTRACKED] then return 'BlinkTreeGitUntracked'\n\t\tend\n\telse\n\t\tlocal index_status = status.index_status\n\t\tlocal worktree_status = status.worktree_status\n\n\t\tif index_status ~= DELTA.UNMODIFIED and index_status ~= DELTA.UNTRACKED then\n\t\t\treturn 'BlinkTreeGitStaged'\n\t\tend\n\n\t\tif worktree_status == DELTA.MODIFIED then return 'BlinkTreeGitModified' end\n\t\tif worktree_status == DELTA.RENAMED then return 'BlinkTreeGitRenamed' end\n\t\tif worktree_status == DELTA.ADDED then return 'BlinkTreeGitAdded' end\n\t\tif worktree_status == DELTA.CONFLICTED then return 'BlinkTreeGitConflict' end\n\t\tif worktree_status == DELTA.UNTRACKED then return 'BlinkTreeGitUntracked' end\n\tend\nend\n\nfunction Git:destroy() self.watch_unsubscribe() end\n\nreturn Git\n"
  },
  {
    "path": "lua/blink/tree/git/libgit2.lua",
    "content": "-- Made by SuperBo in Fugit2\n-- https://github.com/SuperBo/fugit2.nvim/tree/70662d529fe98790d7b2104b4dd67dd229332194\n-- Licensed under MIT\n\nlocal ffi = require('ffi')\n\n-- =====================\n-- | Libgit2 C section |\n-- =====================\n\n--- Load libgit2 via ffi\nffi.cdef([[\n  typedef uint64_t git_object_size_t;\n  typedef int64_t git_off_t;\n  typedef int64_t git_time_t;\n\n  typedef struct git_annotated_commit git_annotated_commit;\n  typedef struct git_blame git_blame;\n  typedef struct git_blob git_blob;\n  typedef struct git_branch_iterator git_branch_iterator;\n  typedef struct git_commit git_commit;\n  typedef struct git_config git_config;\n  typedef struct git_config_iterator git_config_iterator;\n  typedef struct git_diff git_diff;\n  typedef struct git_diff_stats git_diff_stats;\n  typedef struct git_index git_index;\n  typedef struct git_index_conflict_iterator git_index_conflict_iterator;\n  typedef struct git_index_iterator git_index_iterator;\n  typedef struct git_object git_object;\n  typedef struct git_patch git_patch;\n  typedef struct git_rebase git_rebase;\n  typedef struct git_reference git_reference;\n  typedef struct git_remote git_remote;\n  typedef struct git_repository git_repository;\n  typedef struct git_revwalk git_revwalk;\n  typedef struct git_status_list git_status_list;\n  typedef struct git_tag git_tag;\n  typedef struct git_tree git_tree;\n  typedef struct git_tree_entry git_tree_entry;\n\n  typedef struct git_strarray {\n    char **strings;\n    size_t count;\n  } git_strarray;\n\n  typedef struct git_strarray_readonly {\n    const char **strings;\n    size_t count;\n  } git_strarray_readonly;\n\n  typedef struct git_buf {\n    char *ptr;\n    size_t reserved;\n    size_t size;\n  } git_buf;\n\n  typedef struct git_oid {\n\t  unsigned char id[20];\n  } git_oid;\n\n  typedef struct git_time {\n    git_time_t time;\n    int offset;\n    char sign;\n  } git_time;\n\n  typedef struct git_signature {\n    char *name;\n    char *email;\n    git_time when;\n  } git_signature;\n\n  typedef struct git_blame_hunk {\n\t  size_t lines_in_hunk;\n\t  git_oid final_commit_id;\n\t  size_t final_start_line_number;\n\t  git_signature *final_signature;\n\t  git_oid orig_commit_id;\n\t  const char *orig_path;\n\t  size_t orig_start_line_number;\n\t  git_signature *orig_signature;\n\t  char boundary;\n  } git_blame_hunk;\n\n  typedef struct git_blame_options {\n\t  unsigned int version;\n\t  uint32_t flags;\n\t  uint16_t min_match_characters;\n\t  git_oid newest_commit;\n\t  git_oid oldest_commit;\n    size_t min_line;\n    size_t max_line;\n  } git_blame_options;\n\n  typedef struct git_config_entry {\n    const char *name;\n    const char *value;\n    const char *backend_type;\n    const char *origin_path;\n    unsigned int include_depth;\n    unsigned int level;\n    void (*free)(struct git_config_entry *entry);\n  } git_config_entry;\n\n\n  typedef struct git_diff_hunk {\n    int    old_start;\n    int    old_lines;\n    int    new_start;\n    int    new_lines;\n    size_t header_len;\n    char   header[128];\n  } git_diff_hunk;\n\n  typedef struct git_diff_line {\n    char   origin;\n    int    old_lineno;\n    int    new_lineno;\n    int    num_lines;\n    size_t content_len;\n    git_off_t content_offset;\n    const char *content;\n  } git_diff_line;\n\n  typedef struct git_diff_file {\n    git_oid            id;\n    const char *       path;\n    git_object_size_t  size;\n    uint32_t           flags;\n    uint16_t           mode;\n    uint16_t           id_abbrev;\n  } git_diff_file;\n\n  typedef struct git_diff_delta {\n    int           status;\n    uint32_t      flags;\n    uint16_t      similarity;\n    uint16_t      nfiles;\n    git_diff_file old_file;\n    git_diff_file new_file;\n  } git_diff_delta;\n\n  typedef int (*git_diff_notify_cb)(\n    const git_diff *diff_so_far,\n    const struct git_diff_delta *delta_to_add,\n    const char *matched_pathspec,\n\t  void *payload\n  );\n\n  typedef int (*git_diff_progress_cb)(\n    const git_diff *diff_so_far,\n    const char *old_path,\n    const char *new_path,\n    void *payload\n  );\n\n  typedef struct git_diff_options {\n    unsigned int          version;\n    uint32_t              flags;\n    unsigned int          ignore_submodules;\n    git_strarray_readonly pathspec;\n    git_diff_notify_cb    notify_cb;\n    git_diff_progress_cb  progress_cb;\n    void *                payload;\n    uint32_t              context_lines;\n    uint32_t              interhunk_lines;\n    unsigned int          oid_type;\n    uint16_t              id_abbrev;\n    int64_t               max_size;\n    const char *          old_prefix;\n    const char *          new_prefix;\n  } git_diff_options ;\n\n  typedef struct git_diff_similarity_metric {\n    int (*file_signature)(\n      void **out, const git_diff_file *file,\n      const char *fullpath, void *payload\n    );\n    int (*buffer_signature)(\n      void **out, const git_diff_file *file,\n      const char *buf, size_t buflen, void *payload\n    );\n    void (*free_signature)(void *sig, void *payload);\n    int (*similarity)(int *score, void *siga, void *sigb, void *payload);\n    void *payload;\n  } git_diff_similarity_metric;\n\n  typedef struct git_diff_find_options {\n    unsigned int version;\n    uint32_t     flags;\n    uint16_t     rename_threshold;\n    uint16_t     rename_from_rewrite_threshold;\n    uint16_t     copy_threshold;\n    uint16_t     break_rewrite_threshold;\n    size_t       rename_limit;\n    git_diff_similarity_metric *metric;\n  } git_diff_find_options;\n\n  typedef int (* git_apply_delta_cb)(const git_diff_delta *delta, void *payload);\n  typedef int (* git_apply_hunk_cb)(const git_diff_hunk *hunk, void *payload);\n\n  typedef struct git_apply_options {\n    unsigned int version; /**< The version */\n    git_apply_delta_cb delta_cb;\n    git_apply_hunk_cb hunk_cb;\n    void *payload;\n    unsigned int flags;\n  } git_apply_options;\n\n  typedef int (*git_commit_create_cb)(\n    git_oid *out,\n    const git_signature *author,\n    const git_signature *committer,\n    const char *message_encoding,\n    const char *message,\n    const git_tree *tree,\n    size_t parent_count,\n    const git_commit **parents,\n    void *payload\n  );\n  typedef int (*git_checkout_notify_cb)(\n    unsigned int why,\n    const char *path,\n    const git_diff_file *baseline,\n    const git_diff_file *target,\n    const git_diff_file *workdir,\n    void *payload\n  );\n  typedef void (*git_checkout_progress_cb)(\n    const char *path,\n    size_t completed_steps,\n    size_t total_steps,\n    void *payload\n  );\n  typedef struct git_checkout_perfdata {\n    size_t mkdir_calls;\n    size_t stat_calls;\n    size_t chmod_calls;\n  } git_checkout_perfdata;\n  typedef void (*git_checkout_perfdata_cb)(\n    const git_checkout_perfdata *perfdata,\n    void *payload\n  );\n\n  typedef struct git_checkout_options {\n    unsigned int version;\n    unsigned int checkout_strategy;\n    int disable_filters;    /**< don't apply filters like CRLF conversion */\n    unsigned int dir_mode;  /**< default is 0755 */\n    unsigned int file_mode; /**< default is 0644 or 0755 as dictated by blob */\n    int file_open_flags;    /**< default is O_CREAT | O_TRUNC | O_WRONLY */\n    unsigned int notify_flags; /**< see `git_checkout_notify_t` above */\n    git_checkout_notify_cb notify_cb;\n    void *notify_payload;\n    git_checkout_progress_cb progress_cb;\n    void *progress_payload;\n    git_strarray_readonly paths;\n    git_tree *baseline;\n    git_index *baseline_index;\n    const char *target_directory; /**< alternative checkout path to workdir */\n    const char *ancestor_label; /**< the name of the common ancestor side of conflicts */\n    const char *our_label; /**< the name of the \"our\" side of conflicts */\n    const char *their_label; /**< the name of the \"their\" side of conflicts */\n    git_checkout_perfdata_cb perfdata_cb;\n    void *perfdata_payload;\n  } git_checkout_options;\n\n  typedef struct git_merge_options {\n    unsigned int version;\n    uint32_t flags;\n    unsigned int rename_threshold;\n    unsigned int target_limit;\n    git_diff_similarity_metric *metric;\n    unsigned int recursion_limit;\n    const char *default_driver;\n    unsigned int file_favor;\n    uint32_t file_flags;\n  } git_merge_options;\n\n  typedef struct git_rebase_options {\n    unsigned int version;\n    int quiet;\n    int inmemory;\n    const char *rewrite_notes_ref;\n    git_merge_options merge_options;\n    git_checkout_options checkout_options;\n    git_commit_create_cb commit_create_cb;\n    void *reserved;\n    void *payload;\n  } git_rebase_options;\n\n  typedef struct git_rebase_operation {\n    unsigned int type;\n    const git_oid id;\n    const char *exec;\n  } git_rebase_operation;\n\n  typedef struct git_status_entry {\n    unsigned int status;\n    struct git_diff_delta *head_to_index;\n    struct git_diff_delta *index_to_workdir;\n  } git_status_entry;\n\n  typedef struct git_status_options {\n\t  unsigned int     version;\n\t  int              show;\n\t  unsigned int     flags;\n\t  git_strarray     pathspec;\n\t  struct git_tree* baseline;\n\t  uint16_t         rename_threshold;\n  } git_status_options;\n\n  typedef struct {\n    int32_t seconds;\n    /* nsec should not be stored as time_t compatible */\n    uint32_t nanoseconds;\n  } git_index_time;\n\n  typedef struct git_index_entry {\n    git_index_time ctime;\n    git_index_time mtime;\n\n    uint32_t dev;\n    uint32_t ino;\n    uint32_t mode;\n    uint32_t uid;\n    uint32_t gid;\n    uint32_t file_size;\n\n    git_oid id;\n\n    uint16_t flags;\n    uint16_t flags_extended;\n\n    const char *path;\n  } git_index_entry;\n\n  int git_libgit2_init();\n  int git_libgit2_shutdown();\n  int git_libgit2_opts(int option, ...);\n\n  void git_strarray_dispose(git_strarray *array);\n\n  int git_buf_grow(git_buf *buffer, size_t target_size);\n  void git_buf_dispose(git_buf *buffer);\n\n  int git_blame_options_init(git_blame_options *opts, unsigned int version);\n  void git_blame_free(git_blame *blame);\n  int git_blame_buffer(git_blame **out, git_blame *reference, const char *buffer, size_t buffer_len);\n  int git_blame_file(git_blame **out, git_repository *repo, const char *path, git_blame_options *options);\n  const git_blame_hunk * git_blame_get_hunk_byindex(git_blame *blame, uint32_t index);\n  const git_blame_hunk * git_blame_get_hunk_byline(git_blame *blame, size_t lineno);\n  uint32_t git_blame_get_hunk_count(git_blame *blame);\n\n  int git_blob_lookup(git_blob **blob, git_repository *repo, const git_oid *id);\n  const void * git_blob_rawcontent(const git_blob *blob);\n  int git_blob_is_binary(const git_blob *blob);\n  git_object_size_t git_blob_rawsize(const git_blob *blob);\n  const void * git_blob_rawcontent(const git_blob *blob);\n  void git_blob_free(git_blob *blob);\n\n  int git_checkout_head(git_repository *repo, const git_checkout_options *opts);\n  int git_checkout_index(git_repository *repo, git_index *index, const git_checkout_options *opts);\n  int git_checkout_tree(git_repository *repo, const git_object *treeish, const git_checkout_options *opts);\n\n  char * git_oid_tostr(char *out, size_t n, const git_oid *id);\n  char * git_oid_tostr_s(const git_oid *oid);\n  int git_oid_equal(const git_oid *a, const git_oid *b);\n  int git_oid_cpy(git_oid *out, const git_oid *src);\n  int git_oid_fromstr(git_oid *out, const char *str);\n  int git_oid_fromstrp(git_oid *out, const char *str);\n  int git_oid_fromstrn(git_oid *out, const char *str, size_t length);\n\n  char git_diff_status_char(unsigned int status);\n  int git_message_prettify(git_buf *out, const char *message, int strip_comments, char comment_char);\n\n  void git_object_free(git_object *object);\n  int git_object_lookup_bypath(git_object **out, const git_object *treeish, const char *path, int type);\n  const git_oid * git_object_id(const git_object *obj);\n\n  int git_apply_options_init(git_apply_options *opts, unsigned int version);\n  int git_apply(git_repository *repo, git_diff *diff, unsigned int location, const git_apply_options *options);\n\n  int git_commit_lookup(git_commit **commit, git_repository *repo, const git_oid *id);\n  int git_commit_lookup_prefix(git_commit **commit, git_repository *repo, const git_oid *id, size_t len);\n  void git_commit_free(git_commit *commit);\n  const git_signature * git_commit_author(const git_commit *commit);\n  const git_signature * git_commit_committer(const git_commit *commit);\n  const git_oid * git_commit_id(const git_commit *commit);\n  git_repository * git_commit_owner(const git_commit *commit);\n  const char * git_commit_message(const git_commit *commit);\n  const char * git_commit_message_encoding(const git_commit *commit);\n  const char * git_commit_summary(git_commit *commit);\n  const char * git_commit_body(git_commit *commit);\n  git_time_t git_commit_time(const git_commit *commit);\n  int git_commit_extract_signature(git_buf *signature, git_buf *signed_data, git_repository *repo, git_oid *commit_id, const char *field);\n  unsigned int git_commit_parentcount(const git_commit *commit);\n  int git_commit_parent(git_commit **out, const git_commit *commit, unsigned int n);\n  const git_oid * git_commit_parent_id(const git_commit *commit, unsigned int n);\n  int git_commit_tree(git_tree **tree_out, const git_commit *commit);\n  int git_commit_create_v(\n    git_oid *id,\n    git_repository *repo,\n    const char *update_ref,\n    const git_signature *author,\n    const git_signature *committer,\n    const char *message_encoding,\n    const char *message,\n    const git_tree *tree,\n    size_t parent_count,\n    ...\n  );\n  int git_commit_create(\n    git_oid *id,\n    git_repository *repo,\n    const char *update_ref,\n    const git_signature *author,\n    const git_signature *committer,\n    const char *message_encoding,\n    const char *message,\n    const git_tree *tree,\n    size_t parent_count,\n    const git_commit **parents\n  );\n  int git_commit_create_buffer(\n    git_buf *out,\n    git_repository *repo,\n    const git_signature *author,\n    const git_signature *committer,\n    const char *message_encoding,\n    const char *message,\n    const git_tree *tree,\n    size_t parent_count,\n    const git_commit **parents\n  );\n  int git_commit_create_with_signature(\n    git_oid *out,\n    git_repository *repo,\n    const char *commit_content,\n    const char *signature,\n    const char *signature_field\n  );\n  int git_commit_amend(\n    git_oid *id,\n    const git_commit *commit_to_amend,\n    const char *update_ref,\n    const git_signature *author,\n    const git_signature *committer,\n    const char *message_encoding,\n    const char *message,\n    const git_tree *tree\n  );\n\n  int git_config_open_default(git_config **out);\n  int git_config_open_level(git_config **out, const git_config *parent, int level);\n  void git_config_free(git_config *cfg);\n  int git_config_get_entry(git_config_entry **out, const git_config *cfg, const char *name);\n  int git_config_get_int32(int32_t *out, const git_config *cfg, const char *name);\n  int git_config_get_int64(int64_t *out, const git_config *cfg, const char *name);\n  int git_config_get_bool(int *out, const git_config *cfg, const char *name);\n  int git_config_get_path(git_buf *out, const git_config *cfg, const char *name);\n  int git_config_get_string(const char **out, const git_config *cfg, const char *name);\n  int git_config_get_string_buf(git_buf *out, const git_config *cfg, const char *name);\n  void git_config_entry_free(git_config_entry *entry);\n  int git_config_iterator_new(git_config_iterator **out, const git_config *cfg);\n  int git_config_next(git_config_entry **entry, git_config_iterator *iter);\n  void git_config_iterator_free(git_config_iterator *iter);\n\n  int git_diff_find_similar(git_diff *diff, const git_diff_find_options *options);\n  int git_diff_index_to_workdir(git_diff **diff, git_repository *repo, git_index *index, const git_diff_options *opts);\n  int git_diff_tree_to_index(git_diff **diff, git_repository *repo, git_tree *old_tree, git_index *index, const git_diff_options *opts);\n  int git_diff_tree_to_workdir(git_diff **diff, git_repository *repo, git_tree *old_tree, const git_diff_options *opts);\n  int git_diff_tree_to_tree(git_diff **diff, git_repository *repo, git_tree *old_tree, git_tree *new_tree, const git_diff_options *opts);\n  int git_diff_to_buf(git_buf *out, git_diff *diff, unsigned int format);\n  int git_diff_from_buffer(git_diff **out, const char *content, size_t content_len);\n  int git_diff_get_stats(git_diff_stats **out, git_diff *diff);\n  const git_diff_delta * git_diff_get_delta(const git_diff *diff, size_t idx);\n  size_t git_diff_num_deltas(const git_diff *diff);\n  void git_diff_free(git_diff *diff);\n\n  int git_diff_stats_to_buf(git_buf *out, const git_diff_stats *stats, unsigned int format, size_t width);\n  size_t git_diff_stats_files_changed(const git_diff_stats *stats);\n  size_t git_diff_stats_insertions(const git_diff_stats *stats);\n  size_t git_diff_stats_deletions(const git_diff_stats *stats);\n  void git_diff_stats_free(git_diff_stats *stats);\n\n  int git_patch_from_diff(git_patch **out, git_diff *diff, size_t idx);\n  int git_patch_to_buf(git_buf *out, git_patch *patch);\n  size_t git_patch_num_hunks(const git_patch *patch);\n  int git_patch_get_hunk(const git_diff_hunk **out, size_t *lines_in_hunk, git_patch *patch, size_t hunk_idx);\n  int git_patch_get_line_in_hunk(const git_diff_line **out, git_patch *patch, size_t hunk_idx, size_t line_of_hunk);\n  int git_patch_num_lines_in_hunk(const git_patch *patch, size_t hunk_idx);\n  int git_patch_line_stats(size_t *total_context, size_t *total_additions, size_t *total_deletions, const git_patch *patch);\n  void git_patch_free(git_patch *patch);\n\n  const char * git_reference_shorthand(const git_reference *ref);\n  const char * git_reference_name(const git_reference *ref);\n  int git_reference_resolve(git_reference **out, const git_reference *ref);\n  void git_reference_free(git_reference *ref);\n  int git_reference_type(const git_reference *ref);\n  const git_oid * git_reference_target(const git_reference *ref);\n  int git_reference_set_target(git_reference **out, git_reference *ref, const git_oid *id, const char *log_message);\n  int git_reference_peel(git_object **out, const git_reference *ref, int type);\n  int git_reference_name_to_id(git_oid *out, git_repository *repo, const char *name);\n  int git_reference_lookup(git_reference **out, git_repository *repo, const char *name);\n  const char * git_reference_symbolic_target(const git_reference *ref);\n  int git_reference_create(git_reference **out, git_repository *repo, const char *name, const git_oid *id, int force, const char *log_message);\n  int git_reference_symbolic_create(git_reference **out, git_repository *repo, const char *name, const char *target, int force, const char *log_message);\n\n  int git_revwalk_new(git_revwalk **walker, git_repository *repo);\n  int git_revwalk_push(git_revwalk *walk, const git_oid *oid);\n  int git_revwalk_push_head(git_revwalk *walk);\n  int git_revwalk_push_ref(git_revwalk *walk, const char *refname);\n  int git_revwalk_next(git_oid *oid, git_revwalk *walk);\n  int git_revwalk_hide(git_revwalk *walk, const git_oid *oid);\n  void git_revwalk_sorting(git_revwalk *walk, unsigned int sort_mode);\n  void git_revwalk_free(git_revwalk *walk);\n  int git_revwalk_reset(git_revwalk *walker);\n\n  int git_remote_lookup(git_remote **out, git_repository *repo, const char *name);\n  int git_remote_list(git_strarray *out, git_repository *repo);\n  const char * git_remote_name(const git_remote *remote);\n  const char * git_remote_url(const git_remote *remote);\n  const char * git_remote_pushurl(const git_remote *remote);\n  int git_remote_default_branch(git_buf *out, git_remote *remote);\n  int git_remote_disconnect(git_remote *remote);\n  void git_remote_free(git_remote *remote);\n\n  int git_branch_iterator_new(git_branch_iterator **out, git_repository *repo, unsigned int list_flags);\n  int git_branch_next(git_reference **out, unsigned int *out_type, git_branch_iterator *iter);\n  void git_branch_iterator_free(git_branch_iterator *iter);\n  int git_branch_upstream(git_reference **out, const git_reference *branch);\n  int git_branch_remote_name(git_buf *out, git_repository *repo, const char *refname);\n  int git_branch_upstream_remote(git_buf *buf, git_repository *repo, const char *refname);\n  int git_branch_upstream_name(git_buf *out, git_repository *repo, const char *refname);\n  int git_branch_lookup(git_reference **out, git_repository *repo, const char *branch_name, unsigned int branch_type);\n  int git_branch_create(git_reference **out, git_repository *repo, const char *branch_name, const git_commit *target, int force);\n\n  int git_repository_open_ext(git_repository **out, const char *path, unsigned int flags, const char *ceiling_dirs);\n  void git_repository_free(git_repository *repo);\n  const char* git_repository_path(const git_repository *repo);\n  int git_repository_is_empty(git_repository *repo);\n  int git_repository_is_bare(const git_repository *repo);\n  int git_repository_head_detached(git_repository *repo);\n  int git_repository_head(git_reference **out, git_repository *repo);\n  int git_repository_index(git_index **out, git_repository *repo);\n  int git_repository_config(git_config **out, git_repository *repo);\n  int git_repository_set_head(git_repository *repo, const char *refname);\n  int git_repository_set_head_detached(git_repository *repo, const git_oid *committish);\n\n  void git_index_free(git_index *index);\n  int git_index_read(git_index *index, int force);\n  int git_index_write(git_index *index);\n  int git_index_write_tree(git_oid *out, git_index *index);\n  const char * git_index_path(const git_index *index);\n  int git_index_add_from_buffer(git_index *index, const git_index_entry *entry, const void *buffer, size_t len);\n  int git_index_add_bypath(git_index *index, const char *path);\n  int git_index_remove_bypath(git_index *index, const char *path);\n  int git_index_remove_directory(git_index *index, const char *dir, int stage);\n  size_t git_index_entrycount(const git_index *index);\n  int git_index_has_conflicts(const git_index *index);\n  int git_index_conflict_get(const git_index_entry **ancestor_out, const git_index_entry **our_out, const git_index_entry **their_out, git_index *index, const char *path);\n  const git_index_entry * git_index_get_bypath(git_index *index, const char *path, int stage);\n\n  int git_status_list_new(git_status_list **out, git_repository *repo, const git_status_options *opts);\n  void git_status_list_free(git_status_list *statuslist);\n  size_t git_status_list_entrycount(git_status_list *statuslist);\n  const git_status_entry* git_status_byindex(git_status_list *statuslist, size_t idx);\n  int git_status_should_ignore(int *ignored, git_repository *repo, const char *path);\n  int git_status_file(unsigned int *status_flags, git_repository *repo, const char *path);\n\n  int git_tree_lookup(git_tree **out, git_repository *repo, const git_oid *id);\n  void git_tree_free(git_tree *tree);\n  size_t git_tree_entrycount(const git_tree *tree);\n  const git_oid * git_tree_id(const git_tree *tree);\n\n  int git_tree_entry_bypath(git_tree_entry **out, const git_tree *root, const char *path);\n  const git_tree_entry * git_tree_entry_byname(const git_tree *tree, const char *filename);\n  const git_tree_entry * git_tree_entry_byid(const git_tree *tree, const git_oid *id);\n  const git_tree_entry * git_tree_entry_byindex(const git_tree *tree, size_t idx);\n\n  const git_oid * git_tree_entry_id(const git_tree_entry *entry);\n  void git_tree_entry_free(git_tree_entry *entry);\n  const char * git_tree_entry_name(const git_tree_entry *entry);\n  int git_tree_entry_type(const git_tree_entry *entry);\n  int git_tree_entry_to_object(git_object **object_out, git_repository *repo, const git_tree_entry *entry);\n\n  int git_reset_default(git_repository *repo, const git_object *target, const git_strarray_readonly *pathspecs);\n\n  int git_graph_ahead_behind(size_t *ahead, size_t *behind, git_repository *repo, const git_oid *local, const git_oid *upstream);\n  int git_graph_descendant_of(git_repository *repo, const git_oid *commit, const git_oid *ancestor);\n\n  int git_signature_default(git_signature **out, git_repository *repo);\n  void git_signature_free(git_signature *sig);\n\n  int git_tag_lookup(git_tag **out, git_repository *repo, const git_oid *id);\n  const char * git_tag_name(const git_tag *tag);\n  void git_tag_free(git_tag *tag);\n  int git_tag_list(git_strarray *tag_names, git_repository *repo);\n\n  int git_annotated_commit_from_ref(git_annotated_commit **out, git_repository *repo, const git_reference *ref);\n  int git_annotated_commit_from_revspec(git_annotated_commit **out, git_repository *repo, const char *revspec);\n  const git_oid * git_annotated_commit_id(const git_annotated_commit *commit);\n  const char * git_annotated_commit_ref(const git_annotated_commit *commit);\n  void git_annotated_commit_free(git_annotated_commit *commit);\n\n  int git_rebase_init(git_rebase **out, git_repository *repo, const git_annotated_commit *branch, const git_annotated_commit *upstream, const git_annotated_commit *onto, const git_rebase_options *opts);\n  int git_rebase_open(git_rebase **out, git_repository *repo, const git_rebase_options *opts);\n  const char * git_rebase_orig_head_name(git_rebase *rebase);\n  const git_oid * git_rebase_orig_head_id(git_rebase *rebase);\n  const char * git_rebase_onto_name(git_rebase *rebase);\n  const git_oid * git_rebase_onto_id(git_rebase *rebase);\n  size_t git_rebase_operation_entrycount(git_rebase *rebase);\n  size_t git_rebase_operation_current(git_rebase *rebase);\n  git_rebase_operation * git_rebase_operation_byindex(git_rebase *rebase, size_t idx);\n  int git_rebase_inmemory_index(git_index **index, git_rebase *rebase);\n  int git_rebase_next(git_rebase_operation **operation, git_rebase *rebase);\n  int git_rebase_commit(git_oid *id, git_rebase *rebase, const git_signature *author, const git_signature *committer, const char *message_encoding, const char *message);\n  int git_rebase_abort(git_rebase *rebase);\n  int git_rebase_finish(git_rebase *rebase, const git_signature *signature);\n  void git_rebase_free(git_rebase *rebase);\n]])\n\n---@class Libgit2Module\n---@field C ffi.namespace*\nlocal M = {}\n\n---@param path string?\nM.load_library = function(path)\n  if not M.C then rawset(M, 'C', ffi.load(path or 'libgit2')) end\nend\n\nM.uint32 = ffi.typeof('uint32_t')\nM.pointer_t = ffi.typeof('intptr_t')\nM.char_pointer = ffi.typeof('char*')\nM.char_array = ffi.typeof('char[?]')\nM.const_char_pointer_array = ffi.typeof('const char *[?]')\nM.unsigned_int_array = ffi.typeof('unsigned int[?]')\nM.size_t_array = ffi.typeof('size_t[?]')\nM.int64_array = ffi.typeof('int64_t[?]')\nM.int_array = ffi.typeof('int[?]')\n\n---@type ffi.ctype* git_buf[1]\nM.git_buf = ffi.typeof('git_buf[1]')\n\n---@type ffi.ctype* git_config*[1]\nM.git_config_double_pointer = ffi.typeof('git_config*[1]')\n---@type ffi.ctype* git_config* pointer\nM.git_config_pointer = ffi.typeof('git_config*')\n\n---@type ffi.ctype* git_config_entry*[1]\nM.git_config_entry_double_pointer = ffi.typeof('git_config_entry*[1]')\n\n---@type ffi.ctype* git_config_iterator*[1]\nM.git_config_iterator_double_pointer = ffi.typeof('git_config_iterator*[1]')\n\n---@type ffi.ctype* git_checkout_options[1]\nM.git_checkout_options = ffi.typeof('git_checkout_options[1]')\n\n---@type ffi.ctype* git_oid[1]\nM.git_oid = ffi.typeof('git_oid[1]')\n---@type ffi.ctype* git_oid*\nM.git_oid_pointer = ffi.typeof('git_oid*')\n\n---@type ffi.ctype* git_strarray_readonly[1]\nM.git_strarray_readonly = ffi.typeof('git_strarray_readonly[1]')\n---@type ffi.ctype* git_strarray[1]\nM.git_strarray = ffi.typeof('git_strarray[1]')\n\n---@type ffi.ctype* git_annotated_commit **\nM.git_annotated_commit_double_pointer = ffi.typeof('git_annotated_commit*[1]')\n---@type ffi.ctype* git_annotated_commit *\nM.git_annotated_commit_pointer = ffi.typeof('git_annotated_commit*')\n\n---@type ffi.ctype* git_object **\nM.git_object_double_pointer = ffi.typeof('git_object*[1]')\n---@type ffi.ctype* git_object *\nM.git_object_pointer = ffi.typeof('git_object*')\n\n---@type ffi.ctype* git_blame **\nM.git_blame_double_pointer = ffi.typeof('git_blame*[1]')\n---@type ffi.ctype* git_blame*\nM.git_blame_pointer = ffi.typeof('git_blame*')\n---@type ffi.ctype* const git_blame_hunk *\nM.git_blame_hunk_pointer = ffi.typeof('const git_blame_hunk*')\n---@type ffi.ctype* git_blame_options [1]\nM.git_blame_options = ffi.typeof('git_blame_options[1]')\n\n---@type ffi.ctype* git_commit **\nM.git_commit_double_pointer = ffi.typeof('git_commit*[1]')\n---@type ffi.ctype* git_commit *\nM.git_commit_pointer = ffi.typeof('git_commit*')\n---@type ffi.ctype* git_commit * array\nM.git_commit_pointer_array = ffi.typeof('git_commit*[?]')\n---@type ffi.ctype* const git_commit **\nM.git_commit_const_double_pointer = ffi.typeof('const git_commit**')\n\n---@type ffi.ctype* git_blob **\nM.git_blob_double_pointer = ffi.typeof('git_blob*[1]')\n---@type ffi.ctype* git_blob *\nM.git_blob_pointer = ffi.typeof('git_blob*')\n\n---@type ffi.ctype* git_tree **\nM.git_tree_double_pointer = ffi.typeof('git_tree*[1]')\n---@type ffi.ctype* git_tree *\nM.git_tree_pointer = ffi.typeof('git_tree*')\n\n---@type ffi.ctype* git_tree_entry*[1]\nM.git_tree_entry_double_pointer = ffi.typeof('git_tree_entry*[1]')\n---@type ffi.ctype* git_tree_entry*\nM.git_tree_entry_pointer = ffi.typeof('git_tree_entry*')\n\n---@type ffi.ctype* git_rebase_options[1]\nM.git_rebase_options = ffi.typeof('git_rebase_options[1]')\n\n---@type ffi.ctype* git_apply_options[1]\nM.git_apply_options = ffi.typeof('git_apply_options[1]')\n\n---@type ffi.ctype* struct git_diff **\nM.git_diff_double_pointer = ffi.typeof('git_diff*[1]')\n---@type ffi.ctype* struct git_diff *\nM.git_diff_pointer = ffi.typeof('git_diff*')\n\n---@type ffi.ctype* struct git_diff_options [1]\nM.git_diff_options = ffi.typeof('git_diff_options[1]')\n\n---@type ffi.ctype* struct git_diff_find_options [1]\nM.git_diff_find_options = ffi.typeof('git_diff_find_options[1]')\n\n---@type ffi.ctype* struct git_diff_hunk *[1]\nM.git_diff_hunk_double_pointer = ffi.typeof('const git_diff_hunk*[1]')\n\n---@type ffi.ctype* const struct git_diff_line **out\nM.git_diff_line_double_pointer = ffi.typeof('const git_diff_line*[1]')\n\n---@type ffi.ctype* struct git_diff_stats *[1]\nM.git_diff_stats_double_pointer = ffi.typeof('git_diff_stats*[1]')\n\n---@type ffi.ctype* struct git_patch **\nM.git_patch_double_pointer = ffi.typeof('git_patch*[1]')\n---@type ffi.ctype* struct git_patch *\nM.git_patch_pointer = ffi.typeof('git_patch*')\n\n---@type ffi.ctype* git_rebase **\nM.git_rebase_double_pointer = ffi.typeof('git_rebase*[1]')\n---@type ffi.ctype* git_rebase *\nM.git_rebase_pointer = ffi.typeof('git_rebase*')\n\n---@type ffi.ctype* git_rebase_operation_double_pointer\nM.git_rebase_operation_double_pointer = ffi.typeof('git_rebase_operation*[1]')\n---@type ffi.ctype* git_rebase_operation struct pointer\nM.git_rebase_operation_pointer = ffi.typeof('git_rebase_operation*')\n\n---@type ffi.ctype* struct git_repository**\nM.git_repository_double_pointer = ffi.typeof('git_repository*[1]')\n---@type ffi.ctype* struct git_repository*\nM.git_repository_pointer = ffi.typeof('git_repository*')\n\n---@type ffi.ctype* struct git_reference*[1]\nM.git_reference_double_pointer = ffi.typeof('git_reference*[1]')\n\n---@type ffi.ctype* struct git_reference*\nM.git_reference_pointer = ffi.typeof('git_reference*')\n\n---@type ffi.ctype* struct git_remote**\nM.git_remote_double_pointer = ffi.typeof('git_remote*[1]')\n---@type ffi.ctype* struct git_remote*\nM.git_remote_pointer = ffi.typeof('git_remote*')\n\n---@type ffi.ctype* struct git_revwalk**\nM.git_revwalk_double_pointer = ffi.typeof('git_revwalk*[1]')\n\n---@type ffi.ctype* struct git_revwalk*\nM.git_revwalk_pointer = ffi.typeof('git_revwalk*')\n\n---@type ffi.ctype* git_signature **\nM.git_signature_double_pointer = ffi.typeof('git_signature*[1]')\n---@type ffi.ctype* git_signature *\nM.git_signature_pointer = ffi.typeof('git_signature*')\n\n---@type ffi.ctype* git_status_options[1]\nM.git_status_options = ffi.typeof('git_status_options[1]')\n\n---@type ffi.ctype* struct git_status_list*[1]\nM.git_status_list_double_pointer = ffi.typeof('git_status_list*[1]')\n---@type ffi.ctype* struct git_status_list*\nM.git_status_list_pointer = ffi.typeof('git_status_list*')\n\n---@type ffi.ctype* git_tag*[1]\nM.git_tag_double_pointer = ffi.typeof('git_tag*[1]')\n---@type ffi.ctype* git_tag*\nM.git_tag_pointer = ffi.typeof('git_tag*')\n\n---@type ffi.ctype* git_index**\nM.git_index_double_pointer = ffi.typeof('git_index*[1]')\n---@type ffi.ctype* git_index*\nM.git_index_pointer = ffi.typeof('git_index*')\n\n---@type ffi.ctype* git_index_iterator**\nM.git_index_iterator_double_pointer = ffi.typeof('git_index_iterator*[1]')\n---@type ffi.ctype* git_index_entry**\nM.git_index_entry_double_pointer = ffi.typeof('git_index_entry*[1]')\n---@type ffi.ctype* git_index_entry pointer array\nM.git_index_entry_pointer_array = ffi.typeof('const git_index_entry*[?]')\n---@type ffi.ctype* git_index_entry pointer\nM.git_index_entry_pointer = ffi.typeof('const git_index_entry*')\n---@type ffi.ctype* git_index_entry[1]\nM.git_index_entry = ffi.typeof('git_index_entry[1]')\n\n---@type ffi.ctype* struct git_branch_iterator *[1]\nM.git_branch_iterator_double_pointer = ffi.typeof('git_branch_iterator *[1]')\n\n-- ==========================\n-- | libgit2 struct version |\n-- ==========================\n\nlocal _UI64_MAX = 0xffffffffffffffffULL\n\nM.GIT_APPLY_OPTIONS_VERSION = 1\nM.GIT_BLAME_OPTIONS_VERSION = 1\nM.GIT_CHECKOUT_OPTIONS_VERSION = 1\nM.GIT_DIFF_FIND_OPTIONS_VERSION = 1\nM.GIT_DIFF_OPTIONS_VERSION = 1\nM.GIT_FETCH_OPTIONS_VERSION = 1\nM.GIT_MERGE_OPTIONS_VERSION = 1\nM.GIT_PROXY_OPTIONS_VERSION = 1\nM.GIT_REBASE_OPTIONS_VERSION = 1\nM.GIT_REMOTE_CALLBACKS_VERSION = 1\nM.GIT_STATUS_OPTIONS_VERSION = 1\n\nM.GIT_REBASE_NO_OPERATION = _UI64_MAX\n\n-- ================\n-- | libgit2 enum |\n-- ================\n\nlocal POW = {\n  2, -- 1 << 1\n  4, -- 1 << 2\n  8, -- 1 << 3\n  16, -- 1 << 4\n  32, -- 1 << 5\n  64, -- 1 << 6\n  0x80, -- 1 << 7\n  0x100, -- 1 << 8\n  0x200, -- 1 << 9\n  0x400, -- 1 << 10\n  0x800, -- 1 << 11\n  0x1000, -- 1 << 12\n  0x2000, -- 1 << 13\n  0x4000, -- 1 << 14\n  0x8000, -- 1 << 15\n  0x10000, -- 1 << 16\n  0x20000, -- 1 << 17\n  0x40000, -- 1 << 18\n  0x80000, -- 1 << 19\n  0x100000, -- 1 << 20\n  0x200000, -- 1 << 21\n  0x400000, -- 1 << 22\n  0x800000, -- 1 << 23\n  0x1000000, -- 1 << 24\n  0x2000000, -- 1 << 25\n  0x4000000, -- 1 << 26\n  0x8000000, -- 1 << 27\n  0x10000000, -- 1 << 28\n  0x20000000, -- 1 << 29\n  0x40000000, -- 1 << 30\n  0x80000000, -- 1 << 31\n  0x100000000, -- 1 << 32\n}\n\n---@enum GIT_OPT\nM.GIT_OPT = {\n  GET_MWINDOW_SIZE = 0,\n  SET_MWINDOW_SIZE = 1,\n  GET_MWINDOW_MAPPED_LIMIT = 2,\n  SET_MWINDOW_MAPPED_LIMIT = 3,\n  GET_SEARCH_PATH = 4,\n  SET_SEARCH_PATH = 5,\n  SET_CACHE_OBJECT_LIMIT = 6,\n  SET_CACHE_MAX_SIZE = 7,\n  ENABLE_CACHING = 8,\n  GET_CACHED_MEMORY = 9,\n  GET_TEMPLATE_PATH = 10,\n  SET_TEMPLATE_PATH = 11,\n  SET_SSL_CERT_LOCATIONS = 12,\n  SET_USER_AGENT = 13,\n  ENABLE_STRICT_OBJECT_CREATION = 14,\n  ENABLE_STRICT_SYMBOLIC_REF_CREATION = 15,\n  SET_SSL_CIPHERS = 16,\n  GET_USER_AGENT = 17,\n  ENABLE_OFS_DELTA = 18,\n  ENABLE_FSYNC_GITDIR = 19,\n  GET_WINDOWS_SHAREMODE = 20,\n  SET_WINDOWS_SHAREMODE = 21,\n  ENABLE_STRICT_HASH_VERIFICATION = 22,\n  SET_ALLOCATOR = 23,\n  ENABLE_UNSAVED_INDEX_SAFETY = 24,\n  GET_PACK_MAX_OBJECTS = 25,\n  SET_PACK_MAX_OBJECTS = 26,\n  DISABLE_PACK_KEEP_FILE_CHECKS = 27,\n  ENABLE_HTTP_EXPECT_CONTINUE = 28,\n  GET_MWINDOW_FILE_LIMIT = 29,\n  SET_MWINDOW_FILE_LIMIT = 30,\n  SET_ODB_PACKED_PRIORITY = 31,\n  SET_ODB_LOOSE_PRIORITY = 32,\n  GET_EXTENSIONS = 33,\n  SET_EXTENSIONS = 34,\n  GET_OWNER_VALIDATION = 35,\n  SET_OWNER_VALIDATION = 36,\n  GET_HOMEDIR = 37,\n  SET_HOMEDIR = 38,\n  SET_SERVER_CONNECT_TIMEOUT = 39,\n  GET_SERVER_CONNECT_TIMEOUT = 40,\n  SET_SERVER_TIMEOUT = 41,\n  GET_SERVER_TIMEOUT = 42,\n}\n\n---@enum GIT_APPLY_LOCATION\nM.GIT_APPLY_LOCATION = {\n  WORKDIR = 0,\n  INDEX = 1,\n  BOTH = 2,\n}\n\nM.GIT_BLAME = {\n  NORMAL = 0,\n  TRACK_COPIES_SAME_FILE = 1, -- not yet implemented\n  TRACK_COPIES_SAME_COMMIT_MOVES = 2, -- not yet implemented\n  TRACK_COPIES_SAME_COMMIT_COPIES = 4, -- not yet implemented\n  TRACK_COPIES_ANY_COMMIT_COPIES = 8, -- not yet implemented\n  FIRST_PARENT = POW[4], -- (1<<4)\n  USE_MAILMAP = POW[5], -- (1<<5)\n  IGNORE_WHITESPACE = POW[6], -- (1<<6)\n}\n\n---@enum GIT_BRANCH\nM.GIT_BRANCH = {\n  LOCAL = 1,\n  REMOTE = 2,\n  ALL = 3, -- GIT_BRANCH_LOCAL|GIT_BRANCH_REMOTE,\n}\n\n---@enum GIT_CONFIG_LEVEL\nM.GIT_CONFIG_LEVEL = {\n  -- System-wide on Windows, for compatibility with portable git\n  PROGRAMDATA = 1,\n  -- System-wide configuration file; /etc/gitconfig on Linux systems */\n  SYSTEM = 2,\n  -- XDG compatible configuration file; typically ~/.config/git/config */\n  XDG = 3,\n  -- User-specific configuration file (also called Global configuration\n  -- file); typically ~/.gitconfig\n  GLOBAL = 4,\n  -- Repository specific configuration file; $WORK_DIR/.git/config on\n  -- non-bare repos\n  LOCAL = 5,\n  -- Application specific configuration file; freely defined by applications\n  APP = 6,\n  -- Represents the highest level available config file (i.e. the most\n  -- specific config file available that actually is loaded)\n  HIGHEST_LEVEL = -1,\n}\n---@enum GIT_ERROR\nM.GIT_ERROR = {\n  GIT_OK = 0, -- No error\n\n  GIT_ERROR = -1, -- Generic error\n  GIT_ENOTFOUND = -3, -- Requested object could not be found\n  GIT_EEXISTS = -4, -- Object exists preventing operation\n  GIT_EAMBIGUOUS = -5, -- More than one object matches\n  GIT_EBUFS = -6, -- Output buffer too short to hold data\n  --[[\n  GIT_EUSER is a special error that is never generated by libgit2\n  code.  You can return it from a callback (e.g to stop an iteration)\n  to know that it was generated by the callback and not by libgit2.\n  ]]\n  --\n  GIT_EUSER = -7,\n\n  GIT_EBAREREPO = -8, -- Operation not allowed on bare repository\n  GIT_EUNBORNBRANCH = -9, -- HEAD refers to branch with no commits\n  GIT_EUNMERGED = -10, -- Merge in progress prevented operation\n  GIT_ENONFASTFORWARD = -11, -- Reference was not fast-forwardable\n  GIT_EINVALIDSPEC = -12, -- Name/ref spec was not in a valid format\n  GIT_ECONFLICT = -13, -- Checkout conflicts prevented operation\n  GIT_ELOCKED = -14, -- Lock file prevented operation\n  GIT_EMODIFIED = -15, -- Reference value does not match expected\n  GIT_EAUTH = -16, -- Authentication error\n  GIT_ECERTIFICATE = -17, -- Server certificate is invalid\n  GIT_EAPPLIED = -18, -- Patch/merge has already been applied\n  GIT_EPEEL = -19, -- The requested peel operation is not possible\n  GIT_EEOF = -20, -- Unexpected EOF\n  GIT_EINVALID = -21, -- Invalid operation or input\n  GIT_EUNCOMMITTED = -22, -- Uncommitted changes in index prevented operation\n  GIT_EDIRECTORY = -23, -- The operation is not valid for a directory\n  GIT_EMERGECONFLICT = -24, -- A merge conflict exists and cannot continue\n  GIT_PASSTHROUGH = -30, -- A user-configured callback refused to act\n  GIT_ITEROVER = -31, -- Signals end of iteration with iterator\n  GIT_RETRY = -32, -- Internal only\n  GIT_EMISMATCH = -33, -- Hashsum mismatch in object\n  GIT_EINDEXDIRTY = -34, -- Unsaved changes in the index would be overwritten\n  GIT_EAPPLYFAIL = -35, -- Patch application failed\n  GIT_EOWNER = -36, -- The object is not owned by the current user\n  GIT_TIMEOUT = -37, -- The operation timed out\n}\n\n---@enum GIT_INDEX_STAGE\nM.GIT_INDEX_STAGE = {\n  ANY = -1, -- Match any index stage.\n  NORMAL = 0, -- A normal staged file in the index.\n  ANCESTOR = 1, -- The ancestor side of a conflict.\n  OURS = 2, -- The \"ours\" side of a conflict.\n  THEIRS = 3, -- The \"theirs\" side of a conflict.\n}\n\n---@enum GIT_REFERENCE\nM.GIT_REFERENCE = {\n  INVALID = 0, -- Invalid reference\n  DIRECT = 1, -- A reference that points at an object id\n  SYMBOLIC = 2, -- A reference that points at another reference\n  ALL = 3, -- Both GIT_REFERENCE_DIRECT | GIT_REFERENCE_SYMBOLIC\n}\n\n---@enum GIT_OBJECT\nM.GIT_OBJECT = {\n  ANY = -2, -- Object can be any of the following.\n  INVALID = -1, -- Object is invalid.\n  COMMIT = 1, -- A commit object.\n  TREE = 2, -- A tree (directory listing) object.\n  BLOB = 3, -- A file revision object.\n  TAG = 4, -- An annotated tag object.\n  OFS_DELTA = 6, -- A delta, base is given by an offset.\n  REF_DELTA = 7, -- A delta, base is given by object id.\n}\n\n---@enum GIT_SORT\nM.GIT_SORT = {\n  NONE = 0, -- 0, default method from `git`: reverse chronological order\n  TOPOLOGICAL = 1, -- 1 << 0, Sort the repository contents in topological order\n  TIME = 2, -- 1 << 1, Sort the repository contents by commit time.\n  REVERSE = 4, -- 1 << 2, Iterate through the repository contents in reverse order.\n}\n\n---@enum GIT_STATUS\nM.GIT_STATUS = {\n  CURRENT = 0,\n\n  INDEX_NEW = 1, -- 1u << 0\n  INDEX_MODIFIED = POW[1], -- 1u << 1\n  INDEX_DELETED = POW[2], -- 1u << 2\n  INDEX_RENAMED = POW[3], -- 1u << 3\n  INDEX_TYPECHANGE = POW[4], -- 1u << 4\n\n  WT_NEW = POW[7], -- 1u << 7\n  WT_MODIFIED = POW[8], -- 1u << 8\n  WT_DELETED = POW[9], -- 1u << 9\n  WT_TYPECHANGE = POW[10], -- 1u << 10\n  WT_RENAMED = POW[11], -- 1u << 11\n  WT_UNREADABLE = POW[12], -- 1u << 12\n\n  IGNORED = POW[14], -- 1u << 14\n  CONFLICTED = POW[15], -- 1u << 15\n}\n\n---@enum GIT_STATUS_SHOW\nM.GIT_STATUS_SHOW = {\n  INDEX_AND_WORKDIR = 0,\n  INDEX_ONLY = 1,\n  WORKDIR_ONLY = 2,\n}\n\n---@enum GIT_STATUS_OPT\nM.GIT_STATUS_OPT = {\n  INCLUDE_UNTRACKED = 1, -- (1u << 0),\n  INCLUDE_IGNORED = POW[1], -- (1u << 1),\n  INCLUDE_UNMODIFIED = POW[2], -- (1u << 2),\n  EXCLUDE_SUBMODULES = POW[3], -- (1u << 3),\n  RECURSE_UNTRACKED_DIRS = POW[4], -- (1u << 4),\n  DISABLE_PATHSPEC_MATCH = POW[5], -- (1u << 5),\n  RECURSE_IGNORED_DIRS = POW[6], -- (1u << 6),\n  RENAMES_HEAD_TO_INDEX = POW[7], -- (1u << 7),\n  RENAMES_INDEX_TO_WORKDIR = POW[8], -- (1u << 8),\n  SORT_CASE_SENSITIVELY = POW[9], -- (1u << 9),\n  SORT_CASE_INSENSITIVELY = POW[10], -- (1u << 10),\n  RENAMES_FROM_REWRITES = POW[11], -- (1u << 11),\n  NO_REFRESH = POW[12], -- (1u << 12),\n  UPDATE_INDEX = POW[13], -- (1u << 13),\n  INCLUDE_UNREADABLE = POW[14], -- (1u << 14),\n  INCLUDE_UNREADABLE_AS_UNTRACKED = POW[15], -- (1u << 15)\n}\n\n---@enum GIT_DELTA\nM.GIT_DELTA = {\n  UNMODIFIED = 0, -- no changes\n  ADDED = 1, -- entry does not exist in old version\n  DELETED = 2, -- entry does not exist in new version\n  MODIFIED = 3, -- entry content changed between old and new\n  RENAMED = 4, -- entry was renamed between old and new\n  COPIED = 5, -- entry was copied from another old entry\n  IGNORED = 6, -- entry is ignored item in workdir\n  UNTRACKED = 7, -- entry is untracked item in workdir\n  TYPECHANGE = 8, -- type of entry changed between old and new\n  UNREADABLE = 9, -- entry is unreadable\n  CONFLICTED = 10, -- entry in the index is conflicted\n}\n\n---@enum GIT_REPOSITORY_OPEN\nM.GIT_REPOSITORY_OPEN = {\n  NO_SEARCH = 1,\n\n  --  Unless this flag is set, open will not continue searching across\n  -- filesystem boundaries (i.e. when `st_dev` changes from the `stat`\n  -- system call).  For example, searching in a user's home directory at\n  -- \"/home/user/source/\" will not return \"/.git/\" as the found repo if\n  -- \"/\" is a different filesystem than \"/home\".\n  CROSS_FS = 2,\n\n  -- Open repository as a bare repo regardless of core.bare config, and\n  -- defer loading config file for faster setup.\n  -- Unlike `git_repository_open_bare`, this can follow gitlinks.\n  BARE = 4,\n\n  NO_DOTGIT = 8,\n  FROM_ENV = 16,\n}\n\n---@enum GIT_DIFF_FORMAT\nM.GIT_DIFF_FORMAT = {\n  PATCH = 1, -- full git diff\n  PATCH_HEADER = 2, -- just the file headers of patch\n  RAW = 3, -- like git diff --raw\n  NAME_ONLY = 4, -- like git diff --name-only\n  NAME_STATUS = 5, -- like git diff --name-status\n  PATCH_ID = 6, -- git diff as used by git patch-id\n}\n\n---@enum GIT_DIFF\nM.GIT_DIFF = {\n  NORMAL = 0, --0: Normal diff, the default\n  REVERSE = 1, --(1 << 0): Reverse the sides of the diff\n  INCLUDE_IGNORED = 2, --(1 << 1): Include ignored files in the diff\n  RECURSE_IGNORED_DIRS = 4, --(1 << 2): adds files under the ignored directory as IGNORED entries\n  INCLUDE_UNTRACKED = 8, --(1 << 3): Include untracked files in the diff\n  RECURSE_UNTRACKED_DIRS = 0x10, --(1 << 4): adds files under untracked directories as UNTRACKED entries\n  INCLUDE_UNMODIFIED = 0x20, --(1 << 5): include unmodified files in the diff\n  -- a type change between files will be converted into a\n  -- DELETED record for the old and an ADDED record for the new; this\n  -- options enabled the generation of TYPECHANGE delta records\n  INCLUDE_TYPECHANGE = 0x40, --(1 << 6)\n  -- Even with GIT_DIFF_INCLUDE_TYPECHANGE, blob->tree changes still\n  -- generally show as a DELETED blob.  This flag tries to correctly\n  -- label blob->tree transitions as TYPECHANGE records with new_file's\n  -- mode set to tree.  Note: the tree SHA will not be available.\n  INCLUDE_TYPECHANGE_TREES = 0x80, --(1 << 7)\n  IGNORE_FILEMODE = 0x100, --(1u << 8): Ignore file mode changes\n  IGNORE_SUBMODULES = 0x200, --(1u << 9): Treat all submodules as unmodified\n  IGNORE_CASE = 0x400, --(1u << 10): case insensitive for filename comparisons\n  INCLUDE_CASECHANGE = 0x800, --(1u << 11): combined with `IGNORE_CASE` to specify that a file that has changed case will be returned as an add/delete pair.\n\n  -- If the pathspec is set in the diff options, this flags indicates\n  -- that the paths will be treated as literal paths instead of fnmatch patterns.\n  -- Each path in the list must either be a full path to a file or a directory.\n  -- (A trailing slash indicates that the path will _only_ match a directory).\n  -- If a directory is specified, all children will be included.\n  DISABLE_PATHSPEC_MATCH = 0x1000, --(1u << 12)\n\n  -- Disable updating of the `binary` flag in delta records.  This is\n  -- useful when iterating over a diff if you don't need hunk and data\n  -- callbacks and want to avoid having to load file completely.\n  SKIP_BINARY_CHECK = 0x2000, --(1u << 13)\n\n  -- When diff finds an untracked directory, to match the behavior of\n  -- core Git, it scans the contents for IGNORED and UNTRACKED files.\n  -- If *all* contents are IGNORED, then the directory is IGNORED; if\n  -- any contents are not IGNORED, then the directory is UNTRACKED.\n  -- This is extra work that may not matter in many cases. This flag\n  -- turns off that scan and immediately labels an untracked directory\n  -- as UNTRACKED (changing the behavior to not match core Git).\n  ENABLE_FAST_UNTRACKED_DIRS = 0x4000, --(1u << 14)\n\n  -- When diff finds a file in the working directory with stat\n  -- information different from the index, but the OID ends up being the\n  -- same, write the correct stat information into the index.\n  -- Note: without this flag, diff will always leave the index untouched.\n  UPDATE_INDEX = 0x8000, --(1u << 15)\n  INCLUDE_UNREADABLE = 0x10000, -- 1u << 16): Include unreadable files in the diff\n  INCLUDE_UNREADABLE_AS_UNTRACKED = 0x20000, --(1u << 17): Include unreadable files as UNTRACKED\n\n  -- Options controlling how output will be generated\n\n  -- Use a heuristic that takes indentation and whitespace into account\n  -- which generally can produce better diffs when dealing with ambiguous\n  -- diff hunks.\n  INDENT_HEURISTIC = 0x40000, --(1u << 18)\n  IGNORE_BLANK_LINES = 0x80000, --(1u << 19): Ignore blank lines\n  FORCE_TEXT = 0x100000, --(1u << 20): Treat all files as text, disabling binary attributes & detection\n  FORCE_BINARY = 0x200000, --(1u << 21): Treat all files as binary, disabling text diffs\n  IGNORE_WHITESPACE = 0x400000, --(1u << 22): Ignore all whitespaces\n  IGNORE_WHITESPACE_CHANGE = 0x800000, --(1u << 23): Ignore changes in amount of whitespace\n  IGNORE_WHITESPACE_EOL = 0x1000000, --(1u << 24): Ignore whitespace at end of line\n\n  -- When generating patch text, include the content of untracked files.\n  -- This automatically turns on GIT_DIFF_INCLUDE_UNTRACKED but\n  -- it does not turn on GIT_DIFF_RECURSE_UNTRACKED_DIRS.\n  -- Add that flag if you want the content of every single UNTRACKED file.\n  SHOW_UNTRACKED_CONTENT = 0x2000000, --(1u << 25)\n\n  -- When generating output, include the names of unmodified files if\n  -- they are included in the git_diff. Normally these are skipped in\n  -- the formats that list files (e.g. name-only, name-status, raw).\n  -- Even with this, these will not be included in patch format.\n  SHOW_UNMODIFIED = 0x4000000, --(1u << 26)\n\n  PATIENCE = 0x10000000, --(1u << 28): Use the \"patience diff\" algorithm\n  MINIMAL = 0x20000000, --(1u << 29): Take extra time to find minimal diff\n  -- Include the necessary deflate / delta information so that `git-apply`\n  -- can apply given diff information to binary files.\n  SHOW_BINARY = 0x40000000, --(1u << 30)\n}\n\n---@enum GIT_DIFF_FIND\nM.GIT_DIFF_FIND = {\n  FIND_BY_CONFIG = 0, -- Obey `diff.renames\n  FIND_RENAMES = 1, -- (1u << 0): Look for renames? (`--find-renames`)\n  FIND_RENAMES_FROM_REWRITES = 2, -- (1u << 1)\n  FIND_COPIES = 4, -- (1u << 2)\n  FIND_COPIES_FROM_UNMODIFIED = 8, -- (1u << 3)\n  FIND_REWRITES = POW[4], -- (1u << 4)\n  BREAK_REWRITES = POW[5], -- (1u << 5)\n  FIND_AND_BREAK_REWRITES = 48, -- (GIT_DIFF_FIND_REWRITES | GIT_DIFF_BREAK_REWRITES)\n  FIND_FOR_UNTRACKED = POW[6], -- (1u << 6)\n  FIND_ALL = 0xff, -- (0x0ff) Turn on all finding features.\n  FIND_IGNORE_LEADING_WHITESPACE = 0,\n  FIND_IGNORE_WHITESPACE = POW[12], -- (1u << 12),\n  FIND_DONT_IGNORE_WHITESPACE = POW[13], -- (1u << 13),\n  FIND_EXACT_MATCH_ONLY = POW[14], -- (1u << 14),\n  BREAK_REWRITES_FOR_RENAMES_ONLY = POW[15], -- (1u << 15),\n  FIND_REMOVE_UNMODIFIED = POW[16], -- (1u << 16)\n}\n\n---@enum GIT_SUBMODULE\nM.GIT_SUBMODULE = {\n  IGNORE_UNSPECIFIED = -1, -- use the submodule's configuration\n  IGNORE_NONE = 1, -- any change or untracked == dirty\n  IGNORE_UNTRACKED = 2, -- dirty if tracked files change\n  IGNORE_DIRTY = 3, -- only dirty if HEAD moved\n  IGNORE_ALL = 4, -- never dirty\n}\n\n---@enum GIT_DIFF_LINE\nM.GIT_DIFF_LINE = {\n  CONTEXT = ' ',\n  ADDITION = '+',\n  DELETION = '-',\n  CONTEXT_EOFNL = '=', --Both files have no LF at end\n  ADD_EOFNL = '>', --Old has no LF at end, new does\n  DEL_EOFNL = '<', --Old has LF at end, new does not\n  FILE_HDR = 'F',\n  HUNK_HDR = 'H',\n  BINARY = 'B', -- For \"Binary files x and y differ\"\n}\n\n---@enum GIT_REBASE_OPERATION\nM.GIT_REBASE_OPERATION = {\n  PICK = 0,\n  REWORD = 1,\n  EDIT = 2,\n  SQUASH = 3,\n  FIXUP = 4,\n  EXEC = 5,\n}\n\n---@enum GIT_CHECKOUT\nM.GIT_CHECKOUT = {\n  NONE = 0, -- default is a dry run, no actual updates\n  SAFE = 1, --(1u << 0): Allow safe updates that cannot overwrite uncommitted data.\n  FORCE = 2, --(1u << 1): Allow all updates to force working directory to look like index.\n  RECREATE_MISSING = 4, --(1u << 2): Allow checkout to recreate missing files.\n  ALLOW_CONFLICTS = POW[4], --(1u << 4): Allow checkout to make safe updates even if conflicts are found.\n  REMOVE_UNTRACKED = POW[5], --(1u << 5): Remove untracked files not in index (that are not ignored.\n  REMOVE_IGNORED = POW[6], --(1u << 6): Remove ignored files not in index */\n  UPDATE_ONLY = POW[7], --(1u << 7) Only update existing files, don't create new ones.\n  DONT_UPDATE_INDEX = POW[8], --(1u << 8) Normally checkout updates index entries as it goes; this stops that.\n  NO_REFRESH = POW[9], --(1u << 9) Don't refresh index/config/etc before doing checkout.\n  SKIP_UNMERGED = POW[10], -- (1u << 10) Allow checkout to skip unmerged files,\n  USE_OURS = POW[11], --(1u << 11) For unmerged files, checkout stage 2 from index,\n  USE_THEIRS = POW[12], -- (1u << 12) For unmerged files, checkout stage 3 from index */\n  DISABLE_PATHSPEC_MATCH = POW[13], -- (1u << 13) Treat pathspec as simple list of exact match file paths,\n  SKIP_LOCKED_DIRECTORIES = POW[18], --(1u << 18) Ignore directories in use, they will be left empty\n  DONT_OVERWRITE_IGNORED = POW[19], --(1u << 19) Don't overwrite ignored files that exist in the checkout target\n  CONFLICT_STYLE_MERGE = POW[20], --(1u << 20) Write normal merge files for conflicts\n  CONFLICT_STYLE_DIFF3 = POW[21], --(1u << 21) Include common ancestor data in diff3 format files for conflicts\n  DONT_REMOVE_EXISTING = POW[22], --(1u << 22) Don't overwrite existing files or folders\n  DONT_WRITE_INDEX = POW[23], --(1u << 23) Normally checkout writes the index upon completion; this prevents that.\n  DRY_RUN = POW[24], --(1u << 24),\n  CONFLICT_STYLE_ZDIFF3 = POW[25], --(1u << 25) Include common ancestor data in zdiff3 format for conflicts.\n  --(NOT IMPLEMENTED)\n  UPDATE_SUBMODULES = POW[16], --(1u << 16) Recursively checkout submodules with same options\n  UPDATE_SUBMODULES_IF_CHANGED = POW[17], --(1u << 17) Recursively checkout submodules if HEAD moved in super repo\n}\n\n---@enum GIT_MERGE\nM.GIT_MERGE = {\n  FIND_RENAMES = 1, --(1 << 0): Detect renames that occur between the common ancestor and the \"ours\"\n  FAIL_ON_CONFLICT = POW[1], --(1 << 1): If a conflict occurs, exit immediately\n  SKIP_REUC = POW[2], --(1 << 2): Do not write the REUC extension on the generated index\n  NO_RECURSIVE = POW[3], --(1 << 3): This flag provides a similar merge base to `git-merge-resolve`.\n  VIRTUAL_BASE = POW[4], --(1 << 4): Treat this merge as if it is to produce the virtual base of recursive.\n}\n\n-- Inits helper\n\nlocal NULL = ffi.cast('void*', nil)\n\nM.GIT_APPLY_OPTIONS_INIT = { { M.GIT_APPLY_OPTIONS_VERSION } }\nM.GIT_BLAME_OPTIONS_INIT = { { M.GIT_BLAME_OPTIONS_VERSION } }\nM.GIT_STATUS_OPTIONS_INIT = { { M.GIT_STATUS_OPTIONS_VERSION } }\nM.GIT_DIFF_OPTIONS_INIT = {\n  {\n    M.GIT_STATUS_OPTIONS_VERSION,\n    0,\n    M.GIT_SUBMODULE.IGNORE_UNSPECIFIED,\n    { NULL, 0 },\n    NULL,\n    NULL,\n    NULL,\n    3,\n  },\n}\nM.GIT_DIFF_FIND_OPTIONS_INIT = { { M.GIT_DIFF_FIND_OPTIONS_VERSION } }\nM.GIT_CHECKOUT_OPTIONS_INIT = { { M.GIT_CHECKOUT_OPTIONS_VERSION, M.GIT_CHECKOUT.SAFE } }\nM.GIT_MERGE_OPTIONS_INIT = { { M.GIT_MERGE_OPTIONS_VERSION, M.GIT_MERGE.FIND_RENAMES } }\nM.GIT_REBASE_OPTIONS_INIT = {\n  {\n    M.GIT_REBASE_OPTIONS_VERSION,\n    0,\n    0,\n    NULL,\n    M.GIT_MERGE_OPTIONS_INIT[1],\n    M.GIT_CHECKOUT_OPTIONS_INIT[1],\n    NULL,\n    NULL,\n  },\n}\n\nreturn M\n"
  },
  {
    "path": "lua/blink/tree/git/stat.lua",
    "content": "-- Made by SuperBo in Fugit2\n-- https://github.com/SuperBo/fugit2.nvim/tree/70662d529fe98790d7b2104b4dd67dd229332194\n-- Licensed under MIT\n\nlocal M = {}\n\nlocal S_IFMT = 0xf000\nlocal S_IFSOCK = 0xc000\nlocal S_IFLNK = 0xa000\nlocal S_IFREG = 0x8000\nlocal S_IFBLK = 0x6000\nlocal S_IFDIR = 0x4000\nlocal S_IFCHR = 0x2000\nlocal S_IFIFO = 0x1000\nlocal S_ISUID = 0x800\nlocal S_ISGID = 0x400\nlocal S_ISVTX = 0x200\n\nM.S_IFMT = S_IFMT\nM.S_IFLNK = S_IFLNK\nM.S_IFREG = S_IFREG\nM.S_IFDIR = S_IFDIR\n\n---@param m integer file mode\n---@return boolean\nfunction M.S_ISLNK(m) return bit.band(m, S_IFMT) == S_IFLNK end\n\n---@param m integer file mode\n---@return boolean\nfunction M.S_ISREG(m) return bit.band(m, S_IFMT) == S_IFREG end\n\n---@param m integer file mode\n---@return boolean\nfunction M.S_ISDIR(m) return bit.band(m, S_IFMT) == S_IFDIR end\n\n---@param m integer file mode\n---@return boolean\nfunction M.S_ISCHR(m) return bit.band(m, S_IFMT) == S_IFCHR end\n\n---@param m integer file mode\n---@return boolean\nfunction M.S_ISBLK(m) return bit.band(m, S_IFMT) == S_IFBLK end\n\n---@param m integer file mode\n---@return boolean\nfunction M.S_ISFIFO(m) return bit.band(m, S_IFMT) == S_IFIFO end\n\n---@param m integer file mode\n---@return boolean\nfunction M.S_ISSOCK(m) return bit.band(m, S_IFMT) == S_IFSOCK end\n\nreturn M\n"
  },
  {
    "path": "lua/blink/tree/init.lua",
    "content": "-- todo: symlinks\nlocal api = vim.api\nlocal M = {\n  inst = nil,\n}\n\nfunction M.setup(opts)\n  require('blink.tree.config').setup(opts)\n\n  M.setup_highlights()\n\n  vim.api.nvim_create_user_command('BlinkTree', function(info)\n    local args = vim.split(info.args, ' ')\n    local command = args[1] or 'toggle'\n    local silent = args[2] == 'silent'\n\n    if command == 'toggle' then\n      M.toggle()\n    elseif command == 'open' then\n      M.open(silent)\n    elseif command == 'close' then\n      M.close()\n    elseif command == 'toggle-focus' then\n      M.toggle_focus()\n    elseif command == 'focus' then\n      M.focus()\n    elseif command == 'refresh' then\n      M.refresh()\n    elseif command == 'reveal' then\n      M.reveal(silent)\n    end\n  end, { nargs = '*' })\nend\n\nfunction M.get_inst()\n  if M.inst == nil then M.inst = require('blink.tree.window').new() end\n  return M.inst\nend\n\nfunction M.toggle() M.get_inst():toggle() end\nfunction M.open(silent) M.get_inst():open(silent) end\nfunction M.close() M.get_inst():close() end\nfunction M.toggle_focus() M.get_inst():toggle_focus() end\nfunction M.focus() M.get_inst():focus() end\nfunction M.refresh() M.get_inst():refresh() end\nfunction M.reveal(silent) M.get_inst():reveal(silent) end\n\nfunction M.setup_highlights()\n  api.nvim_set_hl(0, 'BlinkTreeNormal', { link = 'Normal', default = true })\n  api.nvim_set_hl(0, 'BlinkTreeNormalNC', { link = 'NormalNC', default = true })\n  api.nvim_set_hl(0, 'BlinkTreeSignColumn', { link = 'SignColumn', default = true })\n  api.nvim_set_hl(0, 'BlinkTreeCursorLine', { link = 'CursorLine', default = true })\n  api.nvim_set_hl(0, 'BlinkTreeFloatBorder', { link = 'FloatBorder', default = true })\n  api.nvim_set_hl(0, 'BlinkTreeStatusLine', { link = 'StatusLine', default = true })\n  api.nvim_set_hl(0, 'BlinkTreeStatusLineNC', { link = 'StatusLineNC', default = true })\n  api.nvim_set_hl(0, 'BlinkTreeVertSplit', { link = 'VertSplit', default = true })\nend\n\nreturn M\n"
  },
  {
    "path": "lua/blink/tree/lib/fs.lua",
    "content": "-- todo: manage number of open files to ensure we don't go over limit\n-- likely via a queue of some sort\n\nlocal sep = '/'\nlocal uv = vim.uv\nlocal config = {\n  hide_dotfiles = true,\n  hide = { ['.cache'] = true },\n  never_show = { ['.git'] = true, ['node_modules'] = true },\n}\n\nlocal FS = {}\n\nfunction FS.create_file(root_dir, path)\n  -- Split the path into parts\n  local parts = {}\n  for part in string.gmatch(path, '([^/]+)') do\n    table.insert(parts, part)\n  end\n\n  -- Ensure no invalid parts\n  for _, part in ipairs(parts) do\n    if part == '.' or part == '..' or part == '' then error('Invalid path: contains \".\" or \"..\" or \"\"') end\n  end\n\n  -- Construct the full path\n  local full_path = root_dir\n  for i, part in ipairs(parts) do\n    full_path = full_path .. sep .. part\n    local part_type = i == #parts and not vim.endswith(path, sep) and 'file' or 'directory'\n    local stat = uv.fs_stat(full_path)\n\n    local exists = stat ~= nil and stat.type == part_type\n    if exists then\n      if part_type == 'file' then error('File already exists: ' .. full_path) end\n      goto continue\n    end\n\n    -- Create the file\n    if part_type == 'file' then\n      local fd = uv.fs_open(full_path, 'w', 438) -- 438 is 0666 in octal\n      if fd then\n        uv.fs_close(fd)\n      else\n        error('Failed to create file: ' .. full_path)\n      end\n    -- Create the directory\n    else\n      local success, err = pcall(uv.fs_mkdir, full_path, 493) -- 493 is 0755 in octal\n      if not success then error('Failed to create directory: ' .. full_path .. ' (' .. err .. ')') end\n    end\n\n    ::continue::\n  end\n\n  return full_path\nend\n\nfunction FS.rename(old_path, new_path)\n  local success, err = pcall(vim.lsp.util.rename, old_path, new_path)\n  if not success then error('Failed to rename: ' .. old_path .. ' -> ' .. new_path .. ' (' .. err .. ')') end\nend\n\nfunction FS.copy_file(old_path, new_path)\n  local success, err = pcall(uv.fs_copyfile, old_path, new_path)\n  if not success then error('Failed to copy: ' .. old_path .. ' -> ' .. new_path .. ' (' .. err .. ')') end\nend\n\nfunction FS.read_file(path)\n  local fd = uv.fs_open(path, 'r', 438) -- 438 is 0666 in octal\n  if not fd then error('Failed to open file: ' .. path) end\n\n  local data = uv.fs_read(fd, uv.fs_stat(path).size, 0)\n  uv.fs_close(fd)\n\n  return data\nend\n\nfunction FS.read_file_async(path, callback)\n  uv.fs_open(path, 'r', 438, function(open_err, fd)\n    if open_err or fd == nil then return callback(open_err) end\n\n    local callback_and_close = function(err, data)\n      uv.fs_close(fd, function() end)\n      callback(err, data)\n    end\n\n    uv.fs_stat(path, function(stat_err, stat)\n      if stat_err or stat == nil then return callback_and_close(stat_err) end\n      uv.fs_read(fd, stat.size, 0, callback_and_close)\n    end)\n  end)\nend\n\nfunction FS.scan_dir_async(path, callback)\n  local max_entries = 200\n  -- open directory, return early if failed\n  uv.fs_opendir(path, function(err, handle)\n    if err ~= nil or handle == nil then\n      -- print('Error opening directory: ' .. parent.path .. ' ' .. (err or 'nil'))\n      callback({})\n      return\n    end\n\n    local all_entries = {}\n\n    local function read_dir()\n      uv.fs_readdir(handle, function(err, entries)\n        if err ~= nil or entries == nil then\n          -- print('Error reading directory: ' .. parent.path .. ' ' .. (err or 'nil'))\n          callback({})\n          return\n        end\n\n        vim.list_extend(all_entries, entries)\n        if #entries == max_entries then\n          read_dir()\n        else\n          callback(all_entries)\n        end\n      end)\n    end\n    read_dir()\n  end, max_entries)\nend\n\nfunction FS.watch_dir(path, callback)\n  local handle = uv.new_fs_event()\n  if handle == nil then error('Failed to create fs event handle') end\n\n  FS.scan_dir_async(path, function(entries)\n    callback(entries)\n\n    -- begin watching\n    handle:start(path, {}, function() FS.scan_dir_async(path, callback) end)\n  end)\n\n  -- unsubscribe\n  return function() handle:stop() end\nend\n\nfunction FS.path_starts_with(path, prefix)\n  path = path[#path] == sep and path or path .. sep\n  prefix = prefix[#prefix] == sep and prefix or prefix .. sep\n  return vim.startswith(path, prefix)\nend\n\nreturn FS\n"
  },
  {
    "path": "lua/blink/tree/lib/tree.lua",
    "content": "local allConfig = require('blink.tree.config')\nlocal config = {\n  hide_dotfiles = allConfig.hide_dotfiles,\n  hide = allConfig.hide,\n  never_show = allConfig.never_show,\n}\n\nlocal Tree = {}\n\nfunction Tree.make_root(path)\n  -- replace home with ~\n  local filename = path\n  local home = vim.env.HOME\n  if home ~= nil and vim.startswith(path, home) then filename = '~' .. string.sub(path, string.len(home) + 1) end\n\n  local node = Tree.make_node(nil, path, filename, true)\n  node.expanded = true\n  return node\nend\n\nfunction Tree.make_node(parent, path, filename, is_dir)\n  local node = {\n    parent = parent,\n    children = {},\n\n    path = path,\n    filename = filename,\n    is_dir = is_dir,\n    expanded = false,\n\n    flags = {\n      cut = false,\n      copy = false,\n    },\n  }\n\n  return node\nend\n\nfunction Tree.destroy_node(node)\n  if node.git_repo then node.git_repo.destroy() end\n\n  if node.children then\n    for _, child in ipairs(node.children) do\n      Tree.destroy_node(child)\n    end\n  end\nend\n\nfunction Tree.new(filename, is_dir, expanded)\n  return {\n    filename = filename,\n    is_dir = is_dir,\n    expanded = expanded,\n    children = {},\n  }\nend\n\n-- loops through the nodes and treats the second argument as the source\n-- of truth, adding and removing nodes as needed, but using the object reference\n-- from the first list whenever possible\nfunction Tree.merge_nodes(old_nodes, nodes)\n  local old_node_count = #old_nodes\n  local changed = false\n\n  local merged_nodes = {}\n\n  -- FIXME: probably breaks if theres two files like FILE.md and file.md\n  local old_node_idx = 1\n  for _, node in ipairs(nodes) do\n    while old_node_idx <= old_node_count do\n      local old_node = old_nodes[old_node_idx]\n\n      if not old_node.is_dir and node.is_dir then goto continue end\n      if old_node.is_dir == node.is_dir and string.lower(old_node.filename) >= string.lower(node.filename) then\n        goto continue\n      end\n\n      old_node_idx = old_node_idx + 1\n      changed = true\n    end\n    ::continue::\n\n    if old_node_idx <= old_node_count and old_nodes[old_node_idx].filename == node.filename then\n      table.insert(merged_nodes, old_nodes[old_node_idx])\n      old_node_idx = old_node_idx + 1\n      Tree.destroy_node(node) -- didn't include new node so we destroy it\n    else\n      table.insert(merged_nodes, node)\n      changed = true\n    end\n  end\n\n  -- didn't include all the previous nodes\n  if old_node_idx < old_node_count then changed = true end\n\n  -- destroy old nodes that weren't included\n  -- TODO: probably slow\n  for i = old_node_idx, old_node_count do\n    local old_node = old_nodes[i]\n    if not vim.tbl_contains(merged_nodes, old_node) then Tree.destroy_node(old_node) end\n  end\n\n  return merged_nodes, changed\nend\n\nfunction Tree.make_children(parent, entries)\n  local children = {}\n\n  -- scan directory and build nodes\n  for _, entry in ipairs(entries) do\n    local path = parent.path .. '/' .. entry.name\n    local node = Tree.make_node(parent, path, entry.name, entry.type == 'directory')\n    -- TODO: move to renderer, differentiate between hidden and never_show\n    local should_show = (not vim.tbl_contains(config.hide, node.filename))\n      and (not vim.tbl_contains(config.never_show, node.filename))\n    if should_show then table.insert(children, node) end\n  end\n\n  -- sort by name (insensitive) and then by type (dir first)\n  table.sort(children, function(a, b)\n    if a.is_dir == b.is_dir then return string.lower(a.filename) < string.lower(b.filename) end\n    return a.is_dir\n  end)\n\n  return children\nend\n\nfunction Tree.build_tree(parent, on_initial, on_change)\n  local fs = require('blink.tree.lib.fs')\n  local git = require('blink.tree.git')\n  if not parent.is_dir or not parent.expanded or parent.watch_unsubscribe then\n    on_initial(parent, false)\n    return\n  end\n\n  local is_initial = true\n  parent.watch_unsubscribe = fs.watch_dir(parent.path, function(entries)\n    -- check if this is a git repo\n    if not parent.git_repo then\n      for _, entry in ipairs(entries) do\n        if entry.type == 'directory' and entry.name == '.git' then\n          parent.git_repo = git.new(parent.path, on_change)\n          break\n        end\n      end\n    end\n\n    local cb = is_initial and on_initial or on_change\n    is_initial = false\n\n    -- update the children\n    local children, changed = Tree.merge_nodes(parent.children, Tree.make_children(parent, entries))\n    parent.children = children\n\n    -- scan child directories and hook up the on_change cb\n    local pending_scans = 0\n    for _, child in ipairs(children) do\n      -- scan child directories\n      if child.is_dir and child.expanded then\n        pending_scans = pending_scans + 1\n\n        Tree.build_tree(child, function(_, child_changed)\n          changed = changed or child_changed\n          pending_scans = pending_scans - 1\n          if pending_scans == 0 then cb(parent, changed) end\n        end, on_change)\n      end\n    end\n\n    -- no child directories\n    if pending_scans == 0 then cb(parent, changed) end\n  end)\nend\n\nfunction Tree.clear_watch(node)\n  Tree.traverse(node, function()\n    if node.watch_unsubscribe then\n      node.watch_unsubscribe()\n      node.watch_unsubscribe = nil\n    end\n  end)\nend\n\nfunction Tree.get_repo(node)\n  local repo = node.git_repo\n  local parent = node.parent\n  while parent ~= nil and repo == nil do\n    repo = parent.git_repo\n    parent = parent.parent\n  end\n  return repo\nend\n\nfunction Tree.traverse(node, cb)\n  cb(node)\n  for _, child in ipairs(node.children) do\n    Tree.traverse(child, cb)\n  end\nend\n\nreturn Tree\n"
  },
  {
    "path": "lua/blink/tree/lib/utils.lua",
    "content": "local api = vim.api\nlocal Utils = {}\n\nfunction Utils.pick_or_create_non_special_window()\n  local wins = api.nvim_list_wins()\n  table.insert(wins, 1, api.nvim_get_current_win())\n\n  local ignore_list = { 'blink-tree', 'Trouble', 'qf', 'edgy' }\n  local ignore = {}\n  for _, ft in ipairs(ignore_list) do\n    ignore[ft] = true\n  end\n\n  -- pick the first non-special window\n  for _, win in ipairs(wins) do\n    local is_relative = api.nvim_win_get_config(win).relative ~= ''\n    local buf = api.nvim_win_get_buf(win)\n    local options = vim.bo[buf]\n    if not is_relative and not ignore[options.filetype] and not ignore[options.buftype] then return win end\n  end\n\n  -- create a new window if all are special\n  return api.nvim_open_win(0, false, {\n    vertical = true,\n    split = 'right',\n  })\nend\n\n--- Debounces a function on the trailing edge. Automatically\n--- `schedule_wrap()`s.\n---\n--- @param fn (function) Function to debounce\n--- @param timeout (number) Timeout in ms\n--- @returns (function, timer) Debounced function and timer. Remember to call\n--- `timer:close()` at the end or you will leak memory!\nfunction Utils.debounce(fn, timeout)\n  local timer = vim.loop.new_timer()\n  local wrapped_fn\n\n  function wrapped_fn(...)\n    local argv = { ... }\n    local argc = select('#', ...)\n\n    timer:start(timeout, 0, function() pcall(vim.schedule_wrap(fn), unpack(argv, 1, argc)) end)\n  end\n  return wrapped_fn, timer\nend\n\nreturn Utils\n"
  },
  {
    "path": "lua/blink/tree/lib/uv.lua",
    "content": "local uv = vim.loop\nlocal UV = {}\n\nfunction UV.exec_async(opts, callback)\n  callback = callback or function() end\n  local stdout = uv.new_pipe()\n  uv.spawn(\n    opts.command[1],\n    { args = vim.list_slice(opts.command, 2), cwd = opts.cwd, stdio = { nil, stdout, nil } },\n    function(code)\n      if code ~= 0 then\n        -- TODO: log something?\n        return callback(code, '')\n      end\n      local buffer = ''\n      uv.read_start(stdout, function(err, data)\n        assert(not err, err)\n        if data then\n          buffer = buffer .. data\n          return\n        end\n\n        uv.read_stop(stdout)\n        stdout:close()\n        callback(code, buffer)\n      end)\n    end\n  )\nend\n\nreturn UV\n"
  },
  {
    "path": "lua/blink/tree/popup.lua",
    "content": "local api = vim.api\n\nlocal Popup = {}\n\n-- function Popup.new()\n--   self.winnr = nil\n--   self.bufnr = nil\n--   return self\n-- end\n--\nfunction Popup:open(opts)\n  if self.winnr ~= nil then return end\n\n  opts = opts or {}\n  opts.relative = opts.relative or 'cursor'\n  opts.width = opts.width or 40\n  opts.height = opts.height or 20\n  opts.row = opts.row or 1\n  opts.col = opts.col or 1\n  opts.style = opts.style or 'minimal'\n  opts.border = opts.border or 'single'\n\n  local initial_text = opts.initial_text or ''\n  opts.initial_text = nil\n\n  self.bufnr = api.nvim_create_buf(false, true)\n  api.nvim_buf_set_lines(self.bufnr, 0, -1, false, { initial_text })\n\n  self.winnr = api.nvim_open_win(self.bufnr, true, opts)\n  api.nvim_win_set_cursor(self.winnr, { 1, #initial_text }) -- move cursor to the end\nend\n\nfunction Popup.new_input(opts, callback)\n  local self = setmetatable({}, { __index = Popup })\n\n  opts = opts or {}\n  opts.width = opts.width or 40\n  opts.height = opts.height or 1\n  self:open(opts)\n\n  -- enter insert mode\n  api.nvim_feedkeys('a', 'n', true)\n\n  local has_run = false\n  local on_submit = function()\n    if has_run then return end\n    has_run = true\n\n    local line = api.nvim_get_current_line()\n    self:close()\n    callback(line)\n  end\n  local on_abort = function()\n    if has_run then return end\n    has_run = true\n\n    self:close()\n    callback(nil)\n  end\n\n  -- listen for close events\n  api.nvim_buf_set_keymap(self.bufnr, 'i', '<CR>', '', {\n    callback = on_submit,\n  })\n  api.nvim_buf_set_keymap(self.bufnr, 'i', '<Esc>', '', {\n    callback = on_abort,\n  })\n  api.nvim_buf_set_keymap(self.bufnr, 'i', '<C-c>', '', {\n    callback = on_abort,\n  })\n  api.nvim_create_autocmd('WinLeave', {\n    callback = on_abort,\n    once = true,\n  })\nend\n\nfunction Popup:close()\n  if api.nvim_win_is_valid(self.winnr) then\n    vim.api.nvim_win_close(self.winnr, true)\n    self.winnr = nil\n  end\n  if api.nvim_buf_is_valid(self.bufnr) then\n    vim.api.nvim_buf_delete(self.bufnr, { force = true })\n    self.bufnr = nil\n  end\nend\n\nreturn Popup\n"
  },
  {
    "path": "lua/blink/tree/renderer.lua",
    "content": "local api = vim.api\nlocal lib_tree = require('blink.tree.lib.tree')\n\nlocal ns = api.nvim_create_namespace('blink_tree')\n\nlocal Renderer = {}\n\nfunction Renderer:render_node(line_number, node, indent)\n  local prefix = ' ' .. string.rep('  ', indent)\n  local icon, highlight = self:get_icon(node)\n  local name = node.filename\n\n  -- keep track of which node is associated with which line\n  local nodes_by_lines = {}\n  nodes_by_lines[line_number] = node\n\n  local icon_spaces = '   '\n  local line = prefix .. icon_spaces .. name\n\n  local line_index = line_number - 1\n  api.nvim_buf_set_lines(self.bufnr, line_index, line_index + 1, false, { line })\n\n  -- update render state for decorations\n  -- todo: calculate this on demand instead of storing it\n  node.render_state = {\n    length = #line,\n    indent = indent,\n    icon = { hl = highlight, idx = #prefix, icon = icon },\n  }\n\n  if node.expanded then\n    for _, child in ipairs(node.children) do\n      local child_nodes_by_lines, new_line_number = self:render_node(line_number + 1, child, indent + 1)\n      line_number = new_line_number\n      for child_line_number, child_node in pairs(child_nodes_by_lines) do\n        nodes_by_lines[child_line_number] = child_node\n      end\n    end\n  end\n\n  return nodes_by_lines, line_number\nend\n\nfunction Renderer.new(bufnr)\n  local self = setmetatable({}, { __index = Renderer })\n  self.nodes_by_lines = nil\n  self.bufnr = bufnr\n\n  local modified = {}\n\n  -- Draws the indent and icon\n  api.nvim_set_decoration_provider(ns, {\n    on_win = function(_, curr_winid, currr_bufnr)\n      local should_render = curr_winid == self.winnr and currr_bufnr == self.bufnr\n      if not should_render then return false end\n\n      modified = self.get_modified_buffers()\n\n      return curr_winid == self.winnr and currr_bufnr == self.bufnr\n    end,\n    on_line = function(_, _, _, line_number)\n      if self.nodes_by_lines == nil then return end\n\n      local node = self.nodes_by_lines[line_number + 1]\n      local next_node = self.nodes_by_lines[line_number + 2]\n      if node == nil then return end\n\n      local render_state = node.render_state\n      if render_state == nil then return end\n\n      -- Indents and Icon\n      local indent = render_state.indent\n      local indent_str = ''\n      while indent > 0 do\n        if indent == render_state.indent then\n          indent_str = indent_str .. '  '\n        elseif (next_node == nil or next_node.render_state.indent < render_state.indent) and indent == 1 then\n          indent_str = indent_str .. '└ '\n        else\n          indent_str = indent_str .. '│ '\n        end\n        indent = indent - 1\n      end\n\n      api.nvim_buf_set_extmark(bufnr, ns, line_number, render_state.icon.idx, {\n        virt_text = { { indent_str, 'BlinkTreeIndent' }, { render_state.icon.icon, render_state.icon.hl } },\n        virt_text_win_col = 1,\n        virt_text_pos = 'overlay',\n        hl_mode = 'combine',\n        ephemeral = true,\n      })\n\n      -- Buffer modified\n      if modified[node.path] then\n        api.nvim_buf_set_extmark(bufnr, ns, line_number, 2, {\n          virt_text = { { ' ● ', 'BlinkTreeModified' } },\n          virt_text_pos = 'right_align',\n          hl_mode = 'combine',\n          priority = 1,\n          ephemeral = true,\n        })\n      end\n\n      -- Git Status\n      local repo = lib_tree.get_repo(node)\n      if repo ~= nil and line_number > 0 then\n        local status = repo:get_status(node.path)\n        if status ~= nil then\n          local hl = repo.get_hl_for_status(status, node.is_dir)\n          if hl ~= nil then\n            api.nvim_buf_set_extmark(bufnr, ns, line_number, 0, {\n              end_col = render_state.length,\n              hl_group = hl,\n              hl_eol = true,\n              ephemeral = true,\n            })\n          end\n        end\n      end\n\n      -- Cut / Copy\n      if node.flags.cut then\n        api.nvim_buf_set_extmark(bufnr, ns, line_number, 0, {\n          virt_text = { { '[cut]', 'BlinkTreeFlagCut' } },\n          hl_mode = 'combine',\n          priority = 1,\n          ephemeral = true,\n        })\n      elseif node.flags.copy then\n        api.nvim_buf_set_extmark(bufnr, ns, line_number, 0, {\n          virt_text = { { '[copy]', 'BlinkTreeFlagCopy' } },\n          hl_mode = 'combine',\n          priority = 1,\n          ephemeral = true,\n        })\n      end\n    end,\n  })\n\n  return self\nend\n\nfunction Renderer.get_modified_buffers()\n  local modified = {}\n  for _, bufnr in ipairs(vim.api.nvim_list_bufs()) do\n    if vim.api.nvim_buf_is_loaded(bufnr) and vim.api.nvim_buf_get_option(bufnr, 'modified') then\n      local path = vim.api.nvim_buf_get_name(bufnr)\n      modified[path] = true\n    end\n  end\n  return modified\nend\n\nfunction Renderer:render_window(winnr, root)\n  self.winnr = winnr\n\n  api.nvim_set_option_value('modifiable', true, { buf = self.bufnr })\n\n  -- render\n  local nodes_by_lines, last_line_number = self:render_node(1, root, 0)\n  self.nodes_by_lines = nodes_by_lines\n\n  -- clear any extra lines from the last render\n  api.nvim_buf_set_lines(self.bufnr, last_line_number, api.nvim_buf_line_count(self.bufnr), false, {})\n\n  api.nvim_set_option_value('modifiable', false, { buf = self.bufnr })\n\n  return nodes_by_lines\nend\n\n-- redraw the entire window with the decoration provider\nfunction Renderer:redraw() api.nvim__redraw({ win = self.winnr, valid = false }) end\n\nfunction Renderer:get_icon(node)\n  if node.is_dir then\n    return node.expanded and '' or '', 'Blue'\n  else\n    local devicons = require('nvim-web-devicons')\n    local icon, color = devicons.get_icon(node.filename, vim.fn.fnamemodify(node.path, ':e'), { default = true })\n    return icon, color\n  end\nend\n\nfunction Renderer:get_hovered_node()\n  if self.nodes_by_lines == nil then return end\n\n  local cursor = api.nvim_win_get_cursor(self.winnr)\n  return self.nodes_by_lines[cursor[1]]\nend\n\nfunction Renderer:select_node(node)\n  if self.nodes_by_lines == nil then return end\n\n  for line_number, n in pairs(self.nodes_by_lines) do\n    if n == node then\n      api.nvim_win_set_cursor(self.winnr, { line_number, 0 })\n      return\n    end\n  end\nend\n\nfunction Renderer:select_path(path)\n  if self.nodes_by_lines == nil then return end\n\n  for line_number, n in pairs(self.nodes_by_lines) do\n    if n.path == path then\n      api.nvim_win_set_cursor(self.winnr, { line_number, 0 })\n      return\n    end\n  end\nend\n\nreturn Renderer\n"
  },
  {
    "path": "lua/blink/tree/tree.lua",
    "content": "local fs = require('blink.tree.lib.fs')\nlocal lib_tree = require('blink.tree.lib.tree')\nlocal Tree = {}\n\nfunction Tree.new(path, on_changed)\n  local self = setmetatable({}, { __index = Tree })\n  self.path = path\n  self.root = lib_tree.make_root(path)\n  self.on_changed = on_changed\n\n  -- immediately build\n  lib_tree.build_tree(self.root, function(tree)\n    self.root = tree\n    self.on_changed()\n  end, function() self.on_changed() end)\n\n  return self\nend\n\n--------------------\n--- Public API\n\nfunction Tree:collapse(node)\n  if not node.expanded then return end\n\n  node.expanded = false\n  lib_tree.clear_watch(node)\n\n  self.on_changed()\nend\n\nfunction Tree:expand(node, callback)\n  if node.expanded then return callback() end\n\n  node.expanded = true\n  local on_initial = function() self.on_changed(callback) end\n  local on_change = function() self.on_changed() end\n\n  lib_tree.build_tree(node, on_initial, on_change)\nend\n\nfunction Tree:expand_path(path, callback)\n  callback = callback or function() end\n  if not fs.path_starts_with(path, self.root.path) then return callback('Path is not contained in tree', nil) end\n\n  local function expand_and_recurse(node, cb)\n    for _, child in ipairs(node.children) do\n      if fs.path_starts_with(path, child.path) then\n        local continue = function()\n          -- final child\n          if child.path == path then return cb(nil, child) end\n          -- or recurse\n          expand_and_recurse(child, cb)\n        end\n\n        -- already expanded, continue\n        if child.expanded then return continue() end\n        -- otherwise, expand the node but don't render\n        child.expanded = true\n        return lib_tree.build_tree(child, continue, function() self.on_changed() end)\n      end\n    end\n\n    return cb('Path not found', nil)\n  end\n\n  expand_and_recurse(self.root, function(err, node)\n    self.on_changed(function() callback(err, node) end)\n  end)\nend\n\nfunction Tree:find_node_by_path(path, parent)\n  parent = parent or self.root\n  if parent.path == path then return parent end\n\n  for _, child in ipairs(parent.children) do\n    if fs.path_starts_with(path, child.path) then return self:find_node_by_path(path, child) end\n  end\n\n  return nil\nend\n\nfunction Tree:destroy()\n  lib_tree.traverse(self.root, function(node)\n    if node.watch_unsubscribe then node.watch_unsubscribe() end\n    if node.git_repo then node.git_repo:destroy() end\n  end)\nend\n\nreturn Tree\n"
  },
  {
    "path": "lua/blink/tree/window.lua",
    "content": "local api = vim.api\n\nlocal Window = {}\n\nfunction Window.new()\n  local self = setmetatable({}, { __index = Window })\n  self.winnr = -1\n  self.bufnr = -1\n  self.tree = require('blink.tree.tree').new(vim.fn.getcwd(), function(callback) self:render(callback) end)\n\n  self.augroup = api.nvim_create_augroup('BlinkTreeWindow', { clear = true })\n\n  api.nvim_create_autocmd('VimLeavePre', {\n    group = self.augroup,\n    callback = function()\n      if self.tree ~= nil then\n        self.tree:destroy()\n        self.tree = nil\n      end\n    end,\n  })\n\n  api.nvim_create_autocmd('WinEnter', {\n    group = self.augroup,\n    callback = function()\n      local current_win = api.nvim_get_current_win()\n      if current_win == self.winnr then\n        api.nvim_feedkeys(api.nvim_replace_termcodes('<Esc>', true, false, true), 'n', false)\n      end\n    end,\n  })\n  -- only allow the cursor to be on the first column which will always be empty\n  -- avoiding issues with cursorword plugins\n  api.nvim_create_autocmd('CursorMoved', {\n    group = self.augroup,\n    callback = function()\n      if self.winnr == api.nvim_get_current_win() then\n        local cursor = api.nvim_win_get_cursor(self.winnr)\n        api.nvim_win_set_cursor(self.winnr, { cursor[1], 0 })\n      end\n    end,\n  })\n\n  -- recreate the tree on dir change\n  api.nvim_create_autocmd('DirChanged', {\n    group = self.augroup,\n    callback = function()\n      if not self.renderer then return end\n\n      self.tree:destroy()\n      self.tree = require('blink.tree.tree').new(vim.fn.getcwd(), function() self:render() end)\n    end,\n  })\n\n  -- set buffer options\n  api.nvim_create_autocmd('BufEnter', {\n    group = self.augroup,\n    callback = function()\n      if vim.bo.filetype ~= 'blink-tree' then return end\n\n      -- set local window options\n      vim.cmd('setlocal winfixwidth')\n      vim.cmd('setlocal cursorline')\n      vim.cmd('setlocal cursorlineopt=line')\n      vim.cmd('setlocal signcolumn=no')\n      vim.cmd('setlocal nowrap')\n      vim.cmd('setlocal nolist nospell nonumber norelativenumber')\n      vim.cmd(\n        'setlocal winhighlight=Normal:BlinkTreeNormal,NormalNC:BlinkTreeNormalNC,SignColumn:BlinkTreeSignColumn,CursorLine:BlinkTreeCursorLine,FloatBorder:BlinkTreeFloatBorder,StatusLine:BlinkTreeStatusLine,StatusLineNC:BlinkTreeStatusLineNC,VertSplit:BlinkTreeVertSplit,EndOfBuffer:BlinkTreeEndOfBuffer'\n      )\n    end,\n  })\n\n  -- hide the cursor when window is focused\n  -- todo: should use winenter and winleave instead?\n  local prev_cursor\n  local prev_blend\n  api.nvim_create_autocmd('BufEnter', {\n    group = self.augroup,\n    callback = function()\n      if vim.bo.filetype == 'blink-tree' and prev_cursor == nil then\n        prev_cursor = api.nvim_get_option_value('guicursor', {})\n        api.nvim_set_option_value('guicursor', 'n:block-Cursor', {})\n\n        local cursor_hl = api.nvim_get_hl(0, { name = 'Cursor' })\n        prev_blend = cursor_hl.blend\n        api.nvim_set_hl(0, 'Cursor', vim.tbl_extend('force', cursor_hl, { blend = 100 }))\n      end\n    end,\n  })\n  api.nvim_create_autocmd('BufLeave', {\n    group = self.augroup,\n    callback = function()\n      if prev_cursor ~= nil then\n        api.nvim_set_option_value('guicursor', prev_cursor, {})\n        prev_cursor = nil\n\n        local cursor_hl = api.nvim_get_hl(0, { name = 'Cursor' })\n        api.nvim_set_hl(0, 'Cursor', vim.tbl_extend('force', cursor_hl, { blend = prev_blend or 0 }))\n        prev_blend = nil\n      end\n    end,\n  })\n\n  -- prevent buffer from being changed\n  -- api.nvim_create_autocmd('BufEnter', {\n  --   callback = function()\n  --     -- ignore if not in tree window\n  --     if self.winnr ~= api.nvim_get_current_win() or not api.nvim_win_is_valid(self.winnr) then return end\n  --     if self.bufnr == api.nvim_get_current_buf() or not api.nvim_buf_is_valid(self.bufnr) then return end\n  --     local bufnr = api.nvim_get_current_buf()\n  --\n  --     -- restore tree buffer to tree window\n  --     api.nvim_win_set_buf(self.winnr, self.bufnr)\n  --\n  --     -- move new buffer to a non-tree window\n  --     local winnr = require('blink.tree.lib.utils').pick_or_create_non_special_window()\n  --     api.nvim_set_current_win(winnr)\n  --     api.nvim_win_set_buf(winnr, bufnr)\n  --   end,\n  -- })\n\n  return self\nend\n\nfunction Window:refresh()\n  -- todo:\nend\n\nfunction Window:ensure_buffer()\n  -- TODO: should check if buffer is valid and cleanup previous\n  if api.nvim_buf_is_valid(self.bufnr) then return end\n\n  self.bufnr = api.nvim_create_buf(false, true)\n  api.nvim_set_option_value('buftype', 'nofile', { buf = self.bufnr })\n  api.nvim_set_option_value('filetype', 'blink-tree', { buf = self.bufnr })\n  api.nvim_set_option_value('buflisted', false, { buf = self.bufnr })\n  api.nvim_set_option_value('modifiable', false, { buf = self.bufnr })\n  api.nvim_set_option_value('swapfile', false, { buf = self.bufnr })\n\n  self.renderer = require('blink.tree.renderer').new(self.bufnr)\n\n  require('blink.tree.binds').attach_to_instance(self)\nend\n\nfunction Window:render(callback)\n  vim.schedule(function()\n    if not self:is_open() then return end\n    self.nodes_by_line = self.renderer:render_window(self.winnr, self.tree.root)\n    if callback then callback() end\n  end)\nend\n\nfunction Window:open(silent, callback)\n  self:ensure_buffer()\n  if self:is_open() then\n    if callback then callback() end\n    return\n  end\n\n  self.winnr = api.nvim_open_win(self.bufnr, not silent, {\n    win = -1,\n    vertical = true,\n    split = 'left',\n    width = 40,\n  })\n\n  -- HACK: we manually trigger this because neovim-session disable autocmds\n  -- during startup\n  -- todo: only force the autocmds if autocmds are disabled\n  local prev_win = vim.api.nvim_get_current_win()\n  if silent then vim.api.nvim_set_current_win(self.winnr) end\n  vim.cmd('do BufEnter')\n  if silent then\n    vim.schedule(function()\n      vim.cmd('do BufLeave')\n      vim.api.nvim_set_current_win(prev_win)\n    end)\n  end\n\n  self:render(callback)\nend\n\nfunction Window:close()\n  if not self:is_open() then return end\n\n  -- if we're the last window, just replace the current buffer with a new buffer\n  if api.nvim_tabpage_list_wins(0)[1] == self.winnr and #api.nvim_list_wins() == 1 then return vim.cmd('enew') end\n\n  -- otherwise close the window\n  -- todo: destroy renderer?\n  api.nvim_win_close(self.winnr, true)\n  api.nvim_buf_delete(self.bufnr, { force = true })\n  self.winnr = -1\nend\n\nfunction Window:toggle()\n  if self:is_open() then\n    self:close()\n  else\n    self:open()\n  end\nend\n\nfunction Window:toggle_focus()\n  if not self:is_open() then return self:open() end\n\n  local win = api.nvim_get_current_win()\n  if win == self.winnr then\n    vim.cmd('wincmd p')\n  else\n    api.nvim_set_current_win(self.winnr)\n  end\nend\n\nfunction Window:focus()\n  if not self:is_open() then return self:open() end\n  api.nvim_set_current_win(self.winnr)\nend\n\nfunction Window:is_open()\n  return api.nvim_win_is_valid(self.winnr)\n    and api.nvim_win_get_buf(self.winnr) == self.bufnr\n    and api.nvim_buf_is_valid(self.bufnr)\nend\n\nfunction Window:reveal(silent)\n  local current_buf_path = vim.fn.expand(vim.api.nvim_buf_get_name(0))\n  if current_buf_path == '' then return end\n\n  self:open(silent, function()\n    self.tree:expand_path(current_buf_path, function() self.renderer:select_path(current_buf_path) end)\n    if not silent then self:focus() end\n  end)\nend\n\nreturn Window\n"
  },
  {
    "path": "scripts/dual_log.sh",
    "content": "# Written by echasnovski in mini.nvim\n# https://github.com/echasnovski/mini.nvim/blob/82584a42c636efd11781211da1396f4c1f5e7877/scripts/dual_log.sh\n\n# Check standalone repos result\nlocal_repos=\"$( ls -d dual/repos/*/ )\"\n\nfor repo in $local_repos; do\n  printf \"\\n\\033[1m$( basename $repo )\\033[0m\\n\"\n  cd $repo > /dev/null\n  git log origin/main..main --abbrev-commit --format=oneline\n  cd - > /dev/null\ndone\n"
  },
  {
    "path": "scripts/dual_push.sh",
    "content": "# Written by echasnovski in mini.nvim\n# https://github.com/echasnovski/mini.nvim/blob/82584a42c636efd11781211da1396f4c1f5e7877/scripts/dual_push.sh\n\n# Push standalone repos result\nlocal_repos=\"$( ls -d dual/repos/*/ )\"\n\nfor repo in $local_repos; do\n  printf \"\\n\\033[1mPushing $( basename $repo )\\033[0m\\n\"\n  cd $repo > /dev/null\n  # Push only if there is something to push (saves time)\n  if [ $( git rev-parse main ) != $( git rev-parse origin/main ) ]\n  then\n    git push origin main\n  fi\n  cd - > /dev/null\ndone\n\necho ''\n"
  },
  {
    "path": "scripts/dual_sync.sh",
    "content": "# Written by echasnovski in mini.nvim\n# https://github.com/echasnovski/mini.nvim/blob/82584a42c636efd11781211da1396f4c1f5e7877/scripts/dual_sync.sh\n\n# Perform local sync of standalone repositories\nrepos_dir=dual/repos\npatches_dir=dual/patches\n\nmkdir -p $repos_dir\nmkdir -p $patches_dir\n\nsync_module () {\n  # First argument is a string with module name. Others - extra paths to track\n  # for module.\n  module=$1\n  shift\n\n  repo=\"$( realpath $repos_dir/blink.$module )\"\n  patch=\"$( realpath $patches_dir/blink.$module.patch )\"\n\n  printf \"\\n\\033[1mblink.$module\\033[0m\\n\"\n\n  # Possibly pull repository\n  if [[ ! -d $repo ]]\n  then\n    printf \"Pulling\\n\"\n    github_repo=\"blink.$module\"\n    git clone --filter=blob:none https://github.com/saghen/$github_repo.git $repo\n  else\n    printf \"No pulling (already present)\\n\"\n  fi\n\n  # Make patch with commits from 'sync' branch to current HEAD which affect\n  # files related to the module\n  printf \"Making patch\\n\"\n  git format-patch sync..HEAD --output $patch -- \\\n    lua/blink/$module \\\n    readmes/$module \\\n    .gitignore \\\n    .stylua.toml \\\n    LICENSE \\\n    \"$@\"\n\n  # Do nothing if patch is empty\n  if [[ ! -s $patch ]]\n  then\n    rm $patch\n    printf \"Patch is empty\\n\"\n    return\n  fi\n\n  # Tweak patch:\n  # - Move 'readmes/xxx/*' to the top level. This should modify only patch\n  #   metadata, and not text (assuming it uses 'readmes/mini-xxx.md' on\n  #   purpose; as in \"use [this link](https://.../readmes/mini-xxx.md)\").\n  # - move 'scripts/xxx/*' to the top level\n  # TODO: handle relative links\n  sed -i 's|readmes/[^/]*/\\(.*\\)|\\1|g' $patch\n  sed -i 's|scripts/[^/]*/\\(.*\\)|\\1|g' $patch\n\n  # Apply patch\n  printf \"Applying patch\\n\"\n  cd $repo\n  git am $patch\n  cd - > /dev/null\n}\n\nsync_module \"chartoggle\"\nsync_module \"clue\"\nsync_module \"cmp\" Cargo.toml Cargo.lock flake.nix flake.lock build.rs\nsync_module \"indent\"\nsync_module \"select\"\nsync_module \"tree\"\n"
  },
  {
    "path": "src/job/default.rs",
    "content": "use crate::options::JobStartOptions;\nuse portable_pty::{native_pty_system, CommandBuilder, PtySize, PtySystem};\nuse std::sync::atomic::AtomicUsize;\n\nstruct Job {\n    id: usize,\n    pid: Option<u32>,\n\n    pty: Option<std::process::Child>,\n    stdin: Option<std::process::ChildStdin>,\n    stdout: Option<std::process::ChildStdout>,\n    stderr: Option<std::process::ChildStderr>,\n\n    options: JobStartOptions,\n}\n\nimpl Job {\n    fn new(id: usize, cmd: &Vec<String>, options: JobStartOptions) -> Self {}\n}\n"
  },
  {
    "path": "src/job/mod.rs",
    "content": "pub mod default;\npub mod options;\npub mod pty;\n"
  },
  {
    "path": "src/job/options.rs",
    "content": "use mlua::prelude::*;\nuse std::collections::HashMap;\n\n#[derive(Clone)]\npub struct JobStartOptions {\n    pub cwd: String,\n    pub env: HashMap<String, String>,\n    pub clear_env: bool,\n\n    pub stdin: bool,\n    pub stdout_buffered: bool,\n    pub stderr_buffered: bool,\n    pub on_stderr: Option<LuaFunction>,\n    pub on_stdout: Option<LuaFunction>,\n    pub on_exit: Option<LuaFunction>,\n\n    // Use msgpack-rpc to communicate with the job over stdio\n    // on_stdout is ignored\n    // on_stderr can still be used\n    pub rpc: bool,\n\n    // Detach the job process, it will not be killed when neovim exits\n    pub detach: bool,\n\n    // Connect the job to a new pseudo terminal, and its stream to the master file descriptor\n    // on_stdout recieves all output\n    // on_stderr is ignored\n    pub pty: bool,\n    // Width of the pty terminal\n    pub width: Option<usize>,\n    // Height of the pty terminal\n    pub height: Option<usize>,\n}\n\nimpl FromLua for JobStartOptions {\n    fn from_lua(value: LuaValue, _lua: &'_ Lua) -> LuaResult<Self> {\n        if let Some(tab) = value.as_table() {\n            let cwd: String = tab.get(\"cwd\").unwrap_or(\n                std::env::current_dir()\n                    .unwrap()\n                    .to_str()\n                    .unwrap()\n                    .to_string(),\n            );\n            let env: HashMap<String, String> = tab.get(\"env\").unwrap_or_default();\n            let clear_env: bool = tab.get(\"clear_env\").unwrap_or(false);\n\n            let stdin: bool = tab.get(\"stdin\").unwrap_or(true);\n            let stdout_buffered: bool = tab.get(\"stdout_buffered\").unwrap_or(false);\n            let stderr_buffered: bool = tab.get(\"stderr_buffered\").unwrap_or(false);\n            let on_stderr: Option<LuaFunction> = tab.get(\"on_stderr\")?;\n            let on_stdout: Option<LuaFunction> = tab.get(\"on_stdout\")?;\n            let on_exit: Option<LuaFunction> = tab.get(\"on_exit\")?;\n\n            let rpc: bool = tab.get(\"rpc\").unwrap_or(false);\n            let detach: bool = tab.get(\"detach\").unwrap_or(false);\n            let pty: bool = tab.get(\"pty\").unwrap_or(false);\n            let width: Option<usize> = tab.get(\"width\")?;\n            let height: Option<usize> = tab.get(\"height\")?;\n\n            Ok(JobStartOptions {\n                cwd,\n                env,\n                clear_env,\n\n                stdin,\n                stdout_buffered,\n                stderr_buffered,\n                on_stderr,\n                on_stdout,\n                on_exit,\n\n                rpc,\n                detach,\n                pty,\n                width,\n                height,\n            })\n        } else {\n            Err(mlua::Error::FromLuaConversionError {\n                from: \"LuaValue\",\n                to: \"JobStartOptions\".to_string(),\n                message: None,\n            })\n        }\n    }\n}\n"
  },
  {
    "path": "src/job/pty.rs",
    "content": "use crate::options::JobStartOptions;\nuse anyhow::Result;\nuse portable_pty::{Child, CommandBuilder, PtyPair, PtySize, native_pty_system};\n\nstruct JobPty {\n    id: usize,\n\n    pid: Option<u32>,\n    child: Box<dyn Child>,\n    pair: PtyPair,\n    stdin: Box<dyn std::io::Write>,\n    stdout: Box<dyn std::io::Read>,\n\n    options: JobStartOptions,\n}\n\nimpl JobPty {\n    fn new(id: usize, cmd: &Vec<String>, options: JobStartOptions) -> Result<Self> {\n        // Use the native pty implementation for the system\n        let pty_system = native_pty_system();\n\n        // Create a new pty\n        let pair = pty_system.openpty(PtySize {\n            rows: options.height.unwrap_or(24) as u16,\n            cols: options.width.unwrap_or(80) as u16,\n            // Not all systems support pixel_width, pixel_height,\n            // but it is good practice to set it to something\n            // that matches the size of the selected font.  That\n            // is more complex than can be shown here in this\n            // brief example though!\n            pixel_width: 0,\n            pixel_height: 0,\n        })?;\n\n        // Build the command\n        let mut cmd = CommandBuilder::from_argv(cmd.iter().map(|s| s.into()).collect());\n        cmd.cwd(&options.cwd);\n\n        if options.clear_env {\n            cmd.env_clear();\n        }\n        for (key, value) in options.env.iter() {\n            cmd.env(key, value);\n        }\n\n        let child = pair.slave.spawn_command(cmd)?;\n        let pid = child.process_id();\n\n        let stdin = pair.master.take_writer()?;\n        let stdout = pair.master.try_clone_reader()?;\n\n        Ok(JobPty {\n            id,\n            pid,\n            child,\n            pair,\n            stdin,\n            stdout,\n            options,\n        })\n    }\n\n    fn send(&mut self, data: &[u8]) -> Result<()> {\n        self.stdin.write_all(data)?;\n        Ok(())\n    }\n\n    fn pid(&self) -> u32 {\n        self.pid.unwrap()\n    }\n\n    fn stop(&mut self) {\n        // TODO: send SIGTERM, and then SIGKILL after timeout\n        self.child.kill().unwrap();\n    }\n\n    fn poll(&mut self) -> Result<bool> {\n        self.poll_stdout()?;\n        self.poll_exit()\n    }\n\n    fn poll_stdout(&mut self) -> Result<bool> {\n        if let Some(on_stdout) = &self.options.on_stdout {\n            if self.options.stdout_buffered {\n                let mut buf = vec![];\n                self.stdout.read_to_end(&mut buf)?;\n                on_stdout.call::<()>(buf).unwrap();\n            }\n\n            let mut buf = [0; 2048];\n            while let Ok(bytes_read) = self.stdout.read(&mut buf) {\n                if bytes_read > 0 {\n                    on_stdout.call::<()>(&buf[..bytes_read]).unwrap();\n                }\n                return Ok(true);\n            }\n        }\n        Ok(false)\n    }\n\n    fn poll_exit(&mut self) -> Result<bool> {\n        let status = self.child.try_wait()?;\n        if let Some(status) = &status {\n            if let Some(on_exit) = &self.options.on_exit {\n                on_exit.call::<()>(status.exit_code()).unwrap();\n            }\n        }\n        return Ok(status.is_some());\n    }\n}\n"
  },
  {
    "path": "src/job/trait.rs",
    "content": ""
  },
  {
    "path": "src/lib.rs",
    "content": "// mod job;\n// use crate::job::*;\n//\n// static ID_COUNTER: AtomicUsize = AtomicUsize::new(0);\n// static JOBS: LazyLock<Mutex<HashMap<usize, Job>>> = LazyLock::new(|| Mutex::new(HashMap::new()));\n//\n// fn start_job(cmd: Vec<String>, options: JobStartOptions) -> usize {\n//     let job = Job::new(cmd, options);\n//     JOBS.lock().unwrap().insert(job.id, job);\n//     job.id\n// }\n//\n// fn job_pid(id: usize) -> Option<u32> {\n//     let job = JOBS.lock().unwrap().get(&id);\n//     let job = job.ok_or(mlua::Error::RuntimeError(\"Job not found\".to_string()))?;\n//     job.pid()\n// }\n//\n// fn stop_job(id: usize) {\n//     let job = JOBS.lock().unwrap().remove(&id);\n//     if let Some(job) = job {\n//         job.stop();\n//     }\n// }\n\n// NOTE: skip_memory_check greatly improves performance\n// https://github.com/mlua-rs/mlua/issues/318\n// #[mlua::lua_module(skip_memory_check)]\n// fn blink_job_internal(lua: &Lua) -> LuaResult<LuaTable> {\n//     let exports = lua.create_table()?;\n//     exports.set(\"start\", lua.create_function(start_job)?)?;\n//     Ok(exports)\n// }\n"
  }
]