[
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\ninsert_final_newline = true\nend_of_line = lf\n\n[nvim-tree-lua.txt]\nmax_line_length = 78\n\n# keep these in sync with .luarc.json\n#   .editorconfig is used within nvim, overriding .luarc.json\n#   .luarc.json is used by style check\n[*.lua]\nindent_style = space\nmax_line_length = 140\nindent_size = 2\n\n# EmmyLuaCodeStyle specific, see\n# https://github.com/CppCXY/EmmyLuaCodeStyle/blob/master/lua.template.editorconfig\ncontinuation_indent = 2\nquote_style = double\ncall_arg_parentheses = always\nspace_before_closure_open_parenthesis = false\nalign_continuous_similar_call_args = true\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: kyazdani42\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yml",
    "content": "name: Bug report\ndescription: Report a problem with nvim-tree\nlabels: [bug]\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        Is this a question?\n        * Please start a new [Q&A discussion](https://github.com/nvim-tree/nvim-tree.lua/discussions/new) instead of raising a bug.\n\n        Before reporting:\n        * search [existing issues](https://github.com/nvim-tree/nvim-tree.lua/issues)\n        * ensure that nvim-tree is updated to the latest version\n\n        If you are experiencing performance issues, please [enable profiling](https://github.com/nvim-tree/nvim-tree.lua#performance-issues) and attach the logs.\n\n        Please note that nvim-tree team members do not have access to nor expertise with Windows. You will need to be an active participant during resolution.\n  - type: textarea\n    attributes:\n      label: \"Description\"\n      description: \"A short description of the problem you are reporting.\"\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: \"Neovim version\"\n      description: \"Output of `nvim --version`. Please see nvim-tree.lua [minimum required version](https://github.com/nvim-tree/nvim-tree.lua#notice).\"\n      placeholder: |\n        NVIM v0.6.1\n        Build type&#58 Release\n        LuaJIT 2.1.0-beta3\n      render: text\n    validations:\n      required: true\n  - type: input\n    attributes:\n      label: \"Operating system and version\"\n      placeholder: \"Linux 5.16.11-arch1-1, macOS 11.5, Windows 10\"\n    validations:\n      required: true\n  - type: input\n    attributes:\n      label: \"Windows variant\"\n      placeholder: \"WSL, PowerShell, cygwin, msys\"\n    validations:\n      required: false\n  - type: input\n    attributes:\n      label: \"nvim-tree version\"\n      description: \"`cd <your-package-directory>/nvim-tree.lua ; git log --format='%h' -n 1`\"\n      placeholder: |\n        nvim-tree branch, commit or tag number\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: \"Clean room replication\"\n      description: \"Minimal(!) configuration necessary to reproduce the issue.\n\n        If not provided it is very unlikely that the nvim-tree team will be able to address your issue.\n\n        See [wiki: Clean Room Replication](https://github.com/nvim-tree/nvim-tree.lua/wiki/Troubleshooting#clean-room-replication) for instructions and paste the contents of your `/tmp/nvt-min.lua` here.\n\n        Please do NOT post a configuration that uses other plugin managers such as lazy, see [wiki: Lazy Loading](https://github.com/nvim-tree/nvim-tree.lua/wiki/Installation#lazy-loading)\"\n      render: lua\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: \"Steps to reproduce\"\n      description: \"Steps to reproduce using the minimal config provided below.\"\n      placeholder: |\n        1. nvim -nu /tmp/nvt-min.lua\n        2. :NvimTreeOpen\n        3. ...\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: \"Expected behavior\"\n      description: \"A description of the behavior you expected:\"\n  - type: textarea\n    attributes:\n      label: \"Actual behavior\"\n      description: \"Observed behavior (may optionally include images, videos or a screencast).\"\n \n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: feature request\nassignees: ''\n\n---\n**Is this a question?**\nPlease start a new [Q&A discussion](https://github.com/nvim-tree/nvim-tree.lua/discussions/new) instead of raising a feature request.\n\n**Can this functionality be implemented utilising API?**\nnvim-tree exposes extensive API (see `:h nvim-tree-api`). Can it be used to achieve your goal? Is there a missing API that would make it possible?\nGiven stable status of nvim-tree it's preferred to add new API than new functionality.\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/nvt-min.lua",
    "content": "vim.g.loaded_netrw = 1\nvim.g.loaded_netrwPlugin = 1\n\nvim.cmd([[set runtimepath=$VIMRUNTIME]])\nvim.cmd([[set packpath=/tmp/nvt-min/site]])\nlocal package_root = \"/tmp/nvt-min/site/pack\"\nlocal install_path = package_root .. \"/packer/start/packer.nvim\"\nlocal function load_plugins()\n  require(\"packer\").startup({\n    {\n      \"wbthomason/packer.nvim\",\n      \"nvim-tree/nvim-tree.lua\",\n      \"nvim-tree/nvim-web-devicons\",\n      -- ADD PLUGINS THAT ARE _NECESSARY_ FOR REPRODUCING THE ISSUE\n    },\n    config = {\n      package_root = package_root,\n      compile_path = install_path .. \"/plugin/packer_compiled.lua\",\n      display = { non_interactive = true },\n    },\n  })\nend\nif vim.fn.isdirectory(install_path) == 0 then\n  print(\"Installing nvim-tree and dependencies.\")\n  vim.fn.system({ \"git\", \"clone\", \"--depth=1\", \"https://github.com/wbthomason/packer.nvim\", install_path })\nend\nload_plugins()\nrequire(\"packer\").sync()\nvim.cmd([[autocmd User PackerComplete ++once echo \"Ready!\" | lua setup()]])\nvim.opt.termguicolors = true\nvim.opt.cursorline = true\n\n-- MODIFY NVIM-TREE SETTINGS THAT ARE _NECESSARY_ FOR REPRODUCING THE ISSUE\n_G.setup = function()\n  require(\"nvim-tree\").setup({})\nend\n\n-- UNCOMMENT this block for diagnostics issues, substituting pattern and cmd as appropriate.\n-- Requires diagnostics.enable = true in setup.\n--[[\nvim.api.nvim_create_autocmd(\"FileType\", {\n  pattern = \"lua\",\n  callback = function()\n    vim.lsp.start {\n      name = \"my-luals\",\n      cmd = { \"lua-language-server\" },\n      root_dir = vim.loop.cwd(),\n    }\n  end,\n})\n]]\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"daily\"\n    reviewers:\n      - \"gegoune\"\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: CI\n\non:\n  pull_request:\n  push:\n    branches: [master]\n  workflow_dispatch:\n\npermissions:\n  contents: read\n\njobs:\n  lint:\n    runs-on: ubuntu-latest\n\n    concurrency:\n      group: ${{ github.workflow }}-${{ matrix.lua_version }}-${{ github.head_ref || github.ref_name }}\n      cancel-in-progress: true\n\n    strategy:\n      matrix:\n        lua_version: [ 5.1 ]\n        luacheck_version: [ 1.2.0 ]\n\n    steps:\n      - name: checkout\n        uses: actions/checkout@v6\n\n      - name: install lua ${{ matrix.lua_version }}\n        uses: leafo/gh-actions-lua@v12\n        with:\n          luaVersion: ${{ matrix.lua_version }}\n\n      - name: install luarocks\n        uses: leafo/gh-actions-luarocks@v6\n\n      - name: install luacheck ${{ matrix.luacheck_version }}\n        run: luarocks install luacheck ${{ matrix.luacheck_version }}\n\n      - run: make lint\n\n  check:\n    runs-on: ubuntu-latest\n\n    concurrency:\n      group: ${{ github.workflow }}-${{ matrix.nvim_version }}-${{ matrix.luals_version }}-${{ matrix.emmyluacodestyle_version }}-${{ github.head_ref || github.ref_name }}\n      cancel-in-progress: true\n\n    strategy:\n      matrix:\n        nvim_version: [ stable, nightly ]\n        luals_version: [ 3.15.0 ]\n        emmyluacodestyle_version: [ 1.6.0 ]\n\n    env:\n      VIMRUNTIME: /home/runner/nvim-${{ matrix.nvim_version }}/share/nvim/runtime\n      DIR_NVIM_SRC: /home/runner/src/neovim-${{ matrix.nvim_version }}\n\n    steps:\n      - name: checkout\n        uses: actions/checkout@v6\n\n      - name: install nvim ${{ matrix.nvim_version }}\n        uses: rhysd/action-setup-vim@v1\n        with:\n          neovim: true\n          version: ${{ matrix.nvim_version }}\n\n      - name: install lua-language-server ${{ matrix.luals_version }}\n        run: |\n          mkdir -p luals\n          curl -L \"https://github.com/LuaLS/lua-language-server/releases/download/${{ matrix.luals_version }}/lua-language-server-${{ matrix.luals_version }}-linux-x64.tar.gz\" | tar zx --directory luals\n          echo \"luals/bin\" >> \"$GITHUB_PATH\"\n\n      - name: install EmmyLuaCodeStyle ${{ matrix.emmyluacodestyle_version }}\n        run: |\n          mkdir -p EmmyLuaCodeStyle\n          curl -L \"https://github.com/CppCXY/EmmyLuaCodeStyle/releases/download/${{ matrix.emmyluacodestyle_version }}/linux-x64.tar.gz\" | tar zx --directory EmmyLuaCodeStyle\n          echo \"EmmyLuaCodeStyle/linux-x64/bin\" >> \"$GITHUB_PATH\"\n\n      - run: make check\n\n      - run: make style\n\n      - run: make style-doc\n\n      - run: make format-check\n\n      - name: build Nvim from source\n        run: |\n          mkdir -p \"${DIR_NVIM_SRC}\"\n          curl -L \"https://github.com/neovim/neovim/archive/refs/tags/${{ matrix.nvim_version }}.tar.gz\" | tar zx --directory \"${DIR_NVIM_SRC}/..\"\n          cd \"${DIR_NVIM_SRC}\"\n          make doc\n          make lintdoc\n\n      - run: make help-check\n"
  },
  {
    "path": ".github/workflows/luarocks-release.yml",
    "content": "name: Luarocks Release\n\non:\n  push:\n    tags:\n      - v*\n  workflow_dispatch:\n\njobs:\n  luarocks-upload:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n      - name: LuaRocks Upload\n        uses: nvim-neorocks/luarocks-tag-release@v7\n        env:\n          LUAROCKS_API_KEY: ${{ secrets.LUAROCKS_API_KEY }}\n        with:\n          summary: A File Explorer For Neovim\n          detailed_description: |\n            Automatic updates\n\n            File type icons\n\n            Git integration\n\n            Diagnostics integration - LSP and COC\n\n            (Live) filtering\n\n            Cut, copy, paste, rename, delete, create etc.\n\n            Highly customisable\n\n            Rich API\n          license: \"GPL-3.0\"\n          labels: neovim\n          dependencies: |\n            nvim-web-devicons\n"
  },
  {
    "path": ".github/workflows/release-please.yml",
    "content": "on:\n  push:\n    branches:\n      - master\n  workflow_dispatch:\nconcurrency:\n  group: ${{ github.workflow }}\n  cancel-in-progress: true\nname: release-please\npermissions:\n  contents: write\n  pull-requests: write\njobs:\n  release-please:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: googleapis/release-please-action@v4\n        id: release\n      - uses: actions/checkout@v6\n      - name: tag major and minor versions\n        if: ${{ steps.release.outputs.release_created }}\n        run: |\n          git config user.name github-actions[bot]\n          git config user.email 41898282+github-actions[bot]@users.noreply.github.com\n          git remote add gh-token \"https://${{ secrets.GITHUB_TOKEN }}@github.com/googleapis/release-please-action.git\"\n          git tag -d       v${{ steps.release.outputs.major }}                                                                       || true\n          git tag -d       v${{ steps.release.outputs.major }}.${{ steps.release.outputs.minor }}                                    || true\n          git tag -d       v${{ steps.release.outputs.major }}.${{ steps.release.outputs.minor }}.${{ steps.release.outputs.patch }} || true\n          git push origin :v${{ steps.release.outputs.major }}                                                                       || true\n          git push origin :v${{ steps.release.outputs.major }}.${{ steps.release.outputs.minor }}                                    || true\n          git push origin :v${{ steps.release.outputs.major }}.${{ steps.release.outputs.minor }}.${{ steps.release.outputs.patch }} || true\n          git tag -a       v${{ steps.release.outputs.major }}                                                                       -m \"Release v${{ steps.release.outputs.major }}\"\n          git tag -a       v${{ steps.release.outputs.major }}.${{ steps.release.outputs.minor }}                                    -m \"Release v${{ steps.release.outputs.major }}.${{ steps.release.outputs.minor }}\"\n          git tag -a       v${{ steps.release.outputs.major }}.${{ steps.release.outputs.minor }}.${{ steps.release.outputs.patch }} -m \"Release v${{ steps.release.outputs.major }}.${{ steps.release.outputs.minor }}.${{ steps.release.outputs.patch }}\"\n          git push origin  v${{ steps.release.outputs.major }}\n          git push origin  v${{ steps.release.outputs.major }}.${{ steps.release.outputs.minor }}\n          git push origin  v${{ steps.release.outputs.major }}.${{ steps.release.outputs.minor }}.${{ steps.release.outputs.patch }}\n"
  },
  {
    "path": ".github/workflows/semantic-pr-subject.yml",
    "content": "name: Semantic Pull Request Subject\non:\n  pull_request:\n    types:\n      - opened\n      - reopened\n      - edited\n      - synchronize\n  workflow_dispatch:\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.head_ref }}\n  cancel-in-progress: true\njobs:\n  semantic-pr-subject:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: amannn/action-semantic-pull-request@v6.1.1\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n"
  },
  {
    "path": ".gitignore",
    "content": "/luals-out/\n/luals/\n# backup vim files\n*~\n"
  },
  {
    "path": ".hooks/pre-commit.sh",
    "content": "#!/usr/bin/env sh\n\nmake\n"
  },
  {
    "path": ".luacheckrc",
    "content": "local M = {}\n\n-- Don't report unused self arguments of methods.\nM.self = false\n\nM.ignore = {\n  \"631\",  -- max_line_length\n}\n\n-- Global objects defined by the C code\nM.globals = {\n  \"vim\",\n}\n\nreturn M\n"
  },
  {
    "path": ".luarc.json",
    "content": "{\n  \"$schema\": \"https://raw.githubusercontent.com/sumneko/vscode-lua/master/setting/schema.json\",\n  \"runtime.version\": \"Lua 5.1\",\n  \"workspace\": {\n    \"library\": [\n      \"$VIMRUNTIME/lua/vim\",\n      \"${3rd}/luv/library\"\n    ]\n  },\n  \"format\": {\n    \"defaultConfig\": {\n      \"indent_style\": \"space\",\n      \"max_line_length\": \"140\",\n      \"indent_size\": \"2\",\n      \"continuation_indent\": \"2\",\n      \"quote_style\": \"double\",\n      \"call_arg_parentheses\": \"always\",\n      \"space_before_closure_open_parenthesis\": \"false\",\n      \"align_continuous_similar_call_args\": \"true\"\n    }\n  },\n  \"diagnostics\": {\n    \"libraryFiles\": \"Disable\",\n    \"globals\": [],\n    \"neededFileStatus\": {\n      \"ambiguity-1\": \"Any\",\n      \"assign-type-mismatch\": \"Any\",\n      \"await-in-sync\": \"Any\",\n      \"cast-local-type\": \"Any\",\n      \"cast-type-mismatch\": \"Any\",\n      \"circle-doc-class\": \"Any\",\n      \"close-non-object\": \"Any\",\n      \"code-after-break\": \"Any\",\n      \"codestyle-check\": \"None\",\n      \"count-down-loop\": \"Any\",\n      \"deprecated\": \"Any\",\n      \"different-requires\": \"Any\",\n      \"discard-returns\": \"Any\",\n      \"doc-field-no-class\": \"Any\",\n      \"duplicate-doc-alias\": \"Any\",\n      \"duplicate-doc-field\": \"Any\",\n      \"duplicate-doc-param\": \"Any\",\n      \"duplicate-index\": \"Any\",\n      \"duplicate-set-field\": \"Any\",\n      \"empty-block\": \"Any\",\n      \"global-element\": \"Any\",\n      \"global-in-nil-env\": \"Any\",\n      \"incomplete-signature-doc\": \"Any\",\n      \"inject-field\": \"Any\",\n      \"invisible\": \"Any\",\n      \"lowercase-global\": \"Any\",\n      \"missing-fields\": \"Any\",\n      \"missing-global-doc\": \"Any\",\n      \"missing-local-export-doc\": \"Any\",\n      \"missing-parameter\": \"Any\",\n      \"missing-return\": \"Any\",\n      \"missing-return-value\": \"Any\",\n      \"name-style-check\": \"None\",\n      \"need-check-nil\": \"Any\",\n      \"newfield-call\": \"Any\",\n      \"newline-call\": \"Any\",\n      \"no-unknown\": \"None\",\n      \"not-yieldable\": \"Any\",\n      \"param-type-mismatch\": \"Any\",\n      \"redefined-local\": \"Any\",\n      \"redundant-parameter\": \"Any\",\n      \"redundant-return\": \"Any\",\n      \"redundant-return-value\": \"Any\",\n      \"redundant-value\": \"Any\",\n      \"return-type-mismatch\": \"Any\",\n      \"spell-check\": \"None\",\n      \"trailing-space\": \"Any\",\n      \"unbalanced-assignments\": \"Any\",\n      \"undefined-doc-class\": \"Any\",\n      \"undefined-doc-name\": \"Any\",\n      \"undefined-doc-param\": \"Any\",\n      \"undefined-env-child\": \"Any\",\n      \"undefined-field\": \"None\",\n      \"undefined-global\": \"Any\",\n      \"unknown-cast-variable\": \"Any\",\n      \"unknown-diag-code\": \"Any\",\n      \"unknown-operator\": \"Any\",\n      \"unreachable-code\": \"Any\",\n      \"unused-function\": \"Any\",\n      \"unused-label\": \"Any\",\n      \"unused-local\": \"Any\",\n      \"unused-vararg\": \"Any\"\n    }\n  }\n}\n"
  },
  {
    "path": ".release-please-manifest.json",
    "content": "{\n  \".\": \"1.16.0\"\n}\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\n\n## [1.16.0](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v1.15.0...nvim-tree-v1.16.0) (2026-03-17)\n\n\n### Features\n\n* **#2994:** add visual mode operations: copy, cut, delete, trash, toggle bookmark ([#3268](https://github.com/nvim-tree/nvim-tree.lua/issues/3268)) ([9197f3e](https://github.com/nvim-tree/nvim-tree.lua/commit/9197f3ee3f0c9a754aab5b16500db6d7da5f68fd))\n* add default &lt;Del&gt; mapping for api.fs.remove ([#3238](https://github.com/nvim-tree/nvim-tree.lua/issues/3238)) ([ca8d82f](https://github.com/nvim-tree/nvim-tree.lua/commit/ca8d82fff26cb12ced239713e3222f4a9dcd0da0))\n\n\n### Bug Fixes\n\n* **##3288:** restore single-item paste conflict prompts ([#3289](https://github.com/nvim-tree/nvim-tree.lua/issues/3289)) ([b3772ad](https://github.com/nvim-tree/nvim-tree.lua/commit/b3772adec8db61ba9098c5624a0823a77be3a23d))\n* **#3178:** handle Windows paths in ignore_dirs and add .zig-cache to defaults ([#3261](https://github.com/nvim-tree/nvim-tree.lua/issues/3261)) ([5499299](https://github.com/nvim-tree/nvim-tree.lua/commit/5499299746b95c49ef4fc89b30c29a79b577881f))\n* **#3187:** prevent closing the last non-floating window when deleting files ([#3282](https://github.com/nvim-tree/nvim-tree.lua/issues/3282)) ([018a078](https://github.com/nvim-tree/nvim-tree.lua/commit/018a078c1e149bcd0c4e65c92aabb8e12501d769))\n* **#3198:** add filesystem_watchers.max_events to handle runaway filesystem events on PowerShell ([#3232](https://github.com/nvim-tree/nvim-tree.lua/issues/3232)) ([c07ce43](https://github.com/nvim-tree/nvim-tree.lua/commit/c07ce43527e5f0242121f4eb1feb7ac0ecea8275))\n* **#3248:** bookmark filter shows contents of marked directories ([#3249](https://github.com/nvim-tree/nvim-tree.lua/issues/3249)) ([5757bcf](https://github.com/nvim-tree/nvim-tree.lua/commit/5757bcf0447d22d8f78826bc5c59b28da2824c3b))\n* **#3251:** pass git.timeout to all vim.system git calls ([#3277](https://github.com/nvim-tree/nvim-tree.lua/issues/3277)) ([e49b0d9](https://github.com/nvim-tree/nvim-tree.lua/commit/e49b0d9bfa70989cf8b5abf5557f51e6e57f68d6))\n* **#3265:** rename module api.health to api.appearance, to avoid :checkhealth detection ([#3266](https://github.com/nvim-tree/nvim-tree.lua/issues/3266)) ([1df1960](https://github.com/nvim-tree/nvim-tree.lua/commit/1df1960d0e3a26643a4100f64fa03b991b9f4b85))\n* **#3267:** renderer.icons.*_placement 'right_align' at the right hand edge, not the right of the name ([#3270](https://github.com/nvim-tree/nvim-tree.lua/issues/3270)) ([fa3c458](https://github.com/nvim-tree/nvim-tree.lua/commit/fa3c45875f9b1f56ace57711c6f2ac22484ed956))\n* **#3281:** fix a bug when a view width of -1 is returned from a function ([#3283](https://github.com/nvim-tree/nvim-tree.lua/issues/3283)) ([c988e28](https://github.com/nvim-tree/nvim-tree.lua/commit/c988e289428d9202b28ba27479647033c7dd2956))\n* allow 0 (unlimited) filesystem_watchers.max_events which is the new default, except for windows at 1000 ([#3279](https://github.com/nvim-tree/nvim-tree.lua/issues/3279)) ([87594aa](https://github.com/nvim-tree/nvim-tree.lua/commit/87594aa7c8808701b418b285e9adf690acead201))\n* increase filesystem_watchers.max_events from 100 to 1000 ([#3263](https://github.com/nvim-tree/nvim-tree.lua/issues/3263)) ([0f4d2d6](https://github.com/nvim-tree/nvim-tree.lua/commit/0f4d2d6998dc324d54d6adce94186bee43725afb))\n* restore bookmark filter for marked directories ([5757bcf](https://github.com/nvim-tree/nvim-tree.lua/commit/5757bcf0447d22d8f78826bc5c59b28da2824c3b))\n\n\n### Performance Improvements\n\n* **#3284:** re-draw instead of refreshing the tree when a buffer is modified ([#3285](https://github.com/nvim-tree/nvim-tree.lua/issues/3285)) ([c8d8d51](https://github.com/nvim-tree/nvim-tree.lua/commit/c8d8d515c29f0f0b1a352e0d75616f74f42fc03b))\n\n## [1.15.0](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v1.14.0...nvim-tree-v1.15.0) (2026-01-11)\n\n\n### Features\n\n* **#1826:** add diagnostics.diagnostic_opts: vim.diagnostic.Opts will override diagnostics.severity and diagnostics.icons ([#3190](https://github.com/nvim-tree/nvim-tree.lua/issues/3190)) ([fefa335](https://github.com/nvim-tree/nvim-tree.lua/commit/fefa335f1c8f690eb668a1efd18ee4fc6d64cd3e))\n* **#1851:** add bookmarks.persist, default path: vim.fn.stdpath(\"data\") .. \"/nvim-tree-bookmarks.json\", disabled by default ([#3033](https://github.com/nvim-tree/nvim-tree.lua/issues/3033)) ([c89215d](https://github.com/nvim-tree/nvim-tree.lua/commit/c89215d6a1107a3c0c134750be48657ba8e6a9aa))\n* **#3213:** add `view.width.lines_excluded` option ([776a5cd](https://github.com/nvim-tree/nvim-tree.lua/commit/776a5cdfac948b490e06f1d1d22c4cb986e40699))\n* **#3213:** add view.width.lines_excluded option ([#3214](https://github.com/nvim-tree/nvim-tree.lua/issues/3214)) ([776a5cd](https://github.com/nvim-tree/nvim-tree.lua/commit/776a5cdfac948b490e06f1d1d22c4cb986e40699))\n* add NvimTreeFilter filetype ([64e2192](https://github.com/nvim-tree/nvim-tree.lua/commit/64e2192f5250796aa4a7f33c6ad888515af50640))\n* load command definitions at nvim startup ([#3211](https://github.com/nvim-tree/nvim-tree.lua/issues/3211)) ([1eda256](https://github.com/nvim-tree/nvim-tree.lua/commit/1eda2569394f866360e61f590f1796877388cb8a))\n* load command definitions in `plugin` directory ([1eda256](https://github.com/nvim-tree/nvim-tree.lua/commit/1eda2569394f866360e61f590f1796877388cb8a))\n* set filter input filetype to NvimTreeFilter ([#3207](https://github.com/nvim-tree/nvim-tree.lua/issues/3207)) ([64e2192](https://github.com/nvim-tree/nvim-tree.lua/commit/64e2192f5250796aa4a7f33c6ad888515af50640))\n* use `add_trailing` also for symlink destination ([81ede55](https://github.com/nvim-tree/nvim-tree.lua/commit/81ede55c47528ff7c81b2a498fbee61b298c4e2f))\n\n\n### Bug Fixes\n\n* **#3226:** set &swapfile=false before setting tree buffer name, avoiding any potential collisions with a swapfile ([#3227](https://github.com/nvim-tree/nvim-tree.lua/issues/3227)) ([8298117](https://github.com/nvim-tree/nvim-tree.lua/commit/8298117311a1f23f039c278e4e4977ab80a15e33))\n* api.tree.change_root_to_node on a file now changes directory to parent as per documentation ([#3228](https://github.com/nvim-tree/nvim-tree.lua/issues/3228)) ([b8b44b6](https://github.com/nvim-tree/nvim-tree.lua/commit/b8b44b6a2494d086a9177251a119f9daec6cace8))\n* correctly assign extmarks to lines when computing tree window width in `grow` when `nvim-tree.view.width.lines_excluded` contains \"root\" ([e66994d](https://github.com/nvim-tree/nvim-tree.lua/commit/e66994d40db2d57c91bf9aeaee8bf7ab8b1131f6))\n* incorrect window width when right_align icons present ([#3239](https://github.com/nvim-tree/nvim-tree.lua/issues/3239)) ([e66994d](https://github.com/nvim-tree/nvim-tree.lua/commit/e66994d40db2d57c91bf9aeaee8bf7ab8b1131f6))\n* prevent NvimTree to be alternate buffer when tab open ([#3205](https://github.com/nvim-tree/nvim-tree.lua/issues/3205)) ([e397756](https://github.com/nvim-tree/nvim-tree.lua/commit/e397756d2a79d74314ea4cd3efc41300e91c0ff0))\n* renderer.add_trailing applies to symlink destination ([#3217](https://github.com/nvim-tree/nvim-tree.lua/issues/3217)) ([81ede55](https://github.com/nvim-tree/nvim-tree.lua/commit/81ede55c47528ff7c81b2a498fbee61b298c4e2f))\n\n\n### Performance Improvements\n\n* **commands:** defer module loading ([#3210](https://github.com/nvim-tree/nvim-tree.lua/issues/3210)) ([68c67ad](https://github.com/nvim-tree/nvim-tree.lua/commit/68c67adfabfd1ce923839570507ef2e81ab8a408))\n\n## [1.14.0](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v1.13.0...nvim-tree-v1.14.0) (2025-08-12)\n\n\n### Features\n\n* **#2685:** highlight git new tracked with NvimTreeGitFileNewHL ([#3176](https://github.com/nvim-tree/nvim-tree.lua/issues/3176)) ([0a52012](https://github.com/nvim-tree/nvim-tree.lua/commit/0a52012d611f3c1492b8d2aba363fabf734de91d))\n* **#2789:** add optional function expand_until to api.tree.expand_all and api.node.expand ([#3166](https://github.com/nvim-tree/nvim-tree.lua/issues/3166)) ([1b876db](https://github.com/nvim-tree/nvim-tree.lua/commit/1b876db04903b93c78c97fd3f3dd85d59eeef5ff))\n* **#2826:** allow only one window with nvim-tree buffer per tab ([#3174](https://github.com/nvim-tree/nvim-tree.lua/issues/3174)) ([dd2364d](https://github.com/nvim-tree/nvim-tree.lua/commit/dd2364d6802f7f57a98acb8b545ed484c6697626))\n* **#3157:** add view.cursorlineopt ([#3158](https://github.com/nvim-tree/nvim-tree.lua/issues/3158)) ([8eb5e0b](https://github.com/nvim-tree/nvim-tree.lua/commit/8eb5e0bfd1c4da6efc03ab0c1ccf463dbaae831e))\n\n\n### Bug Fixes\n\n* **#3077:** deleting a directory containing symlinked directory will delete the contents of the linked directory ([#3168](https://github.com/nvim-tree/nvim-tree.lua/issues/3168)) ([10db694](https://github.com/nvim-tree/nvim-tree.lua/commit/10db6943cb40625941a35235eeb385ffdfbf827a))\n* **#3157:** add view.cursorlineopt ([8eb5e0b](https://github.com/nvim-tree/nvim-tree.lua/commit/8eb5e0bfd1c4da6efc03ab0c1ccf463dbaae831e))\n* **#3172:** live filter exception ([#3173](https://github.com/nvim-tree/nvim-tree.lua/issues/3173)) ([0a7fcdf](https://github.com/nvim-tree/nvim-tree.lua/commit/0a7fcdf3f8ba208f4260988a198c77ec11748339))\n* invalid window id for popup info window ([#3147](https://github.com/nvim-tree/nvim-tree.lua/issues/3147)) ([d54a187](https://github.com/nvim-tree/nvim-tree.lua/commit/d54a1875a91e1a705795ea26074795210b92ce7f))\n* **picker:** exclude full_name window id from the choice ([#3165](https://github.com/nvim-tree/nvim-tree.lua/issues/3165)) ([543ed3c](https://github.com/nvim-tree/nvim-tree.lua/commit/543ed3cac212dc3993ef9f042f6c0812e34ddd43))\n* window picker ignore hidden window ([#3145](https://github.com/nvim-tree/nvim-tree.lua/issues/3145)) ([d87b41c](https://github.com/nvim-tree/nvim-tree.lua/commit/d87b41ca537e2131622d48a6c25ccf2fbe0e5d62))\n\n\n### Performance Improvements\n\n* **#3171:** cache toplevel for untracked ([#3185](https://github.com/nvim-tree/nvim-tree.lua/issues/3185)) ([4425136](https://github.com/nvim-tree/nvim-tree.lua/commit/442513648c6936e754c3308a1c58591a399493e5))\n* **#3171:** use vim.system() instead of vim.fn.system() to execute git toplevel ([#3175](https://github.com/nvim-tree/nvim-tree.lua/issues/3175)) ([9a05b9e](https://github.com/nvim-tree/nvim-tree.lua/commit/9a05b9e9f928856ca23dbf876fab372003180c3f))\n\n\n### Reverts\n\n* **#3180, #3177:** invalid group or tabpage ([#3181](https://github.com/nvim-tree/nvim-tree.lua/issues/3181)) ([9b289ab](https://github.com/nvim-tree/nvim-tree.lua/commit/9b289abd6998e30fd24cbc9919e0b0cbed6364ce))\n* **#3180, #3177:** resolve live filter failures ([#3183](https://github.com/nvim-tree/nvim-tree.lua/issues/3183)) ([a4699c0](https://github.com/nvim-tree/nvim-tree.lua/commit/a4699c0904103e7767334f6da05f5c2ea5514845))\n\n## [1.13.0](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v1.12.0...nvim-tree-v1.13.0) (2025-06-14)\n\n\n### Features\n\n* **#3113:** add renderer.icons.folder_arrow_padding ([#3114](https://github.com/nvim-tree/nvim-tree.lua/issues/3114)) ([ea5097a](https://github.com/nvim-tree/nvim-tree.lua/commit/ea5097a1e2702b4827cb7380e7fa0bd6da87699c))\n* **#3132:** add api.node.expand and api.node.collapse ([#3133](https://github.com/nvim-tree/nvim-tree.lua/issues/3133)) ([ae59561](https://github.com/nvim-tree/nvim-tree.lua/commit/ae595611fb2225f2041996c042aa4e4b8663b41e))\n\n\n### Bug Fixes\n\n* \"Invalid buffer id\" on closing nvim-tree window ([#3129](https://github.com/nvim-tree/nvim-tree.lua/issues/3129)) ([25d16aa](https://github.com/nvim-tree/nvim-tree.lua/commit/25d16aab7d29ca940a9feb92e6bb734697417009))\n* **#2746:** background and right aligned icons in floating windows ([#3128](https://github.com/nvim-tree/nvim-tree.lua/issues/3128)) ([cbc3165](https://github.com/nvim-tree/nvim-tree.lua/commit/cbc3165e08893bb499da035c6f6f9d1512b57664))\n* **#3117:** allow changing filename's casing ([bd54d1d](https://github.com/nvim-tree/nvim-tree.lua/commit/bd54d1d33c20d8630703b9842480291588dbad07))\n* **#3117:** windows: change file/dir case ([#3135](https://github.com/nvim-tree/nvim-tree.lua/issues/3135)) ([bd54d1d](https://github.com/nvim-tree/nvim-tree.lua/commit/bd54d1d33c20d8630703b9842480291588dbad07))\n* **#3122:** remove redundant vim.validate ([#3123](https://github.com/nvim-tree/nvim-tree.lua/issues/3123)) ([e7d1b7d](https://github.com/nvim-tree/nvim-tree.lua/commit/e7d1b7dadc62fe2eccc17d814354b0a5688621ce))\n* **#3124:** fix icon padding for \"right_align\" placements, notably for dotfiles ([#3125](https://github.com/nvim-tree/nvim-tree.lua/issues/3125)) ([e4cd856](https://github.com/nvim-tree/nvim-tree.lua/commit/e4cd856ebf4fec51db10c69d63e43224b701cbce))\n* **#3124:** prevent empty icons_right_align response from breaking padding ([e4cd856](https://github.com/nvim-tree/nvim-tree.lua/commit/e4cd856ebf4fec51db10c69d63e43224b701cbce))\n* **#3134:** setting one glyph to \"\" no longer disables others ([#3136](https://github.com/nvim-tree/nvim-tree.lua/issues/3136)) ([ebcaccd](https://github.com/nvim-tree/nvim-tree.lua/commit/ebcaccda1c575fa19a8087445276e6671e2b9b37))\n* **#3143:** actions.open_file.window_picker.exclude applies when not using window picker ([#3144](https://github.com/nvim-tree/nvim-tree.lua/issues/3144)) ([05d8172](https://github.com/nvim-tree/nvim-tree.lua/commit/05d8172ebf9cdb2d140cf25b75625374fbc3df7f))\n* fixes [#3134](https://github.com/nvim-tree/nvim-tree.lua/issues/3134) ([ebcaccd](https://github.com/nvim-tree/nvim-tree.lua/commit/ebcaccda1c575fa19a8087445276e6671e2b9b37))\n* invalid buffer issue ([25d16aa](https://github.com/nvim-tree/nvim-tree.lua/commit/25d16aab7d29ca940a9feb92e6bb734697417009))\n\n## [1.12.0](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v1.11.0...nvim-tree-v1.12.0) (2025-04-20)\n\n\n### Features\n\n* add TreePreOpen event ([#3105](https://github.com/nvim-tree/nvim-tree.lua/issues/3105)) ([c24c047](https://github.com/nvim-tree/nvim-tree.lua/commit/c24c0470d9de277fbebecd718f33561ed7c90298))\n\n\n### Bug Fixes\n\n* **#3101:** when renderer.highlight_opened_files = \"none\" do not reload on BufUnload and BufReadPost ([#3102](https://github.com/nvim-tree/nvim-tree.lua/issues/3102)) ([5bea2b3](https://github.com/nvim-tree/nvim-tree.lua/commit/5bea2b37523a31288e0fcab42f3be5c1bd4516bb))\n* explicitly set `border` to `\"none\"` in full name float ([#3094](https://github.com/nvim-tree/nvim-tree.lua/issues/3094)) ([c3c1935](https://github.com/nvim-tree/nvim-tree.lua/commit/c3c193594213c5e2f89ec5d7729cad805f76b256))\n* reliably dispatch exactly one TreeOpen and TreeClose events ([#3107](https://github.com/nvim-tree/nvim-tree.lua/issues/3107)) ([3a63717](https://github.com/nvim-tree/nvim-tree.lua/commit/3a63717d3d332d8f39aaf65be7a0e4c2265af021))\n\n## [1.11.0](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v1.10.0...nvim-tree-v1.11.0) (2025-02-22)\n\n\n### Features\n\n* **#1984:** add quit_on_open and focus opts to various api.node.open functions ([#3054](https://github.com/nvim-tree/nvim-tree.lua/issues/3054)) ([3281f33](https://github.com/nvim-tree/nvim-tree.lua/commit/3281f331f7f0bef13eb00fb2d5a9d28b2f6155a2))\n* **#3037:** add API node.buffer.delete, node.buffer.wipe ([#3040](https://github.com/nvim-tree/nvim-tree.lua/issues/3040)) ([fee1da8](https://github.com/nvim-tree/nvim-tree.lua/commit/fee1da88972f5972a8296813f6c00d7598325ebd))\n\n\n### Bug Fixes\n\n* **#3045:** wipe scratch buffers for full name and show info popups ([#3050](https://github.com/nvim-tree/nvim-tree.lua/issues/3050)) ([fca0b67](https://github.com/nvim-tree/nvim-tree.lua/commit/fca0b67c0b5a31727fb33addc4d9c100736a2894))\n* **#3059:** test for presence of new 0.11 API vim.hl.range ([#3060](https://github.com/nvim-tree/nvim-tree.lua/issues/3060)) ([70825f2](https://github.com/nvim-tree/nvim-tree.lua/commit/70825f23db61ecd900c4cfea169bffe931926a9d))\n* arithmetic on nil value error on first git project open ([#3064](https://github.com/nvim-tree/nvim-tree.lua/issues/3064)) ([8052310](https://github.com/nvim-tree/nvim-tree.lua/commit/80523101f0ae48b7f1990e907b685a3d79776c01))\n* stl and stlnc fillchars are hidden in window picker ([b699143](https://github.com/nvim-tree/nvim-tree.lua/commit/b69914325a945ee5157f0d21047210b42af5776e))\n* window picker: hide fillchars: stl and stlnc ([#3066](https://github.com/nvim-tree/nvim-tree.lua/issues/3066)) ([b699143](https://github.com/nvim-tree/nvim-tree.lua/commit/b69914325a945ee5157f0d21047210b42af5776e))\n\n## [1.10.0](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v1.9.0...nvim-tree-v1.10.0) (2025-01-13)\n\n\n### Features\n\n* **api:** add node.open.vertical_no_picker, node.open.horizontal_no_picker ([#3031](https://github.com/nvim-tree/nvim-tree.lua/issues/3031)) ([68fc4c2](https://github.com/nvim-tree/nvim-tree.lua/commit/68fc4c20f5803444277022c681785c5edd11916d))\n\n\n### Bug Fixes\n\n* **#3015:** dynamic width no longer truncates on right_align icons ([#3022](https://github.com/nvim-tree/nvim-tree.lua/issues/3022)) ([f7b76cd](https://github.com/nvim-tree/nvim-tree.lua/commit/f7b76cd1a75615c8d6254fc58bedd2a7304eb7d8))\n* **#3018:** error when focusing nvim-tree when in terminal mode ([#3019](https://github.com/nvim-tree/nvim-tree.lua/issues/3019)) ([db8d7ac](https://github.com/nvim-tree/nvim-tree.lua/commit/db8d7ac1f524fc6f808764b29fa695c51e014aa6))\n* **#3041:** use vim.diagnostic.get for updating diagnostics ([#3042](https://github.com/nvim-tree/nvim-tree.lua/issues/3042)) ([aae0185](https://github.com/nvim-tree/nvim-tree.lua/commit/aae01853ddbd790d1efd6ff04ff96cf38c02c95f))\n* Can't re-enter normal mode from terminal mode ([db8d7ac](https://github.com/nvim-tree/nvim-tree.lua/commit/db8d7ac1f524fc6f808764b29fa695c51e014aa6))\n* hijack directory \"BufEnter\", \"BufNewFile\" events are nested ([#3044](https://github.com/nvim-tree/nvim-tree.lua/issues/3044)) ([39bc630](https://github.com/nvim-tree/nvim-tree.lua/commit/39bc63081605c1d4b974131ebecaea11e8a8595f))\n* view.width functions may return strings ([#3020](https://github.com/nvim-tree/nvim-tree.lua/issues/3020)) ([6b4be1d](https://github.com/nvim-tree/nvim-tree.lua/commit/6b4be1dc0cd4d5d5b8e8b56b510a75016e99746f))\n\n## [1.9.0](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v1.8.0...nvim-tree-v1.9.0) (2024-12-07)\n\n\n### Features\n\n* **#2948:** add custom decorators, :help nvim-tree-decorators ([#2996](https://github.com/nvim-tree/nvim-tree.lua/issues/2996)) ([7a4ff1a](https://github.com/nvim-tree/nvim-tree.lua/commit/7a4ff1a516fe92a5ed6b79d7ce31ea4d8f341a72))\n\n\n### Bug Fixes\n\n* **#2954:** more efficient LSP updates, increase diagnostics.debounce_delay from 50ms to 500ms ([#3007](https://github.com/nvim-tree/nvim-tree.lua/issues/3007)) ([1f3ffd6](https://github.com/nvim-tree/nvim-tree.lua/commit/1f3ffd6af145af2a4930a61c50f763264922c3fe))\n* **#2990:** Do not check if buffer is buflisted in diagnostics.update() ([#2998](https://github.com/nvim-tree/nvim-tree.lua/issues/2998)) ([28eac28](https://github.com/nvim-tree/nvim-tree.lua/commit/28eac2801b201f301449e976d7a9e8cfde053ba3))\n* **#3009:** nvim &lt; 0.10 apply view options locally ([#3010](https://github.com/nvim-tree/nvim-tree.lua/issues/3010)) ([ca7c4c3](https://github.com/nvim-tree/nvim-tree.lua/commit/ca7c4c33cac2ad66ec69d45e465379716ef0cc97))\n* **api:** correct argument types in `wrap_node` and `wrap_node_or_nil` ([#3006](https://github.com/nvim-tree/nvim-tree.lua/issues/3006)) ([f7c65e1](https://github.com/nvim-tree/nvim-tree.lua/commit/f7c65e11d695a084ca10b93df659bb7e68b71f9f))\n\n## [1.8.0](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v1.7.1...nvim-tree-v1.8.0) (2024-11-09)\n\n\n### Features\n\n* **#2819:** add actions.open_file.relative_path, default enabled, following successful experiment ([#2995](https://github.com/nvim-tree/nvim-tree.lua/issues/2995)) ([2ee1c5e](https://github.com/nvim-tree/nvim-tree.lua/commit/2ee1c5e17fdfbf5013af31b1410e4a5f28f4cadd))\n* **#2938:** add default filesystem_watchers.ignore_dirs = { \"/.ccls-cache\", \"/build\", \"/node_modules\", \"/target\", } ([#2940](https://github.com/nvim-tree/nvim-tree.lua/issues/2940)) ([010ae03](https://github.com/nvim-tree/nvim-tree.lua/commit/010ae0365aafd6275c478d932515d2e8e897b7bb))\n\n\n### Bug Fixes\n\n* **#2945:** stack overflow on api.git.reload or fugitive event with watchers disabled ([#2949](https://github.com/nvim-tree/nvim-tree.lua/issues/2949)) ([5ad8762](https://github.com/nvim-tree/nvim-tree.lua/commit/5ad87620ec9d1190d15c88171a3f0122bc16b0fe))\n* **#2947:** root is never a dotfile, so that it doesn't propagate to children ([#2958](https://github.com/nvim-tree/nvim-tree.lua/issues/2958)) ([f5f6789](https://github.com/nvim-tree/nvim-tree.lua/commit/f5f67892996b280ae78b1b0a2d07c4fa29ae0905))\n* **#2951:** highlights incorrect following cancelled pick ([#2952](https://github.com/nvim-tree/nvim-tree.lua/issues/2952)) ([1c9553a](https://github.com/nvim-tree/nvim-tree.lua/commit/1c9553a19f70df3dcb171546a3d5e034531ef093))\n* **#2954:** resolve occasional tree flashing on diagnostics, set tree buffer options in deterministic order ([#2980](https://github.com/nvim-tree/nvim-tree.lua/issues/2980)) ([82ab19e](https://github.com/nvim-tree/nvim-tree.lua/commit/82ab19ebf79c1839d7351f2fed213d1af13a598e))\n* **#2961:** windows: escape brackets and parentheses when opening file ([#2962](https://github.com/nvim-tree/nvim-tree.lua/issues/2962)) ([63c7ad9](https://github.com/nvim-tree/nvim-tree.lua/commit/63c7ad9037fb7334682dd0b3a177cee25c5c8a0f))\n* **#2969:** After a rename, the node loses selection ([#2974](https://github.com/nvim-tree/nvim-tree.lua/issues/2974)) ([1403933](https://github.com/nvim-tree/nvim-tree.lua/commit/14039337a563f4efd72831888f332a15585f0ea1))\n* **#2972:** error on :colorscheme ([#2973](https://github.com/nvim-tree/nvim-tree.lua/issues/2973)) ([6e5a204](https://github.com/nvim-tree/nvim-tree.lua/commit/6e5a204ca659bb8f2a564df75df2739edec03cb0))\n* **#2976:** use vim.loop to preserve neovim 0.9 compatibility ([#2977](https://github.com/nvim-tree/nvim-tree.lua/issues/2977)) ([00dff48](https://github.com/nvim-tree/nvim-tree.lua/commit/00dff482f9a8fb806a54fd980359adc6cd45d435))\n* **#2978:** grouped folder not showing closed icon ([#2979](https://github.com/nvim-tree/nvim-tree.lua/issues/2979)) ([120ba58](https://github.com/nvim-tree/nvim-tree.lua/commit/120ba58254835d412bbc91cffe847e9be835fadd))\n* **#2981:** windows: root changed when navigating with LSP ([#2982](https://github.com/nvim-tree/nvim-tree.lua/issues/2982)) ([c22124b](https://github.com/nvim-tree/nvim-tree.lua/commit/c22124b37409bee6d1a0da77f4f3a1526f7a204d))\n* symlink file icons rendered when renderer.icons.show.file = false, folder.symlink* was incorrectly rendered as folder.default|open ([#2983](https://github.com/nvim-tree/nvim-tree.lua/issues/2983)) ([2156bc0](https://github.com/nvim-tree/nvim-tree.lua/commit/2156bc08c982d3c4b4cfc2b8fd7faeff58a88e10))\n\n## [1.7.1](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v1.7.0...nvim-tree-v1.7.1) (2024-09-30)\n\n\n### Bug Fixes\n\n* **#2794:** sshfs compatibility ([#2922](https://github.com/nvim-tree/nvim-tree.lua/issues/2922)) ([9650e73](https://github.com/nvim-tree/nvim-tree.lua/commit/9650e735baad0d39505f4cb4867a60f02858536a))\n* **#2928:** nil explorer in parent move action ([#2929](https://github.com/nvim-tree/nvim-tree.lua/issues/2929)) ([0429f28](https://github.com/nvim-tree/nvim-tree.lua/commit/0429f286b350c65118d66b646775bf187936fa47))\n* **#2930:** empty groups expanded on reload ([#2935](https://github.com/nvim-tree/nvim-tree.lua/issues/2935)) ([4520c03](https://github.com/nvim-tree/nvim-tree.lua/commit/4520c0355cc561830ee2cf90dc37a2a75abf7995))\n* invalid explorer on open ([#2927](https://github.com/nvim-tree/nvim-tree.lua/issues/2927)) ([59a8a6a](https://github.com/nvim-tree/nvim-tree.lua/commit/59a8a6ae5e9d3eae99d08ab655d12fd51d5d17f3))\n\n\n### Reverts\n\n* **#2794:** sshfs compatibility ([#2920](https://github.com/nvim-tree/nvim-tree.lua/issues/2920)) ([8405ecf](https://github.com/nvim-tree/nvim-tree.lua/commit/8405ecfbd6bb08a94ffc9c68fef211eea56e8a3b))\n\n## [1.7.0](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v1.6.1...nvim-tree-v1.7.0) (2024-09-21)\n\n\n### Features\n\n* **#2430:** use vim.ui.open as default system_open, for neovim 0.10+ ([#2912](https://github.com/nvim-tree/nvim-tree.lua/issues/2912)) ([03f737e](https://github.com/nvim-tree/nvim-tree.lua/commit/03f737e5744a2b3ebb4b086f7636a3399224ec0c))\n* help closes on &lt;Esc&gt; and api.tree.toggle_help mappings ([#2909](https://github.com/nvim-tree/nvim-tree.lua/issues/2909)) ([b652dbd](https://github.com/nvim-tree/nvim-tree.lua/commit/b652dbd0e0489c5fbb81fbededf0d99029cd2f38))\n\n\n### Bug Fixes\n\n* **#2862:** windows path replaces backslashes with forward slashes ([#2903](https://github.com/nvim-tree/nvim-tree.lua/issues/2903)) ([45a93d9](https://github.com/nvim-tree/nvim-tree.lua/commit/45a93d99794fff3064141d5b3a50db98ce352697))\n* **#2906:** resource leak on populate children ([#2907](https://github.com/nvim-tree/nvim-tree.lua/issues/2907)) ([a4dd5ad](https://github.com/nvim-tree/nvim-tree.lua/commit/a4dd5ad5c8f9349142291d24e0e6466995594b9a))\n* **#2917:** fix root copy paths: Y, ge, gy, y ([#2918](https://github.com/nvim-tree/nvim-tree.lua/issues/2918)) ([b18ce8b](https://github.com/nvim-tree/nvim-tree.lua/commit/b18ce8be8f162eee0bc37addcfe17d7d019fcec7))\n* safely close last tree window ([#2913](https://github.com/nvim-tree/nvim-tree.lua/issues/2913)) ([bd48816](https://github.com/nvim-tree/nvim-tree.lua/commit/bd4881660bf0ddfa6acb21259f856ba3dcb26a93))\n* safely close tree window with pcall and debug logging ([bd48816](https://github.com/nvim-tree/nvim-tree.lua/commit/bd4881660bf0ddfa6acb21259f856ba3dcb26a93))\n\n## [1.6.1](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v1.6.0...nvim-tree-v1.6.1) (2024-09-09)\n\n\n### Bug Fixes\n\n* **#2794:** sshfs compatibility ([#2893](https://github.com/nvim-tree/nvim-tree.lua/issues/2893)) ([2d6e64d](https://github.com/nvim-tree/nvim-tree.lua/commit/2d6e64dd8c45a86f312552b7a47eef2c8623a25c))\n* **#2868:** windows: do not visit unenumerable directories such as Application Data ([#2874](https://github.com/nvim-tree/nvim-tree.lua/issues/2874)) ([2104786](https://github.com/nvim-tree/nvim-tree.lua/commit/210478677cb9d672c4265deb0e9b59d58b675bd4))\n* **#2878:** nowrapscan prevents move from root ([#2880](https://github.com/nvim-tree/nvim-tree.lua/issues/2880)) ([4234095](https://github.com/nvim-tree/nvim-tree.lua/commit/42340952af598a08ab80579d067b6da72a9e6d29))\n* **#2879:** remove unnecessary tree window width setting to prevent unnecessary :wincmd = ([#2881](https://github.com/nvim-tree/nvim-tree.lua/issues/2881)) ([d43ab67](https://github.com/nvim-tree/nvim-tree.lua/commit/d43ab67d0eb4317961c5e9d15fffe908519debe0))\n\n## [1.6.0](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v1.5.0...nvim-tree-v1.6.0) (2024-08-10)\n\n\n### Features\n\n* **#2225:** add renderer.hidden_display to show a summary of hidden files below the tree ([#2856](https://github.com/nvim-tree/nvim-tree.lua/issues/2856)) ([e25eb7f](https://github.com/nvim-tree/nvim-tree.lua/commit/e25eb7fa83f7614bb23d762e91d2de44fcd7103b))\n* **#2349:** add \"right_align\" option for renderer.icons.*_placement ([#2839](https://github.com/nvim-tree/nvim-tree.lua/issues/2839)) ([1d629a5](https://github.com/nvim-tree/nvim-tree.lua/commit/1d629a5d3f7d83d516494c221a2cfc079f43bc47))\n* **#2349:** add \"right_align\" option for renderer.icons.*_placement ([#2846](https://github.com/nvim-tree/nvim-tree.lua/issues/2846)) ([48d0e82](https://github.com/nvim-tree/nvim-tree.lua/commit/48d0e82f9434691cc50d970898142a8c084a49d6))\n* add renderer.highlight_hidden, renderer.icons.show.hidden and renderer.icons.hidden_placement for dotfile icons/highlights ([#2840](https://github.com/nvim-tree/nvim-tree.lua/issues/2840)) ([48a9290](https://github.com/nvim-tree/nvim-tree.lua/commit/48a92907575df1dbd7242975a04e98169cb3a115))\n\n\n### Bug Fixes\n\n* **#2859:** make sure window still exists when restoring options ([#2863](https://github.com/nvim-tree/nvim-tree.lua/issues/2863)) ([466fbed](https://github.com/nvim-tree/nvim-tree.lua/commit/466fbed3e4b61fcc23a48fe99de7bfa264a9fee8))\n\n## [1.5.0](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v1.4.0...nvim-tree-v1.5.0) (2024-07-11)\n\n\n### Features\n\n* **#2127:** add experimental.actions.open_file.relative_path to open files with a relative path rather than absolute ([#2805](https://github.com/nvim-tree/nvim-tree.lua/issues/2805)) ([869c064](https://github.com/nvim-tree/nvim-tree.lua/commit/869c064721a6c2091f22c3541e8f0ff958361771))\n* **#2598:** add api.tree.resize ([#2811](https://github.com/nvim-tree/nvim-tree.lua/issues/2811)) ([2ede0de](https://github.com/nvim-tree/nvim-tree.lua/commit/2ede0de67b47e89e2b4cb488ea3f58b8f5a8c90a))\n* **#2799:** `filesystem_watchers.ignore_dirs` and `git.disable_for_dirs` may be functions ([#2800](https://github.com/nvim-tree/nvim-tree.lua/issues/2800)) ([8b2c5c6](https://github.com/nvim-tree/nvim-tree.lua/commit/8b2c5c678be4b49dff6a2df794877000113fd77b))\n* **#2799:** filesystem_watchers.ignore_dirs and git.disable_for_dirs may be functions ([8b2c5c6](https://github.com/nvim-tree/nvim-tree.lua/commit/8b2c5c678be4b49dff6a2df794877000113fd77b))\n\n\n### Bug Fixes\n\n* **#2813:** macos: enable file renaming with changed capitalization ([#2814](https://github.com/nvim-tree/nvim-tree.lua/issues/2814)) ([abfd1d1](https://github.com/nvim-tree/nvim-tree.lua/commit/abfd1d1b6772540364743531cc0331e08a0027a9))\n* **#2819:** experimental.actions.open_file.relative_path issue following change directory ([#2820](https://github.com/nvim-tree/nvim-tree.lua/issues/2820)) ([12a9a99](https://github.com/nvim-tree/nvim-tree.lua/commit/12a9a995a455d2c2466e47140663275365a5d2fc))\n\n## [1.4.0](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v1.3.3...nvim-tree-v1.4.0) (2024-06-09)\n\n### Notice\n\n* Neovim 0.9 is now the minimum supported version; please upgrade to neovim release version 0.9 or 0.10.\n\n### Reverts\n\n* **#2781:** \"refactor: replace deprecated use of vim.diagnostic.is_disabled()\" ([#2784](https://github.com/nvim-tree/nvim-tree.lua/issues/2784)) ([517e4fb](https://github.com/nvim-tree/nvim-tree.lua/commit/517e4fbb9ef3c0986da7047f44b4b91a2400f93c))\n\n\n### Miscellaneous Chores\n\n* release 1.4.0 ([1cac800](https://github.com/nvim-tree/nvim-tree.lua/commit/1cac8005df6da484c97499247754afa59fef92db))\n\n## [1.3.3](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v1.3.2...nvim-tree-v1.3.3) (2024-05-14)\n\n\n### Bug Fixes\n\n* nil access exception with git integration when changing branches ([#2774](https://github.com/nvim-tree/nvim-tree.lua/issues/2774)) ([340d3a9](https://github.com/nvim-tree/nvim-tree.lua/commit/340d3a9795e06bdd1814228de398cd510f9bfbb0))\n\n## [1.3.2](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v1.3.1...nvim-tree-v1.3.2) (2024-05-12)\n\n\n### Bug Fixes\n\n* **#2758:** use nvim-webdevicons default file icon, not renderer.icons.glyphs.default, as per :help ([#2759](https://github.com/nvim-tree/nvim-tree.lua/issues/2759)) ([347e1eb](https://github.com/nvim-tree/nvim-tree.lua/commit/347e1eb35264677f66a79466bb5e3d111968e12c))\n* **#2758:** use nvim-webdevicons default for default files ([347e1eb](https://github.com/nvim-tree/nvim-tree.lua/commit/347e1eb35264677f66a79466bb5e3d111968e12c))\n* **#925:** handle newlines in file names ([#2754](https://github.com/nvim-tree/nvim-tree.lua/issues/2754)) ([64f61e4](https://github.com/nvim-tree/nvim-tree.lua/commit/64f61e4c913047a045ff90bd188dd3b54ee443cf))\n\n## [1.3.1](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v1.3.0...nvim-tree-v1.3.1) (2024-04-25)\n\n\n### Bug Fixes\n\n* **#2535:** TextYankPost event sends vim.v.event ([#2734](https://github.com/nvim-tree/nvim-tree.lua/issues/2734)) ([d8d3a15](https://github.com/nvim-tree/nvim-tree.lua/commit/d8d3a1590a05b2d8b5eb26e2ed1c6052b1b47a77))\n* **#2733:** escape trash path ([#2735](https://github.com/nvim-tree/nvim-tree.lua/issues/2735)) ([81eb8d5](https://github.com/nvim-tree/nvim-tree.lua/commit/81eb8d519233c105f30dc0a278607e62b20502fd))\n\n## [1.3.0](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v1.2.0...nvim-tree-v1.3.0) (2024-03-30)\n\n\n### Features\n\n* add update_focused_file.exclude  ([#2673](https://github.com/nvim-tree/nvim-tree.lua/issues/2673)) ([e20966a](https://github.com/nvim-tree/nvim-tree.lua/commit/e20966ae558524f8d6f93dc37f5d2a8605f893e2))\n\n\n### Bug Fixes\n\n* **#2658:** change SpellCap groups to reduce confusion: ExecFile-&gt;Question, ImageFile->Question, SpecialFile->Title, Symlink->Underlined; add all other highlight groups to :NvimTreeHiTest ([#2732](https://github.com/nvim-tree/nvim-tree.lua/issues/2732)) ([0aca092](https://github.com/nvim-tree/nvim-tree.lua/commit/0aca0920f44b12a8383134bcb52da9faec123608))\n* bookmark filter shows marked directory children ([#2719](https://github.com/nvim-tree/nvim-tree.lua/issues/2719)) ([2d97059](https://github.com/nvim-tree/nvim-tree.lua/commit/2d97059661c83787372c8c003e743c984ba3ac50))\n\n## [1.2.0](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v1.1.1...nvim-tree-v1.2.0) (2024-03-24)\n\n\n### Features\n\n* add api.tree.toggle_enable_filters ([#2706](https://github.com/nvim-tree/nvim-tree.lua/issues/2706)) ([f7c09bd](https://github.com/nvim-tree/nvim-tree.lua/commit/f7c09bd72e50e1795bd3afb9e2a2b157b4bfb3c3))\n\n## [1.1.1](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v1.1.0...nvim-tree-v1.1.1) (2024-03-15)\n\n\n### Bug Fixes\n\n* **#2395:** marks.bulk.move defaults to directory at cursor ([#2688](https://github.com/nvim-tree/nvim-tree.lua/issues/2688)) ([cfea5bd](https://github.com/nvim-tree/nvim-tree.lua/commit/cfea5bd0806aab41bef6014c6cf5a510910ddbdb))\n* **#2705:** change NvimTreeWindowPicker cterm background from Cyan to more visible DarkBlue ([#2708](https://github.com/nvim-tree/nvim-tree.lua/issues/2708)) ([1fd9c98](https://github.com/nvim-tree/nvim-tree.lua/commit/1fd9c98960463d2d5d400916c0633b2df016941d))\n* bookmark filter should include parent directory ([#2704](https://github.com/nvim-tree/nvim-tree.lua/issues/2704)) ([76b9810](https://github.com/nvim-tree/nvim-tree.lua/commit/76b98109f62caa12b2f1dff472060b2233ea2e90))\n\n## [1.1.0](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v1.0.0...nvim-tree-v1.1.0) (2024-03-14)\n\n\n### Features\n\n* **#2630:** file renames can now create directories ([#2657](https://github.com/nvim-tree/nvim-tree.lua/issues/2657)) ([efafd73](https://github.com/nvim-tree/nvim-tree.lua/commit/efafd73efa9bc8c26282aed563ba0f01c7465b06))\n* add api.fs.copy.basename, default mapping ge ([#2698](https://github.com/nvim-tree/nvim-tree.lua/issues/2698)) ([8f2a50f](https://github.com/nvim-tree/nvim-tree.lua/commit/8f2a50f1cd0c64003042364cf317c8788eaa6c8c))\n\n\n### Bug Fixes\n\n* **#2695:** git toplevel guard against missing paths ([#2696](https://github.com/nvim-tree/nvim-tree.lua/issues/2696)) ([3c4267e](https://github.com/nvim-tree/nvim-tree.lua/commit/3c4267eb5045fa86b67fe40c0c63d31efc801e77))\n* searchcount exception on invalid search regex ([#2693](https://github.com/nvim-tree/nvim-tree.lua/issues/2693)) ([041dbd1](https://github.com/nvim-tree/nvim-tree.lua/commit/041dbd18f440207ad161503a384e7c82d575db66))\n\n## [1.0.0](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v0.100.0...nvim-tree-v1.0.0) (2024-02-18)\n\n\n### Features\n\n* **#2654:** filters.custom may be a function ([#2655](https://github.com/nvim-tree/nvim-tree.lua/issues/2655)) ([4a87b8b](https://github.com/nvim-tree/nvim-tree.lua/commit/4a87b8b46b4a30107971871df3cb7f4c30fdd5d0))\n\n\n### Miscellaneous Chores\n\n* release 1.0.0 ([#2678](https://github.com/nvim-tree/nvim-tree.lua/issues/2678)) ([d16246a](https://github.com/nvim-tree/nvim-tree.lua/commit/d16246a7575538f77e9246520449b99333c469f7))\n\n## [0.100.0](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v0.99.0...nvim-tree-v0.100.0) (2024-02-11)\n\n\n### Features\n\n* **#1389:** api: recursive node navigation for git and diagnostics ([#2525](https://github.com/nvim-tree/nvim-tree.lua/issues/2525)) ([5d13cc8](https://github.com/nvim-tree/nvim-tree.lua/commit/5d13cc8205bce4963866f73c50f6fdc18a515ffe))\n* **#2415:** add :NvimTreeHiTest ([#2664](https://github.com/nvim-tree/nvim-tree.lua/issues/2664)) ([b278fc2](https://github.com/nvim-tree/nvim-tree.lua/commit/b278fc25ae0fc95e4808eb5618f07fc2522fd2b3))\n* **#2415:** colour and highlight overhaul, see :help nvim-tree-highlight-overhaul ([#2455](https://github.com/nvim-tree/nvim-tree.lua/issues/2455)) ([e9c5abe](https://github.com/nvim-tree/nvim-tree.lua/commit/e9c5abe073a973f54d3ca10bfe30f253569f4405))\n* add node.open.toggle_group_empty, default mapping L ([#2647](https://github.com/nvim-tree/nvim-tree.lua/issues/2647)) ([8cbb1db](https://github.com/nvim-tree/nvim-tree.lua/commit/8cbb1db8e90b62fc56f379992e622e9f919792ce))\n\n\n### Bug Fixes\n\n* **#2415:** disambiguate highlight groups, see :help nvim-tree-highlight-overhaul ([#2639](https://github.com/nvim-tree/nvim-tree.lua/issues/2639)) ([d9cb432](https://github.com/nvim-tree/nvim-tree.lua/commit/d9cb432d2c8d8fa9267ddbd7535d76fe4df89360))\n* **#2415:** fix NvimTreeIndentMarker highlight group: FileIcon-&gt;FolderIcon ([e9ac136](https://github.com/nvim-tree/nvim-tree.lua/commit/e9ac136a3ab996aa8e4253253521dcf2cb66b81b))\n* **#2415:** highlight help header and mappings ([#2669](https://github.com/nvim-tree/nvim-tree.lua/issues/2669)) ([39e6fef](https://github.com/nvim-tree/nvim-tree.lua/commit/39e6fef85ac3bb29532b877aa7c9c34911c661af))\n* **#2415:** nvim 0.8 highlight overhaul support, limited to only show highest highlight precedence ([#2642](https://github.com/nvim-tree/nvim-tree.lua/issues/2642)) ([f39f7b6](https://github.com/nvim-tree/nvim-tree.lua/commit/f39f7b6fcd3865ac2146de4cb4045286308f2935))\n* **#2415:** NvimTreeIndentMarker highlight group: FileIcon-&gt;FolderIcon ([#2656](https://github.com/nvim-tree/nvim-tree.lua/issues/2656)) ([e9ac136](https://github.com/nvim-tree/nvim-tree.lua/commit/e9ac136a3ab996aa8e4253253521dcf2cb66b81b))\n* **#2624:** open file from docked floating window ([#2627](https://github.com/nvim-tree/nvim-tree.lua/issues/2627)) ([f24afa2](https://github.com/nvim-tree/nvim-tree.lua/commit/f24afa2cef551122b8bd53bb2e4a7df42343ce2e))\n* **#2632:** occasional error stack when locating nvim-tree window ([#2633](https://github.com/nvim-tree/nvim-tree.lua/issues/2633)) ([48b1d86](https://github.com/nvim-tree/nvim-tree.lua/commit/48b1d8638fa3726236ae22e0e48a74ac8ea6592a))\n* **#2637:** show buffer modified icons and highlights ([#2638](https://github.com/nvim-tree/nvim-tree.lua/issues/2638)) ([7bdb220](https://github.com/nvim-tree/nvim-tree.lua/commit/7bdb220d0fe604a77361e92cdbc7af1b8a412126))\n* **#2643:** correctly apply linked highlight groups in tree window ([#2653](https://github.com/nvim-tree/nvim-tree.lua/issues/2653)) ([fbee8a6](https://github.com/nvim-tree/nvim-tree.lua/commit/fbee8a69a46f558d29ab84e96301425b0501c668))\n* allow highlight overrides for DEFAULT_DEFS: NvimTreeFolderIcon, NvimTreeWindowPicker ([#2636](https://github.com/nvim-tree/nvim-tree.lua/issues/2636)) ([74525ac](https://github.com/nvim-tree/nvim-tree.lua/commit/74525ac04760bf0d9fec2bf51474d2b05f36048e))\n* bad column offset when using full_name ([#2629](https://github.com/nvim-tree/nvim-tree.lua/issues/2629)) ([75ff64e](https://github.com/nvim-tree/nvim-tree.lua/commit/75ff64e6663fc3b23c72dca32b2f838acefe7c8a))\n* passing nil as window handle in view.get_winnr ([48b1d86](https://github.com/nvim-tree/nvim-tree.lua/commit/48b1d8638fa3726236ae22e0e48a74ac8ea6592a))\n\n## 0.99.0 (2024-01-01)\n\n\n### Features\n\n* **#1850:** add \"no bookmark\" filter ([#2571](https://github.com/nvim-tree/nvim-tree.lua/issues/2571)) ([8f92e1e](https://github.com/nvim-tree/nvim-tree.lua/commit/8f92e1edd399f839a23776dcc6eee4ba18030370))\n* add kind param to vim.ui.select function calls ([#2602](https://github.com/nvim-tree/nvim-tree.lua/issues/2602)) ([dc839a7](https://github.com/nvim-tree/nvim-tree.lua/commit/dc839a72a6496ce22ebd3dd959115cf97c1b20a0))\n* add option to skip gitignored files on git navigation ([#2583](https://github.com/nvim-tree/nvim-tree.lua/issues/2583)) ([50f30bc](https://github.com/nvim-tree/nvim-tree.lua/commit/50f30bcd8c62ac4a83d133d738f268279f2c2ce2))\n\n\n### Bug Fixes\n\n* **#2519:** Diagnostics Not Updated When Tree Not Visible ([#2597](https://github.com/nvim-tree/nvim-tree.lua/issues/2597)) ([96a783f](https://github.com/nvim-tree/nvim-tree.lua/commit/96a783fbd606a458bcce2ef8041240a8b94510ce))\n* **#2609:** help toggle ([#2611](https://github.com/nvim-tree/nvim-tree.lua/issues/2611)) ([fac4900](https://github.com/nvim-tree/nvim-tree.lua/commit/fac4900bd18a9fa15be3d104645d9bdef7b3dcec))\n* hijack_cursor on update focused file and vim search ([#2600](https://github.com/nvim-tree/nvim-tree.lua/issues/2600)) ([02ae523](https://github.com/nvim-tree/nvim-tree.lua/commit/02ae52357ba4da77a4c120390791584a81d15340))\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to `nvim-tree.lua`\n\nThank you for contributing.\n\nSee [wiki: Development](https://github.com/nvim-tree/nvim-tree.lua/wiki/Development) for environment setup, tips and tools.\n\n<!-- \nhttps://github.com/jonschlinkert/markdown-toc\nmarkdown-toc --maxdepth=2 -i CONTRIBUTING.md\n-->\n\n<!-- toc -->\n\n- [Tools](#tools)\n- [Quality](#quality)\n  * [lint](#lint)\n  * [style](#style)\n  * [format-fix](#format-fix)\n  * [check](#check)\n  * [format-check](#format-check)\n- [Diagnostics](#diagnostics)\n- [Backwards Compatibility](#backwards-compatibility)\n- [:help Documentation](#help-documentation)\n  * [Generated Content](#generated-content)\n  * [Updating And Generating](#updating-and-generating)\n  * [Checking And Linting](#checking-and-linting)\n- [Windows](#windows)\n- [Pull Request](#pull-request)\n  * [Subject](#subject)\n- [AI Usage Policy: Highly Discouraged](#ai-usage-policy-highly-discouraged)\n  * [nvim-tree Is A Community Project](#nvim-tree-is-a-community-project)\n  * [The Burden Of Review Must Not Increase](#the-burden-of-review-must-not-increase)\n  * [AI Generated PR Rules](#ai-generated-pr-rules)\n\n<!-- tocstop -->\n\n# Tools\n\nFollowing are used during CI and strongly recommended during local development.\n\nLanguage server: [luals](https://luals.github.io)\n\nLint: [luacheck](https://github.com/lunarmodules/luacheck/)\n\nnvim-tree migrated from stylua to EmmyLuaCodeStyle ~2024/10. `vim.lsp.buf.format()` may be used as it is the default formatter for luals, using an embedded [EmmyLuaCodeStyle](https://github.com/CppCXY/EmmyLuaCodeStyle)\n\nFormatting: [EmmyLuaCodeStyle](https://github.com/CppCXY/EmmyLuaCodeStyle): `CodeFormat` executable\n\nYou can install them via you OS package manager e.g. `pacman`, `brew` or other via other package managers such as `cargo` or `luarocks`\n\n# Quality\n\nThe following quality checks are mandatory and are performed during CI. They run on the entire `lua` directory and return 1 on any failure.\n\nYou can run them all via `make` or `make all`\n\nYou can setup git hooks to run all checks by running `scripts/setup-hooks.sh`\n\n## lint\n\n1. Runs luacheck quietly using `.luacheck` settings\n\n```sh\nmake lint\n```\n\n## style\n\n1. Runs lua language server `codestyle-check` only, using `.luarc.json` settings\n1. Runs `scripts/doc-comments.sh` to normalise annotated documentation\n\n```sh\nmake style\n```\n\n## format-fix\n\nYou can automatically fix most style issues using `CodeFormat`:\n\n```sh\nmake format-fix\n```\n\n## check\n\n1. Runs the checks that the LSP lua language server runs inside nvim using `.luarc.json` via `scripts/luals-check.sh`\n\n```sh\nmake check\n```\n\nAssumes `$VIMRUNTIME` is `/usr/share/nvim/runtime`. Adjust as necessary e.g.\n\n```sh\nVIMRUNTIME=\"/my/path/to/runtime\" make check\n```\n\nIf `lua-language-server` is not available or `--check` doesn't function (e.g. Arch Linux 3.9.1-1) you can manually install it as per `ci.yml` using its current `luals_version` e.g.\n\n```sh\nmkdir luals\ncurl -L \"https://github.com/LuaLS/lua-language-server/releases/download/3.15.0/lua-language-server-3.15.0-linux-x64.tar.gz\" | tar zx --directory luals\n\nPATH=\"luals/bin:${PATH}\" make check\n```\n\n## format-check\n\nThis is run in CI. Commit or stage your changes and run:\n\n```sh\nmake format-check\n```\n\n- Re-runs `make format-fix`\n- Checks that `git diff` is empty, to ensure that all content has been generated. This is why a stage or commit is necessary.\n\n# Diagnostics\n\nDiagnostics issues may not be suppressed. See [luals](https://luals.github.io) documentation for details on how to structure the code and comments.\n\nSuppressions are permitted only in the following cases:\n\n- Backwards compatibility shims\n- neovim API metadata incorrect, awaiting upstream fix\n- classic class framework\n\n# Backwards Compatibility\n\nWhenever new neovim API is introduced, please ensure that it is available in older versions. See `:help deprecated.txt` and `$VIMRUNTIME/lua/vim/_meta/api.lua`\n\nSee `nvim-tree.setup` for the oldest supported version of neovim. If the API is not availble in that version, a backwards compatibility shim must be used e.g.\n\n```lua\nif vim.fn.has(\"nvim-0.10\") == 1 then\n  modified = vim.api.nvim_get_option_value(\"modified\", { buf = target_bufid })\nelse\n  modified = vim.api.nvim_buf_get_option(target_bufid, \"modified\") ---@diagnostic disable-line: deprecated\nend\n```\n\n# :help Documentation\n\nPlease update or add to `doc/nvim-tree-lua.txt` as needed.\n\n## Generated Content\n\n`doc/nvim-tree-lua.txt` content starting at `*nvim-tree-config*` will be replaced with generated content. Do not manually edit that content.\n\n### API and Config\n\nHelp is generated for:\n- `nvim_tree.config` classes from `lua/nvim-tree/_meta/config/`\n- `nvim_tree.api` functions from `lua/nvim-tree/_meta/api/`\n\nPlease add or update documentation when you make changes, see `:help dev-lua-doc` for docstring format.\n\n`scripts/vimdoc_config.lua` contains the manifest of help sources.\n\n### Config And Mappings\n\nHelp is updated for:\n- Default keymap at `keymap.on_attach_default`\n- Default config at `--- config-default-start`\n\n## Updating And Generating\n\nNvim sources are required. You will be prompted with instructions on fetching and using the sources.\n\nSee comments at the start of each script for complete details.\n\n```sh\nmake help-update\n```\n\n- `scripts/help-defaults.sh`\n  - Update config defaults `*nvim-tree-config-default*`\n  - Update default mappings:\n    - `*nvim-tree-mappings-default*`\n    - `*nvim-tree-quickstart-help*`\n\n- `scripts/vimdoc.sh doc`\n  - Remove content starting at `*nvim-tree-config*`\n  - Generate config classes `*nvim-tree-config*`\n  - Generate API `*nvim-tree-api*`\n\n## Checking And Linting\n\nThis is run in CI. Commit or stage your changes and run:\n\n```sh\nmake help-check\n```\n\n- Re-runs `make help-update`\n- Checks that `git diff` is empty, to ensure that all content has been generated. This is why a stage or commit is necessary.\n- Lints `doc/nvim-tree-lua.txt` using `scripts/vimdoc.sh lintdoc` to check for no broken links etc.\n\n# Windows\n\nPlease note that nvim-tree team members do not have access to nor expertise with Windows.\n\nYou will need to be an active participant during development and raise a PR to resolve any issues that may arise.\n\nPlease ensure that windows specific features and fixes are behind the appropriate feature flag, see [wiki: OS Feature Flags](https://github.com/nvim-tree/nvim-tree.lua/wiki/Development#os-feature-flags)\n\n# Pull Request\n\nPlease reference any issues in the description e.g. \"resolves #1234\", which will be closed upon merge.\n\nPlease check \"allow edits by maintainers\" to allow nvim-tree developers to make small changes such as documentation tweaks.\n\nDo not enable or use any AI review tools (e.g. Copilot) on the Pull Request.\n\n## Subject\n\nThe merge commit message will be the subject of the PR.\n\nA [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0) subject will be validated by the Semantic Pull Request Subject CI job. Reference the issue to be used in the release notes e.g.\n\n`fix(#2395): marks.bulk.move defaults to directory at cursor`\n\nAvailable types:\n* feat: A new feature\n* fix: A bug fix\n* docs: Documentation only changes\n* style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)\n* refactor: A code change that neither fixes a bug nor adds a feature\n* perf: A code change that improves performance\n* test: Adding missing tests or correcting existing tests\n* build: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)\n* ci: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)\n* chore: Other changes that don't modify src or test files\n* revert: Reverts a previous commit\n\nIf in doubt, look at previous commits.\n\nSee also [The Conventional Commits ultimate cheatsheet](https://gist.github.com/gabrielecanepa/fa6cca1a8ae96f77896fe70ddee65527)\n\n# AI Usage Policy: Highly Discouraged\n\n## nvim-tree Is A Community Project\n\nnvim-tree is a work of passion, building a community of free thinking individuals who contribute highly polished, elegant and maintainable code. Community members range from novices to professionals and all are welcome. Teaching, encouraging and celebrating the growth of less experienced developers is of great importance.\n\nAI generated code is discouraged as this doesn't match these nvim-tree values.\n\nHuman PR reviews will always be prioritised over AI generated PRs.\n\n## The Burden Of Review Must Not Increase\n\nThere must be a human in the loop at all times. The contributor is the author of and is fully accountable for AI generated contributions.\n\nAI generated PRs have low, or even non-existent entry level. Human generated PRs require you to be motivated, do research, familiarise yourself with code, make effort to write the code and test it.\n\nLow effort or unqualified code increases the burden of review and testing on maintainers, who are limited in time.\n\nContributors must:\n\n- Read and review all generated code and documentation before raising a PR\n- Fully understand all code and documentation\n- Be able to answer any questions during review\n\n## AI Generated PR Rules\n\nThe PR description and comments must be written by the contributor, with no AI assistance beyond grammar or English translations.\n\nThe description must:\n\n- Describe the solution design, justifying all decisions\n- Clearly state which AI was used\n- List which code and documentation was written by a human and which was written by AI\n- Detail all testing performed\n\nThere must be far greater than usual number of detailed, explicit comments:\n\n- File level: overview of the changes made\n- Line level: 1-2 comments per function/method\n\nDo not enable or use any AI review tools (e.g. Copilot) on the Pull Request.\n"
  },
  {
    "path": "LICENSE",
    "content": "nvim-tree.lua is a file explorer / filesystem tree view plugin for neovim\nCopyright © 2019 Yazdani Kiyan\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n"
  },
  {
    "path": "Makefile",
    "content": "all: lint style check\n\n#\n# mandatory checks\n#\nlint: luacheck\n\nstyle: style-check style-doc\n\ncheck: luals\n\n#\n# subtasks\n#\nluacheck:\n\tluacheck --codes --quiet lua --exclude-files \"**/_meta/**\"\n\nstyle-check:\n\tscripts/luals-check.sh codestyle-check\n\nstyle-doc:\n\tscripts/doc-comments.sh\n\nluals:\n\tscripts/luals-check.sh\n\n#\n# format\n#\nformat-fix:\n\tCodeFormat format --config .editorconfig --workspace lua\n\nformat-check:\n\tCodeFormat format --config .editorconfig --workspace lua\n\tgit diff --exit-code lua\n\n#\n# utility\n#\nhelp-update:\n\tscripts/vimdoc.sh doc\n\tscripts/help-defaults.sh\n\n#\n# CI\n# --ignore-blank-lines is used as nightly has removed unnecessary blank lines that stable (0.11.5) currently inserts\n#\nhelp-check: help-update\n\tscripts/vimdoc.sh lintdoc\n\tgit diff --ignore-blank-lines --exit-code doc/nvim-tree-lua.txt\n\n\n.PHONY: all lint style check luacheck style-check style-doc luals format-fix format-check help-update help-check\n\n"
  },
  {
    "path": "README.md",
    "content": "# A File Explorer For Neovim Written In Lua\n\n[![CI](https://github.com/nvim-tree/nvim-tree.lua/actions/workflows/ci.yml/badge.svg)](https://github.com/nvim-tree/nvim-tree.lua/actions/workflows/ci.yml)\n\n<img align=\"left\" width=\"199\" height=\"598\" src=\"https://user-images.githubusercontent.com/1505378/232662694-8dc494e0-24da-497a-8541-29344293378c.png\">\n<img align=\"left\" width=\"199\" height=\"598\" src=\"https://user-images.githubusercontent.com/1505378/232662698-2f321315-c67a-486b-85d8-8c391de52392.png\">\n\n   Automatic updates\n\n   File type icons\n\n   Git integration\n\n   Diagnostics integration: LSP and COC\n\n   (Live) filtering\n\n   Cut, copy, paste, rename, delete, create\n\n   Highly customisable\n\n<br clear=\"left\"/>\n<br />\n\nTake a look at the [wiki](https://github.com/nvim-tree/nvim-tree.lua/wiki) for Showcases, Tips, Recipes and more.\n\nQuestions and general support: [Discussions](https://github.com/nvim-tree/nvim-tree.lua/discussions)\n\n<!-- \nhttps://github.com/jonschlinkert/markdown-toc\nmarkdown-toc --maxdepth=2 -i README.md\n-->\n\n<!-- toc -->\n\n- [Requirements](#requirements)\n- [Installing](#installing)\n- [Quick Start](#quick-start)\n  * [Setup](#setup)\n  * [Help](#help)\n  * [Custom Mappings](#custom-mappings)\n  * [Highlight Groups](#highlight-groups)\n- [Commands](#commands)\n- [Roadmap](#roadmap)\n- [API](#api)\n- [Contributing](#contributing)\n- [Screenshots](#screenshots)\n- [Team](#team)\n\n<!-- tocstop -->\n\n# Requirements\n\n[neovim >=0.9.0](https://github.com/neovim/neovim/wiki/Installing-Neovim)\n\n[nvim-web-devicons](https://github.com/nvim-tree/nvim-web-devicons) is optional and used to display file icons. It requires a [patched font](https://www.nerdfonts.com/). Your terminal emulator must be configured to use that font, usually \"Hack Nerd Font\"\n\n# Installing\n\nPlease install via your preferred package manager. See [Installation](https://github.com/nvim-tree/nvim-tree.lua/wiki/Installation) for some specific package manager instructions.\n\nMajor or minor versions may be specified via tags: `v<MAJOR>` e.g. `v1` or `v<MAJOR>.<MINOR>` e.g. `v1.23`\n\n# Quick Start\n\nInstall the plugins via your package manager:\n  `\"nvim-tree/nvim-tree.lua\"`\n  `\"nvim-tree/nvim-web-devicons\"`\n\nDisabling [netrw](https://neovim.io/doc/user/pi_netrw.html) is strongly advised, see [:help nvim-tree-netrw](doc/nvim-tree-lua.txt)\n\n## Setup\n\nSetup the plugin in your `init.lua`.\n\nSee [:help nvim-tree-setup](doc/nvim-tree-lua.txt) and [:help nvim-tree-config-default](doc/nvim-tree-lua.txt)\n\n```lua\n  -- disable netrw at the very start of your init.lua\n  vim.g.loaded_netrw = 1\n  vim.g.loaded_netrwPlugin = 1\n\n  -- optionally enable 24-bit colour\n  vim.opt.termguicolors = true\n\n  -- empty setup using defaults\n  require(\"nvim-tree\").setup()\n\n  -- OR setup with a config\n\n  ---@type nvim_tree.config\n  local config = {\n    sort = {\n      sorter = \"case_sensitive\",\n    },\n    view = {\n      width = 30,\n    },\n    renderer = {\n      group_empty = true,\n    },\n    filters = {\n      dotfiles = true,\n    },\n  }\n  require(\"nvim-tree\").setup(config)\n```\n\n## Help\n\nOpen the tree:  `:NvimTreeOpen`\n\nShow the mappings:  `g?`\n\n## Custom Mappings\n\n[:help nvim-tree-mappings-default](doc/nvim-tree-lua.txt) are applied by default however you may customise via [:help nvim_tree.config](doc/nvim-tree-lua.txt) `{on_attach}` e.g.\n\n```lua\n  local function my_on_attach(bufnr)\n    local api = require \"nvim-tree.api\"\n\n    local function opts(desc)\n      return { desc = \"nvim-tree: \" .. desc, buffer = bufnr, noremap = true, silent = true, nowait = true }\n    end\n\n    -- default mappings\n    api.map.on_attach.default(bufnr)\n\n    -- custom mappings\n    vim.keymap.set(\"n\", \"<C-t>\", api.tree.change_root_to_parent,        opts(\"Up\"))\n    vim.keymap.set(\"n\", \"?\",     api.tree.toggle_help,                  opts(\"Help\"))\n  end\n\n  -- pass to setup along with your other config\n  require(\"nvim-tree\").setup({\n    ---\n    on_attach = my_on_attach,\n    ---\n  })\n```\n\n## Highlight Groups\n\nSee [:help nvim-tree-highlight-groups](doc/nvim-tree-lua.txt)\n\nRun `:NvimTreeHiTest` to show all the highlights that nvim-tree uses.\n\nThey can be customised before or after setup is called and will be immediately\napplied at runtime. e.g.\n\n```lua\n  vim.cmd([[\n      :hi      NvimTreeExecFile    guifg=#ffa0a0\n      :hi      NvimTreeSpecialFile guifg=#ff80ff gui=underline\n      :hi      NvimTreeSymlink     guifg=Yellow  gui=italic\n      :hi link NvimTreeImageFile   Title\n  ]])\n```\n\n# Commands\n\nSee [:help nvim-tree-commands](doc/nvim-tree-lua.txt)\n\nSome commands may be executed with a bang `!` or take a `path` string argument.\n\nAll commands execute public API.\n\nSome basic commands:\n\n`:NvimTreeFocus`                           [:help nvim_tree.api.tree.open()](doc/nvim-tree-lua.txt)\n```lua\n  require(\"nvim-tree.api\").tree.open()\n```\n\n`:NvimTreeToggle`                          [:help nvim_tree.api.tree.toggle()](doc/nvim-tree-lua.txt)\n```lua\n  require(\"nvim-tree.api\").tree.toggle({\n    path = \"<args>\",\n    find_file = false,\n    update_root = false,\n    focus = true,\n  })\n```\n\n`:NvimTreeFindFile`                        [:help nvim_tree.api.tree.find_file()](doc/nvim-tree-lua.txt)\n```lua\n  require(\"nvim-tree.api\").tree.find_file({\n    open = true,\n    update_root = \"<bang>\",\n    focus = true,\n  })\n```\n\n`:NvimTreeCollapse`                        [:help nvim_tree.api.tree.collapse_all()](doc/nvim-tree-lua.txt)\n\n```lua\n  require(\"nvim-tree.api\").tree.collapse_all({\n    keep_buffers = false\n  })\n```\n\n# Roadmap\n\nnvim-tree is stable and new major features will not be added. The focus is on existing user experience.\n\nUsers are encouraged to add their own custom features via the public [API](#api).\n\nDevelopment is focused on:\n- Bug fixes\n- Performance\n- Quality of Life improvements\n- API / Events\n- Enhancements to existing features\n- Multi-instance capabilities\n\n# API\n\nnvim-tree exposes a public API. This is non breaking, with additions made as necessary. See [:help nvim-tree-api](doc/nvim-tree-lua.txt)\n\nSee wiki [Recipes](https://github.com/nvim-tree/nvim-tree.lua/wiki/Recipes) and [Tips](https://github.com/nvim-tree/nvim-tree.lua/wiki/Tips) for ideas and inspiration.\n\nPlease raise a [feature request](https://github.com/nvim-tree/nvim-tree.lua/issues/new?assignees=&labels=feature+request&template=feature_request.md&title=) if the API is insufficient for your needs. Contributions are always welcome, see below.\n\nYou may also subscribe to events that nvim-tree will dispatch in a variety of situations, see [:help nvim-tree-events](doc/nvim-tree-lua.txt)\n\n# Contributing\n\nPRs are always welcome. See [CONTRIBUTING](CONTRIBUTING.md) and [wiki: Development](https://github.com/nvim-tree/nvim-tree.lua/wiki/Development) to get started.\n\nSee [bug](https://github.com/nvim-tree/nvim-tree.lua/issues?q=is%3Aissue+is%3Aopen+label%3Abug) and [PR Please](https://github.com/nvim-tree/nvim-tree.lua/issues?q=is%3Aopen+is%3Aissue+label%3A%22PR+please%22) issues if you are looking for some work to get you started.\n\n# Screenshots\n\nSee [Showcases](https://github.com/nvim-tree/nvim-tree.lua/wiki/Showcases) wiki page for examples of user's configurations with sources.\n\nPlease add your own!\n\n# Team\n\n* [@alex-courtis](https://github.com/alex-courtis) Arch Linux\n* [@gegoune](https://github.com/gegoune) macOS\n* [@Akmadan23](https://github.com/Akmadan23) Linux\n* [@dependabot[bot]](https://github.com/apps/dependabot) Ubuntu Linux\n"
  },
  {
    "path": "doc/.gitignore",
    "content": "tags\n"
  },
  {
    "path": "doc/nvim-tree-lua.txt",
    "content": "*nvim-tree-lua.txt*       A File Explorer For Nvim        *nvim_tree*  *nvim-tree*\n\nAuthor: Yazdani Kiyan\n                                       Type |gO| to see the table of contents.\nHelp tag prefixes:\n• `nvim-tree-`  Help Sections e.g.\n  • |nvim-tree-mappings|\n  • |nvim-tree-config|\n• `nvim_tree.`  API and classes e.g.\n  • |nvim_tree.api.node.navigate.parent()|\n  • |nvim_tree.config.filesystem_watchers|\n\n==============================================================================\nIntroduction                                          *nvim-tree-introduction*\n\nFeatures\n\n    - Automatic updates\n    - File type icons\n    - Git integration\n    - Diagnostics integration: LSP and COC\n    - (Live) filtering\n    - Cut, copy, paste, rename, delete, create\n    - Highly customisable\n\nFile Icons\n\n    https://github.com/nvim-tree/nvim-web-devicons is optional and used to display file icons.\n    It requires a patched font: https://www.nerdfonts.com\n    Your terminal emulator must be configured to use that font, usually \"Hack Nerd Font\"\n\n     should look like an open folder.\n\n    Disable the display of icons with |nvim_tree.config.renderer.icons.show|\n\nColours\n\n    Syntax highlighting uses g:terminal_color_ from colorschemes, falls back to\n    ugly colors otherwise.\n\nGit Integration\n\n    One or two icons for git status. When two are shown, the left is staged.\n\n      ✗  unstaged\n      ✓  staged\n        unmerged\n      ➜  renamed\n      ★  untracked\n        deleted\n      ◌  ignored\n\nRequirements\n\n    Nvim >= 0.9\n\n==============================================================================\nQuickstart                                              *nvim-tree-quickstart*\n\nInstall the plugins via your package manager:\n  `\"nvim-tree/nvim-tree.lua\"`\n  `\"nvim-tree/nvim-web-devicons\"`\n\nDisabling |netrw| is strongly advised, see |nvim-tree-netrw|\n\n==============================================================================\nQuickstart: Setup                                 *nvim-tree-quickstart-setup*\n\nSetup the plugin in your `init.lua`.\n\nSee |nvim-tree-setup| and |nvim-tree-config-default| >lua\n\n  -- disable netrw at the very start of your init.lua\n  vim.g.loaded_netrw = 1\n  vim.g.loaded_netrwPlugin = 1\n\n  -- optionally enable 24-bit colour\n  vim.opt.termguicolors = true\n\n  -- empty setup using defaults\n  require(\"nvim-tree\").setup()\n\n  -- OR setup with a config\n\n  ---@type nvim_tree.config\n  local config = {\n    sort = {\n      sorter = \"case_sensitive\",\n    },\n    view = {\n      width = 30,\n    },\n    renderer = {\n      group_empty = true,\n    },\n    filters = {\n      dotfiles = true,\n    },\n  }\n  require(\"nvim-tree\").setup(config)\n<\n==============================================================================\nQuickstart: Help                                   *nvim-tree-quickstart-help*\n\nOpen the tree:  `:NvimTreeOpen`\n\nShow the mappings:  `g?`\n\n `<C-]>`           n    CD                         |nvim_tree.api.tree.change_root_to_node()|\n `<C-e>`           n    Open: In Place             |nvim_tree.api.node.open.replace_tree_buffer()|\n `<C-k>`           n    Info                       |nvim_tree.api.node.show_info_popup()|\n `<C-r>`           n    Rename: Omit Filename      |nvim_tree.api.fs.rename_sub()|\n `<C-t>`           n    Open: New Tab              |nvim_tree.api.node.open.tab()|\n `<C-v>`           n    Open: Vertical Split       |nvim_tree.api.node.open.vertical()|\n `<C-x>`           n    Open: Horizontal Split     |nvim_tree.api.node.open.horizontal()|\n `<BS>`            n    Close Directory            |nvim_tree.api.node.navigate.parent_close()|\n `<CR>`            n    Open                       |nvim_tree.api.node.open.edit()|\n `<Del>`           nx   Delete                     |nvim_tree.api.fs.remove()|\n `<Tab>`           n    Open Preview               |nvim_tree.api.node.open.preview()|\n `>`               n    Next Sibling               |nvim_tree.api.node.navigate.sibling.next()|\n `<`               n    Previous Sibling           |nvim_tree.api.node.navigate.sibling.prev()|\n `.`               n    Run Command                |nvim_tree.api.node.run.cmd()|\n `-`               n    Up                         |nvim_tree.api.tree.change_root_to_parent()|\n `a`               n    Create File Or Directory   |nvim_tree.api.fs.create()|\n `bd`              n    Delete Bookmarked          |nvim_tree.api.marks.bulk.delete()|\n `bt`              n    Trash Bookmarked           |nvim_tree.api.marks.bulk.trash()|\n `bmv`             n    Move Bookmarked            |nvim_tree.api.marks.bulk.move()|\n `B`               n    Toggle Filter: No Buffer   |nvim_tree.api.filter.no_buffer.toggle()|\n `c`               nx   Copy                       |nvim_tree.api.fs.copy.node()|\n `C`               n    Toggle Filter: Git Clean   |nvim_tree.api.filter.git.clean.toggle()|\n `[c`              n    Prev Git                   |nvim_tree.api.node.navigate.git.prev()|\n `]c`              n    Next Git                   |nvim_tree.api.node.navigate.git.next()|\n `d`               nx   Delete                     |nvim_tree.api.fs.remove()|\n `D`               nx   Trash                      |nvim_tree.api.fs.trash()|\n `E`               n    Expand All                 |nvim_tree.api.tree.expand_all()|\n `e`               n    Rename: Basename           |nvim_tree.api.fs.rename_basename()|\n `]e`              n    Next Diagnostic            |nvim_tree.api.node.navigate.diagnostics.next()|\n `[e`              n    Prev Diagnostic            |nvim_tree.api.node.navigate.diagnostics.prev()|\n `F`               n    Live Filter: Clear         |nvim_tree.api.filter.live.clear()|\n `f`               n    Live Filter: Start         |nvim_tree.api.filter.live.start()|\n `g?`              n    Help                       |nvim_tree.api.tree.toggle_help()|\n `gy`              n    Copy Absolute Path         |nvim_tree.api.fs.copy.absolute_path()|\n `ge`              n    Copy Basename              |nvim_tree.api.fs.copy.basename()|\n `H`               n    Toggle Filter: Dotfiles    |nvim_tree.api.filter.dotfiles.toggle()|\n `I`               n    Toggle Filter: Git Ignored |nvim_tree.api.filter.git.ignored.toggle()|\n `J`               n    Last Sibling               |nvim_tree.api.node.navigate.sibling.last()|\n `K`               n    First Sibling              |nvim_tree.api.node.navigate.sibling.first()|\n `L`               n    Toggle Group Empty         |nvim_tree.api.node.open.toggle_group_empty()|\n `M`               n    Toggle Filter: No Bookmark |nvim_tree.api.filter.no_bookmark.toggle()|\n `m`               nx   Toggle Bookmark            |nvim_tree.api.marks.toggle()|\n `o`               n    Open                       |nvim_tree.api.node.open.edit()|\n `O`               n    Open: No Window Picker     |nvim_tree.api.node.open.no_window_picker()|\n `p`               n    Paste                      |nvim_tree.api.fs.paste()|\n `P`               n    Parent Directory           |nvim_tree.api.node.navigate.parent()|\n `q`               n    Close                      |nvim_tree.api.tree.close()|\n `r`               n    Rename                     |nvim_tree.api.fs.rename()|\n `R`               n    Refresh                    |nvim_tree.api.tree.reload()|\n `s`               n    Run System                 |nvim_tree.api.node.run.system()|\n `S`               n    Search                     |nvim_tree.api.tree.search_node()|\n `u`               n    Rename: Full Path          |nvim_tree.api.fs.rename_full()|\n `U`               n    Toggle Filter: Custom      |nvim_tree.api.filter.custom.toggle()|\n `W`               n    Collapse All               |nvim_tree.api.tree.collapse_all()|\n `x`               nx   Cut                        |nvim_tree.api.fs.cut()|\n `y`               n    Copy Name                  |nvim_tree.api.fs.copy.filename()|\n `Y`               n    Copy Relative Path         |nvim_tree.api.fs.copy.relative_path()|\n `<2-LeftMouse>`   n    Open                       |nvim_tree.api.node.open.edit()|\n `<2-RightMouse>`  n    CD                         |nvim_tree.api.tree.change_root_to_node()|\n\n==============================================================================\nQuickstart: Custom Mappings             *nvim-tree-quickstart-custom-mappings*\n\n|nvim-tree-mappings-default| are applied by default however you may customise\nvia |nvim_tree.config| {on_attach} e.g. >lua\n\n  local function my_on_attach(bufnr)\n    local api = require \"nvim-tree.api\"\n\n    local function opts(desc)\n      return { desc = \"nvim-tree: \" .. desc, buffer = bufnr, noremap = true, silent = true, nowait = true }\n    end\n\n    -- default mappings\n    api.map.on_attach.default(bufnr)\n\n    -- custom mappings\n    vim.keymap.set(\"n\", \"<C-t>\", api.tree.change_root_to_parent,        opts(\"Up\"))\n    vim.keymap.set(\"n\", \"?\",     api.tree.toggle_help,                  opts(\"Help\"))\n  end\n\n  -- pass to setup along with your other config\n  require(\"nvim-tree\").setup({\n    ---\n    on_attach = my_on_attach,\n    ---\n  })\n<\n==============================================================================\nQuickstart: Highlight Groups                  *nvim-tree-quickstart-highlight*\n\nRun |:NvimTreeHiTest| to show all the highlights that nvim-tree uses.\n\nThey can be customised before or after setup is called and will be immediately\napplied at runtime. e.g. >lua\n\n  vim.cmd([[\n      :hi      NvimTreeExecFile    guifg=#ffa0a0\n      :hi      NvimTreeSpecialFile guifg=#ff80ff gui=underline\n      :hi      NvimTreeSymlink     guifg=Yellow  gui=italic\n      :hi link NvimTreeImageFile   Title\n  ]])\n<\nSee |nvim-tree-highlight-groups| for details.\n\n==============================================================================\nCommands                                                  *nvim-tree-commands*\n\nSome commands may be executed with a |<bang>| `!` or take a `path` string argument.\n\nAll commands execute public API.\n\n*:NvimTreeOpen*                            |nvim_tree.api.tree.open()| >lua\n\n  require(\"nvim-tree.api\").tree.open({\n    path = \"<args>\"\n  })\n<\n*:NvimTreeClose*                           |nvim_tree.api.tree.close()| >lua\n\n  require(\"nvim-tree.api\").tree.close()\n<\n*:NvimTreeToggle*                          |nvim_tree.api.tree.toggle()| >lua\n\n  require(\"nvim-tree.api\").tree.toggle({\n    path = \"<args>\",\n    find_file = false,\n    update_root = false,\n    focus = true,\n  })\n<\n*:NvimTreeFocus*                           |nvim_tree.api.tree.open()| >lua\n\n  require(\"nvim-tree.api\").tree.open()\n<\n*:NvimTreeRefresh*                         |nvim_tree.api.tree.reload()| >lua\n\n  require(\"nvim-tree.api\").tree.reload()\n<\n*:NvimTreeFindFile*                        |nvim_tree.api.tree.find_file()| >lua\n\n  require(\"nvim-tree.api\").tree.find_file({\n    open = true,\n    update_root = \"<bang>\",\n    focus = true,\n  })\n<\n*:NvimTreeFindFileToggle*                  |nvim_tree.api.tree.toggle()| >lua\n\n  require(\"nvim-tree.api\").tree.toggle({\n    path = \"<args>\",\n    find_file = true,\n    update_root = \"<bang>\",\n    focus = true,\n  })\n<\n*:NvimTreeClipboard*                       |nvim_tree.api.fs.print_clipboard()| >lua\n\n  require(\"nvim-tree.api\").fs.print_clipboard()\n<\n*:NvimTreeCollapse*                        |nvim_tree.api.tree.collapse_all()| >lua\n\n  require(\"nvim-tree.api\").tree.collapse_all({\n    keep_buffers = false\n  })\n<\n*:NvimTreeCollapseKeepBuffers*             |nvim_tree.api.tree.collapse_all()| >lua\n\n  require(\"nvim-tree.api\").tree.collapse_all({\n    keep_buffers = true\n  })\n<\n*:NvimTreeHiTest*                         |nvim_tree.api.appearance.hi_test()| >lua\n\n  require(\"nvim-tree.api\").appearance.hi_test()\n<\n*:NvimTreeResize*                          |nvim_tree.api.tree.resize()| >lua\n\n  local sign = c.args:sub(1, 1)\n  if sign == \"+\" or sign == \"-\" then\n    require(\"nvim-tree.api\").tree.resize({ relative = tonumber(c.args) })\n  else\n    require(\"nvim-tree.api\").tree.resize({ absolute = tonumber(c.args) })\n  end\n<\n\n==============================================================================\nSetup                                                        *nvim-tree-setup*\n\nYou must run the `setup()` function once to initialise nvim-tree. It may be\ncalled again to apply a change in configuration without restarting Nvim.\n\nThe `setup()` function takes one optional argument: |nvim_tree.config|. If\nomitted nvim-tree will be initialised with default configuration:\n|nvim-tree-config-default|.\n\nConfig can be validated with |lsp| when passed directly e.g. >lua\n\n  require(\"nvim-tree\").setup({\n    hijack_cursor = true,\n  })\n<\nor as a typed variable e.g. >lua\n\n  ---@type nvim_tree.config\n  local config = {\n    hijack_cursor = true,\n  }\n  require(\"nvim-tree\").setup(config)\n<\nThe first `setup()` call is cheap: it does nothing more than validate / apply\nthe configuration. Nothing happens until the tree is first opened.\n\nSubsequent `setup()` calls are expensive as they tear down the world before\napplying configuration.\n\n==============================================================================\nMappings                                                  *nvim-tree-mappings*\n\nMappings are set via the |nvim_tree.config| {on_attach} function, which is run upon\ncreating the nvim-tree buffer. Mappings are usually |nvim-tree-api| functions\nhowever may be your own.\n\nWhen {on_attach} is not a function, |nvim-tree-mappings-default| will be used.\n\nActive mappings may be viewed via HELP, default `g?`. The mapping's description\nis used when displaying HELP.\n\nThe `on_attach` function is passed the `bufnr` of nvim-tree. Use\n|vim.keymap.set()| or |nvim_set_keymap()| to define mappings as usual. e.g. >lua\n\n  local function my_on_attach(bufnr)\n    local api = require(\"nvim-tree.api\")\n\n    local function opts(desc)\n      return { desc = \"nvim-tree: \" .. desc, buffer = bufnr, noremap = true, silent = true, nowait = true }\n    end\n\n    -- copy default mappings here from defaults in next section\n    vim.keymap.set(\"n\", \"<C-]>\", api.tree.change_root_to_node,          opts(\"CD\"))\n    vim.keymap.set(\"n\", \"<C-e>\", api.node.open.replace_tree_buffer,     opts(\"Open: In Place\"))\n    ---\n    -- OR use all default mappings\n    api.map.on_attach.default(bufnr)\n\n    -- remove a default\n    vim.keymap.del(\"n\", \"<C-]>\", { buffer = bufnr })\n\n    -- override a default\n    vim.keymap.set(\"n\", \"<C-e>\", api.tree.reload,                       opts(\"Refresh\"))\n\n    -- add your mappings\n    vim.keymap.set(\"n\", \"?\",     api.tree.toggle_help,                  opts(\"Help\"))\n    ---\n  end\n\n  require(\"nvim-tree\").setup({\n    ---\n    on_attach = my_on_attach,\n    ---\n  })\n<\nSingle left mouse mappings can be achieved via `<LeftRelease>`.\n\nSingle right / middle mouse mappings will require changes to |'mousemodel'| or |'mouse'|.\n\n|vim.keymap.set()| {rhs} is a `(function|string)` thus it may be necessary to\ndefine your own function to map complex functionality e.g. >lua\n\n  local function print_node_path()\n    local api = require(\"nvim-tree.api\")\n    local node = api.tree.get_node_under_cursor()\n    print(node.absolute_path)\n  end\n\n  -- on_attach\n  vim.keymap.set(\"n\", \"<C-P>\", print_node_path, opts(\"Print Path\"))\n<\n==============================================================================\nMappings: Default                                 *nvim-tree-mappings-default*\n\nIn the absence of an |nvim_tree.config| {on_attach} function, the following\ndefaults will be applied.\n\nYou are encouraged to copy these to your {on_attach} function. >lua\n\n  local api = require(\"nvim-tree.api\")\n\n  local function opts(desc)\n    return { desc = \"nvim-tree: \" .. desc, buffer = bufnr, noremap = true, silent = true, nowait = true }\n  end\n\n  -- BEGIN_ON_ATTACH_DEFAULT\n  vim.keymap.set(\"n\",          \"<C-]>\",          api.tree.change_root_to_node,       opts(\"CD\"))\n  vim.keymap.set(\"n\",          \"<C-e>\",          api.node.open.replace_tree_buffer,  opts(\"Open: In Place\"))\n  vim.keymap.set(\"n\",          \"<C-k>\",          api.node.show_info_popup,           opts(\"Info\"))\n  vim.keymap.set(\"n\",          \"<C-r>\",          api.fs.rename_sub,                  opts(\"Rename: Omit Filename\"))\n  vim.keymap.set(\"n\",          \"<C-t>\",          api.node.open.tab,                  opts(\"Open: New Tab\"))\n  vim.keymap.set(\"n\",          \"<C-v>\",          api.node.open.vertical,             opts(\"Open: Vertical Split\"))\n  vim.keymap.set(\"n\",          \"<C-x>\",          api.node.open.horizontal,           opts(\"Open: Horizontal Split\"))\n  vim.keymap.set(\"n\",          \"<BS>\",           api.node.navigate.parent_close,     opts(\"Close Directory\"))\n  vim.keymap.set(\"n\",          \"<CR>\",           api.node.open.edit,                 opts(\"Open\"))\n  vim.keymap.set({ \"n\", \"x\" }, \"<Del>\",          api.fs.remove,                      opts(\"Delete\"))\n  vim.keymap.set(\"n\",          \"<Tab>\",          api.node.open.preview,              opts(\"Open Preview\"))\n  vim.keymap.set(\"n\",          \">\",              api.node.navigate.sibling.next,     opts(\"Next Sibling\"))\n  vim.keymap.set(\"n\",          \"<\",              api.node.navigate.sibling.prev,     opts(\"Previous Sibling\"))\n  vim.keymap.set(\"n\",          \".\",              api.node.run.cmd,                   opts(\"Run Command\"))\n  vim.keymap.set(\"n\",          \"-\",              api.tree.change_root_to_parent,     opts(\"Up\"))\n  vim.keymap.set(\"n\",          \"a\",              api.fs.create,                      opts(\"Create File Or Directory\"))\n  vim.keymap.set(\"n\",          \"bd\",             api.marks.bulk.delete,              opts(\"Delete Bookmarked\"))\n  vim.keymap.set(\"n\",          \"bt\",             api.marks.bulk.trash,               opts(\"Trash Bookmarked\"))\n  vim.keymap.set(\"n\",          \"bmv\",            api.marks.bulk.move,                opts(\"Move Bookmarked\"))\n  vim.keymap.set(\"n\",          \"B\",              api.filter.no_buffer.toggle,        opts(\"Toggle Filter: No Buffer\"))\n  vim.keymap.set({ \"n\", \"x\" }, \"c\",              api.fs.copy.node,                   opts(\"Copy\"))\n  vim.keymap.set(\"n\",          \"C\",              api.filter.git.clean.toggle,        opts(\"Toggle Filter: Git Clean\"))\n  vim.keymap.set(\"n\",          \"[c\",             api.node.navigate.git.prev,         opts(\"Prev Git\"))\n  vim.keymap.set(\"n\",          \"]c\",             api.node.navigate.git.next,         opts(\"Next Git\"))\n  vim.keymap.set({ \"n\", \"x\" }, \"d\",              api.fs.remove,                      opts(\"Delete\"))\n  vim.keymap.set({ \"n\", \"x\" }, \"D\",              api.fs.trash,                       opts(\"Trash\"))\n  vim.keymap.set(\"n\",          \"E\",              api.tree.expand_all,                opts(\"Expand All\"))\n  vim.keymap.set(\"n\",          \"e\",              api.fs.rename_basename,             opts(\"Rename: Basename\"))\n  vim.keymap.set(\"n\",          \"]e\",             api.node.navigate.diagnostics.next, opts(\"Next Diagnostic\"))\n  vim.keymap.set(\"n\",          \"[e\",             api.node.navigate.diagnostics.prev, opts(\"Prev Diagnostic\"))\n  vim.keymap.set(\"n\",          \"F\",              api.filter.live.clear,              opts(\"Live Filter: Clear\"))\n  vim.keymap.set(\"n\",          \"f\",              api.filter.live.start,              opts(\"Live Filter: Start\"))\n  vim.keymap.set(\"n\",          \"g?\",             api.tree.toggle_help,               opts(\"Help\"))\n  vim.keymap.set(\"n\",          \"gy\",             api.fs.copy.absolute_path,          opts(\"Copy Absolute Path\"))\n  vim.keymap.set(\"n\",          \"ge\",             api.fs.copy.basename,               opts(\"Copy Basename\"))\n  vim.keymap.set(\"n\",          \"H\",              api.filter.dotfiles.toggle,         opts(\"Toggle Filter: Dotfiles\"))\n  vim.keymap.set(\"n\",          \"I\",              api.filter.git.ignored.toggle,      opts(\"Toggle Filter: Git Ignored\"))\n  vim.keymap.set(\"n\",          \"J\",              api.node.navigate.sibling.last,     opts(\"Last Sibling\"))\n  vim.keymap.set(\"n\",          \"K\",              api.node.navigate.sibling.first,    opts(\"First Sibling\"))\n  vim.keymap.set(\"n\",          \"L\",              api.node.open.toggle_group_empty,   opts(\"Toggle Group Empty\"))\n  vim.keymap.set(\"n\",          \"M\",              api.filter.no_bookmark.toggle,      opts(\"Toggle Filter: No Bookmark\"))\n  vim.keymap.set({ \"n\", \"x\" }, \"m\",              api.marks.toggle,                   opts(\"Toggle Bookmark\"))\n  vim.keymap.set(\"n\",          \"o\",              api.node.open.edit,                 opts(\"Open\"))\n  vim.keymap.set(\"n\",          \"O\",              api.node.open.no_window_picker,     opts(\"Open: No Window Picker\"))\n  vim.keymap.set(\"n\",          \"p\",              api.fs.paste,                       opts(\"Paste\"))\n  vim.keymap.set(\"n\",          \"P\",              api.node.navigate.parent,           opts(\"Parent Directory\"))\n  vim.keymap.set(\"n\",          \"q\",              api.tree.close,                     opts(\"Close\"))\n  vim.keymap.set(\"n\",          \"r\",              api.fs.rename,                      opts(\"Rename\"))\n  vim.keymap.set(\"n\",          \"R\",              api.tree.reload,                    opts(\"Refresh\"))\n  vim.keymap.set(\"n\",          \"s\",              api.node.run.system,                opts(\"Run System\"))\n  vim.keymap.set(\"n\",          \"S\",              api.tree.search_node,               opts(\"Search\"))\n  vim.keymap.set(\"n\",          \"u\",              api.fs.rename_full,                 opts(\"Rename: Full Path\"))\n  vim.keymap.set(\"n\",          \"U\",              api.filter.custom.toggle,           opts(\"Toggle Filter: Custom\"))\n  vim.keymap.set(\"n\",          \"W\",              api.tree.collapse_all,              opts(\"Collapse All\"))\n  vim.keymap.set({ \"n\", \"x\" }, \"x\",              api.fs.cut,                         opts(\"Cut\"))\n  vim.keymap.set(\"n\",          \"y\",              api.fs.copy.filename,               opts(\"Copy Name\"))\n  vim.keymap.set(\"n\",          \"Y\",              api.fs.copy.relative_path,          opts(\"Copy Relative Path\"))\n  vim.keymap.set(\"n\",          \"<2-LeftMouse>\",  api.node.open.edit,                 opts(\"Open\"))\n  vim.keymap.set(\"n\",          \"<2-RightMouse>\", api.tree.change_root_to_node,       opts(\"CD\"))\n  -- END_ON_ATTACH_DEFAULT\n<\nAlternatively, you may apply these default mappings from your\n|nvim_tree.config| {on_attach} via |nvim_tree.api.map.on_attach.default()| e.g. >lua\n\n  local function my_on_attach(bufnr)\n    local api = require(\"nvim-tree.api\")\n\n    local function opts(desc)\n      return { desc = \"nvim-tree: \" .. desc, buffer = bufnr, noremap = true, silent = true, nowait = true }\n    end\n\n    api.map.on_attach.default(bufnr)\n\n    -- your removals and mappings go here\n  end\n<\n\n==============================================================================\nIcons And Highlighting                          *nvim-tree-icons-highlighting*\n\nIcons may be displayed before files and directories.\n\nAdditional icons and highlighting may be displayed to indicate various states\nfor files and and directories. Highlighting is additive.\n\nDecorators are responsible for providing the icons and highlighting. They\napply additively in order of precedence.\n\n`DECORATOR`\n  See |nvim_tree.config.renderer.decorator| for available decorators and their\n  default precedence.\n\n`ICON?`\n  Enable via |nvim_tree.config.renderer.icons.show\n\n`REQUIRES`\n  Features that must be enabled to show icons and highlighting.\n\n`PLACEMENT`\n  Where to place icons: |nvim_tree.config.renderer.icons.placement|\n\n`HIGHLIGHT`\n  What should be highlighted: |nvim_tree.config.renderer.highlight|\n\n`DEVICONS?`\n  Glyphs and their colors will be overridden by optional plugin:\n  `nvim-tree/nvim-web-devicons`\n  See |nvim_tree.config.renderer.icons.web_devicons|\n\n`GLYPHS`\n  Icon glyphs definitions.\n\n`GROUPS`\n  Applicable highlight groups: |nvim-tree-highlight-groups|\n\nSome defaults noted. In ascending order of default decorator additive\nprecedence:\n\n`WHAT          DECORATOR     ICON?            REQUIRES                      PLACEMENT                             HIGHLIGHT                        GLYPHS                                              DEVICONS?  GROUPS`\nFile Icon      -            {file}       Y    -                             -                                     -                               |nvim_tree.config.renderer.icons.glyphs| {default}    Y          `NvimTreeNormal`, `NvimTreeFileIcon`\nFolder Icon    -            {folder}     Y    -                             -                                     -                               |nvim_tree.config.renderer.icons.glyphs.folder|       Y          `NvimTree*FolderName`, `NvimTree*FolderIcon`\nGit Status    `\"Git\"`         {git}        Y   |nvim_tree.config.git|          {git_placement}        `\"before\"`       {highlight_git}         `\"none\"`   |nvim_tree.config.renderer.icons.glyphs.git|          N          `NvimTreeGit*`\n|bufloaded()|   `\"Open\"`         -                -                             -                                    {highlight_opened_files}`\"none\"`    -                                                  N         ` NvimTreeOpened*`\nDotfiles      `\"Hidden\"`      {hidden}     N    -                            {hidden_placement}     `\"after\"`        {highlight_hidden}      `\"none\"`   |nvim_tree.config.renderer.icons.glyphs| {hidden}     N          `NvimTreeHidden*`\n|'modified'|    `\"Modified\"`    {modified}   Y   |nvim_tree.config.modified|     {modified_placement}   `\"after\"`        {highlight_modified}    `\"none\"`   |nvim_tree.config.renderer.icons.glyphs| {modified}   N          `NvimTreeModified*`\nBookmarked    `\"Bookmark\"`    {bookmarks}  Y    -                            {bookmarks_placement}  `\"signcolumn\"`   {highlight_bookmarks}   `\"none\"`   |nvim_tree.config.renderer.icons.glyphs| {bookmark}   N          `NvimTreeBookmark*`\nDiag Status  ` \"Diagnostics\"` {diagnostics}Y   |nvim_tree.config.diagnostics|  {diagnostics_placement}`\"signcolumn\"`   {highlight_diagnostics} `\"none\" `  |nvim_tree.config.diagnostics.icons|                  N          `NvimTreeDiagnostic*`\nCopied        `\"Copied\"`       -                -                             -                                    {highlight_clipboard}   `\"name\"`    -                                                  N          `NvimTreeCopiedHL`\nCut           `\"Cut\"`          -                -                             -                                    {highlight_clipboard}   `\"name\"`    -                                                  N          `NvimTreeCutHL`\n\n\n==============================================================================\nHighlight Groups                                  *nvim-tree-highlight-groups*\n\nAll the following highlight groups can be configured by hand. Aside from\n`NvimTreeWindowPicker`, it is not advised to colorize the background of these\ngroups.\n\nExample |:highlight| >vim\n    :hi NvimTreeSymlink guifg=blue gui=bold,underline\n<\nIt is recommended to enable |'termguicolors'| for the more pleasant 24-bit\ncolours.\n\nTo view the nvim-tree highlight groups run |:NvimTreeHiTest|\n\nTo view all active highlight groups run `:so $VIMRUNTIME/syntax/hitest.vim`\nas per |:highlight|\n\nThe `*HL` groups are additive, following |nvim_tree.config.renderer.decorator|\nprecedence.\nOnly present attributes will clobber each other.\nIn this example a modified, opened file will have magenta text, with cyan\nundercurl: >vim\n    :hi NvimTreeOpenedHL       guifg=magenta guisp=red  gui=underline\n    :hi NvimTreeModifiedFileHL               guisp=cyan gui=undercurl\n<\nTo prevent usage of a highlight:\n\n- Before setup: link the group to `Normal` e.g. >vim\n    :hi NvimTreeExecFile Normal\n<\n- After setup: link it to `NONE`, to override the default link e.g. >lua\n    :hi! link NvimTreeExecFile NONE\n<\n==============================================================================\nHighlight Groups: Default                 *nvim-tree-highlight-groups-default*\n\n|:highlight-link| `default` or |:highlight-default| define the groups on setup:\n\nStandard: >\n    NvimTreeNormal              Normal\n    NvimTreeNormalFloat         NormalFloat\n    NvimTreeNormalNC            NormalFloat\n\n    NvimTreeLineNr              LineNr\n    NvimTreeWinSeparator        WinSeparator\n    NvimTreeEndOfBuffer         EndOfBuffer\n    NvimTreePopup               Normal\n    NvimTreeSignColumn          NvimTreeNormal\n\n    NvimTreeCursorColumn        CursorColumn\n    NvimTreeCursorLine          CursorLine\n    NvimTreeCursorLineNr        CursorLineNr\n\n    NvimTreeStatusLine          StatusLine\n    NvimTreeStatusLineNC        StatusLineNC\n<\nFile Text: >\n    NvimTreeExecFile            SpellCap\n    NvimTreeImageFile           SpellCap\n    NvimTreeSpecialFile         SpellCap\n    NvimTreeSymlink             SpellCap\n<\nFolder Text: >\n    NvimTreeRootFolder          Title\n    NvimTreeFolderName          Directory\n    NvimTreeEmptyFolderName     Directory\n    NvimTreeOpenedFolderName    Directory\n    NvimTreeSymlinkFolderName   Directory\n<\nFile Icons: >\n    NvimTreeFileIcon            NvimTreeNormal\n    NvimTreeSymlinkIcon         NvimTreeNormal\n<\nFolder Icons: >\n    NvimTreeFolderIcon          guifg=#8094b4 ctermfg=Blue\n    NvimTreeOpenedFolderIcon    NvimTreeFolderIcon\n    NvimTreeClosedFolderIcon    NvimTreeFolderIcon\n    NvimTreeFolderArrowClosed   NvimTreeIndentMarker\n    NvimTreeFolderArrowOpen     NvimTreeIndentMarker\n<\nIndent: >\n    NvimTreeIndentMarker        NvimTreeFileIcon\n<\nPicker: >\n    NvimTreeWindowPicker        guifg=#ededed guibg=#4493c8 gui=bold ctermfg=White ctermbg=Cyan\n<\nLive Filter: >\n    NvimTreeLiveFilterPrefix    PreProc\n    NvimTreeLiveFilterValue     ModeMsg\n<\nClipboard: >\n    NvimTreeCopiedHL            SpellRare\n    NvimTreeCutHL               SpellBad\n<\nBookmarks: >\n    NvimTreeBookmarkIcon        NvimTreeFolderIcon\n    NvimTreeBookmarkHL          SpellLocal\n<\nModified: >\n    NvimTreeModifiedIcon        Type\n    NvimTreeModifiedFileHL      NvimTreeModifiedIcon\n    NvimTreeModifiedFolderHL    NvimTreeModifiedIcon\n\nHidden: >\n    NvimTreeModifiedIcon        Conceal\n    NvimTreeModifiedFileHL      NvimTreeHiddenIcon\n    NvimTreeModifiedFolderHL    NvimTreeHiddenFileHL\n<\nHidden Display: >\n    NvimTreeHiddenDisplay       Conceal\n<\nOpened: >\n    NvimTreeOpenedHL            Special\n<\nGit Icon: >\n    NvimTreeGitDeletedIcon      Statement\n    NvimTreeGitDirtyIcon        Statement\n    NvimTreeGitIgnoredIcon      Comment\n    NvimTreeGitMergeIcon        Constant\n    NvimTreeGitNewIcon          PreProc\n    NvimTreeGitRenamedIcon      PreProc\n    NvimTreeGitStagedIcon       Constant\n<\nGit File File Highlight: >\n    NvimTreeGitFileDeletedHL    NvimTreeGitDeletedIcon\n    NvimTreeGitFileDirtyHL      NvimTreeGitDirtyIcon\n    NvimTreeGitFileIgnoredHL    NvimTreeGitIgnoredIcon\n    NvimTreeGitFileMergeHL      NvimTreeGitMergeIcon\n    NvimTreeGitFileNewHL        NvimTreeGitNewIcon\n    NvimTreeGitFileRenamedHL    NvimTreeGitRenamedIcon\n    NvimTreeGitFileStagedHL     NvimTreeGitStagedIcon\n<\nGit Folder Folder Highlight: >\n    NvimTreeGitFolderDeletedHL  NvimTreeGitFileDeletedHL\n    NvimTreeGitFolderDirtyHL    NvimTreeGitFileDirtyHL\n    NvimTreeGitFolderIgnoredHL  NvimTreeGitFileIgnoredHL\n    NvimTreeGitFolderMergeHL    NvimTreeGitFileMergeHL\n    NvimTreeGitFolderNewHL      NvimTreeGitFileNewHL\n    NvimTreeGitFolderRenamedHL  NvimTreeGitFileRenamedHL\n    NvimTreeGitFolderStagedHL   NvimTreeGitFileStagedHL\n<\nDiagnostics Icon: >\n    NvimTreeDiagnosticErrorIcon         DiagnosticError\n    NvimTreeDiagnosticWarnIcon          DiagnosticWarn\n    NvimTreeDiagnosticInfoIcon          DiagnosticInfo\n    NvimTreeDiagnosticHintIcon          DiagnosticHint\n<\nDiagnostics File Highlight: >\n    NvimTreeDiagnosticErrorFileHL       DiagnosticUnderlineError\n    NvimTreeDiagnosticWarnFileHL        DiagnosticUnderlineWarn\n    NvimTreeDiagnosticInfoFileHL        DiagnosticUnderlineInfo\n    NvimTreeDiagnosticHintFileHL        DiagnosticUnderlineHint\n<\nDiagnostics Folder Highlight: >\n    NvimTreeDiagnosticErrorFolderHL     NvimTreeDiagnosticErrorFileHL\n    NvimTreeDiagnosticWarnFolderHL      NvimTreeDiagnosticWarnFileHL\n    NvimTreeDiagnosticInfoFolderHL      NvimTreeDiagnosticInfoFileHL\n    NvimTreeDiagnosticHintFolderHL      NvimTreeDiagnosticHintFileHL\n<\n==============================================================================\nEvents                                                      *nvim-tree-events*\n\nnvim-tree will dispatch events whenever an action is made. These events can be\nsubscribed to through handler functions. This allows for even further\ncustomization of nvim-tree.\n\nA handler for an event is just a function which receives one argument, the\npayload of the event. The payload is different for each event type. Refer\nto |nvim_tree_registering_handlers| for more information.\n\n*nvim_tree_registering_handlers*\n\nHandlers are registered by calling |nvim_tree.api.events.subscribe()| function\nwith an |nvim_tree_events_kind|.\n\ne.g. handler for node renamed: >lua\n\n    local api = require(\"nvim-tree.api\")\n    local Event = api.events.Event\n\n    api.events.subscribe(Event.NodeRenamed, function(data)\n      print(\"Node renamed from \" .. data.old_name .. \" to \" ..  data.new_name)\n    end)\n<\n*nvim_tree_events_kind*\n\n- Event.Ready\n                When NvimTree has been initialized.\n                • Note: Handler takes no parameter.\n\n- Event.TreePreOpen\n                Invoked before the window and buffer for NvimTree are created\n                or opened. Before `Event.TreeOpen`\n                • Note: Handler takes no parameter.\n\n- Event.TreeOpen\n                Invoked after the NvimTree is opened.\n                • Note: Handler takes no parameter.\n\n- Event.TreeClose\n                Invoked after the NvimTree is closed, but before the window is\n                closed. Dispatched on |WinClosed| event for NvimTree window.\n                • Note: Handler takes no parameter.\n\n- Event.Resize - When NvimTree is resized.\n                handler parameters: ~\n                    size:        `number` size of the view in columns.\n\n- Event.WillRenameNode\n                • Note: A node can either be a file or a directory.\n                handler parameters: ~\n                  {old_name}     `{string}` Absolute path to the old node location.\n                  {new_name}     `{string}` Absolute path to the new node location.\n\n- Event.NodeRenamed\n                • Note: A node can either be a file or a directory.\n                handler parameters: ~\n                  {old_name}     `{string}` Absolute path to the old node location.\n                  {new_name}     `{string}` Absolute path to the new node location.\n\n- Event.FileCreated\n                handler parameters: ~\n                  {fname}        `{string}` Absolute path to the created file\n\n- Event.WillCreateFile\n                handler parameters: ~\n                  {fname}        `{string}` Absolute path to the file to be\n                  created\n\n- Event.FileRemoved\n                handler parameters: ~\n                  {fname}        `{string}` Absolute path to the removed file.\n\n- Event.WillRemoveFile\n                handler parameters: ~\n                  {fname}        `{string}` Absolute path to the file to be\n                  removed\n\n- Event.FolderCreated\n                handler parameters: ~\n                  {folder_name}  `{string}` Absolute path to the created folder.\n\n- Event.FolderRemoved\n                handler parameters: ~\n                  {folder_name}  `{string}` Absolute path to the removed folder.\n\n- Event.TreeAttachedPost\n                Invoked after the tree's buffer has been created and mappings\n                have been applied: |nvim-tree-mappings| or |nvim_tree.config| {on_attach}\n                handler parameters: ~\n                  {buf}          `{number} `API buffer handle (buffer number)\n\n- Event.TreeRendered\n                Invoked every time the tree is redrawn. Normally this event\n                happens after `Event.TreeOpen` except that handlers of this\n                one will have access to the tree buffer populated with the\n                final content.\n                handler parameters: ~\n                  {bufnr}          `{number} `API buffer handle (buffer number)\n                  {winnr}          `{number} `API window handle (window number)\n\n*nvim_tree_events_startup*\n\nThere are two special startup events in the form of User autocommands:\n\n`NvimTreeRequired`      first `require(\"nvim-tree\")`\n`NvimTreeSetup`         `setup({})` completed\n\nImmediately before firing: a global variable of the same name will be set to a\nvalue of 1.\n\nExample subscription: >lua\n\n  vim.api.nvim_create_autocmd(\"User\", {\n    pattern = \"NvimTreeRequired\",\n    callback = function(data)\n      ---\n    end,\n  })\n<\n==============================================================================\nPrompts                                                    *nvim-tree-prompts*\n\nSome NvimTree actions use the builtin |vim.ui.select()| prompt API for\nconfirmations when the |nvim_tree.config| {select_prompts} option is set.\n\nThe API accepts the optional `kind` key as part of the {opts} parameter, which\ncan can be used to identify the type of prompt, to allow user side\nconfigurations for different types of prompts.\n\n- `nvimtree_overwrite_rename`\n    overwrite or rename during |nvim_tree.api.fs.paste()|\n\n- `nvimtree_remove`\n    delete during |nvim_tree.api.fs.remove()|\n\n- `nvimtree_trash`\n    send to trash during |nvim_tree.api.fs.trash()|\n\n- `nvimtree_bulk_delete`\n    delete all bookmarked during |nvim_tree.api.marks.bulk.delete()|\n\n- `nvimtree_bulk_trash`\n    send all bookmarked to trash during |nvim_tree.api.marks.bulk.trash()|\n\n==============================================================================\nOS Specific Restrictions                               *nvim-tree-os-specific*\n\nWindows WSL and PowerShell\n- Trash is synchronized\n- Executable file detection is disabled as this is non-performant and can\n  freeze Nvim\n- Some filesystem watcher error related to permissions will not be reported\n\nPowershell\n- Observed Nvim hanging after a runaway (infinite) number of events on a\n  single directory. See |nvim_tree.config.filesystem_watchers| {max_events}\n\n==============================================================================\nnetrw                                                        *nvim-tree-netrw*\n\n|netrw| is a standard Nvim plugin that is enabled by default. It provides,\namongst other functionality, a file/directory browser.\n\nIt interferes with nvim-tree and the intended user experience is nvim-tree\nreplacing the |netrw| browser.\n\nIt is strongly recommended to disable |netrw|. As it is a bundled plugin it\nmust be disabled manually at the start of your `init.lua` as per |netrw-noload|: >lua\n\n  vim.g.loaded_netrw       = 1\n  vim.g.loaded_netrwPlugin = 1\n<\nThere are many |netrw| features beyond the file browser. If you want to\nkeep using |netrw| without its browser features please ensure:\n\n|nvim_tree.config| {disable_netrw}` = false`\n|nvim_tree.config| {hijack_netrw}`  = true`\n\n==============================================================================\nLegacy                                                      *nvim-tree-legacy*\n\nBackwards compatible, silent refactors have been done.\n\n==============================================================================\nLegacy: Config                                       *nvim-tree-legacy-config*\n\nLegacy config is translated to the current, making type and value changes as\nneeded.\n\n`update_cwd`                         |nvim_tree.config| {sync_root_with_cwd}\n`update_focused_file.update_cwd`     |nvim_tree.config.update_focused_file| {update_root}\n`open_on_tab`                        |nvim_tree.config.tab.sync| {open}\n`ignore_buf_on_tab_change`           |nvim_tree.config.tab.sync| {ignore}\n`renderer.root_folder_modifier`      |nvim_tree.config.renderer| {root_folder_label}\n`update_focused_file.debounce_delay` |nvim_tree.config.view| {debounce_delay}\n`trash.require_confirm`              |nvim_tree.config.ui.confirm| {trash}\n`view.adaptive_size`                 |nvim_tree.config.view| {width}\n`sort_by`                            |nvim_tree.config.sort| {sorter}\n`git.ignore`                         |nvim_tree.config.filters| {git_ignored}\n`renderer.icons.webdev_colors`       |nvim_tree.config.renderer.icons.web_devicons.file| {color}\n`renderer.icons.padding`             |nvim_tree.config.renderer.icons.padding| {icon}\n\n==============================================================================\nLegacy: API                                             *nvim-tree-legacy-api*\n\nDeprecated API with unchanged function signature:\n\n`api.config.mappings.get_keymap`         |nvim_tree.api.map.keymap.current()|\n`api.config.mappings.get_keymap_default` |nvim_tree.api.map.keymap.default()|\n`api.config.mappings.default_on_attach`  |nvim_tree.api.map.on_attach.default()|\n\n`api.diagnostics.hi_test`                |nvim_tree.api.appearance.hi_test()|\n\n`api.live_filter.start`                  |nvim_tree.api.filter.live.start()|\n`api.live_filter.clear`                  |nvim_tree.api.filter.live.clear()|\n\n`api.tree.toggle_enable_filters`         |nvim_tree.api.filter.toggle()|\n`api.tree.toggle_gitignore_filter`       |nvim_tree.api.filter.git.ignored.toggle()|\n`api.tree.toggle_git_clean_filter`       |nvim_tree.api.filter.git.clean.toggle()|\n`api.tree.toggle_no_buffer_filter`       |nvim_tree.api.filter.no_buffer.toggle()|\n`api.tree.toggle_custom_filter`          |nvim_tree.api.filter.custom.toggle()|\n`api.tree.toggle_hidden_filter`          |nvim_tree.api.filter.dotfiles.toggle()|\n`api.tree.toggle_no_bookmark_filter`     |nvim_tree.api.filter.no_bookmark.toggle()|\n\n==============================================================================\nLegacy: Highlight                                 *nvim-tree-legacy-highlight*\n\nLegacy highlight group are still obeyed when they are defined and the current\nhighlight group is not, hard linking as follows: >\n\n    NvimTreeModifiedIcon        NvimTreeModifiedFile\n    NvimTreeOpenedHL            NvimTreeOpenedFile\n    NvimTreeBookmarkIcon        NvimTreeBookmark\n\n    NvimTreeGitDeletedIcon      NvimTreeGitDeleted\n    NvimTreeGitDirtyIcon        NvimTreeGitDirty\n    NvimTreeGitIgnoredIcon      NvimTreeGitIgnored\n    NvimTreeGitMergeIcon        NvimTreeGitMerge\n    NvimTreeGitNewIcon          NvimTreeGitNew\n    NvimTreeGitRenamedIcon      NvimTreeGitRenamed\n    NvimTreeGitStagedIcon       NvimTreeGitStaged\n\n    NvimTreeGitFileDeletedHL    NvimTreeFileDeleted\n    NvimTreeGitFileDirtyHL      NvimTreeFileDirty\n    NvimTreeGitFileIgnoredHL    NvimTreeFileIgnored\n    NvimTreeGitFileMergeHL      NvimTreeFileMerge\n    NvimTreeGitFileNewHL        NvimTreeFileNew\n    NvimTreeGitFileRenamedHL    NvimTreeFileRenamed\n    NvimTreeGitFileStagedHL     NvimTreeFileStaged\n\n    NvimTreeGitFolderDeletedHL  NvimTreeFolderDeleted\n    NvimTreeGitFolderDirtyHL    NvimTreeFolderDirty\n    NvimTreeGitFolderIgnoredHL  NvimTreeFolderIgnored\n    NvimTreeGitFolderMergeHL    NvimTreeFolderMerge\n    NvimTreeGitFolderNewHL      NvimTreeFolderNew\n    NvimTreeGitFolderRenamedHL  NvimTreeFolderRenamed\n    NvimTreeGitFolderStagedHL   NvimTreeFolderStaged\n\n    NvimTreeLspDiagnosticsError                 NvimTreeDiagnosticErrorIcon\n    NvimTreeLspDiagnosticsWarning               NvimTreeDiagnosticWarnIcon\n    NvimTreeLspDiagnosticsInformation           NvimTreeDiagnosticInfoIcon\n    NvimTreeLspDiagnosticsHint                  NvimTreeDiagnosticHintIcon\n\n    NvimTreeLspDiagnosticsErrorText             NvimTreeDiagnosticErrorFileHL\n    NvimTreeLspDiagnosticsWarningText           NvimTreeDiagnosticWarnFileHL\n    NvimTreeLspDiagnosticsInformationText       NvimTreeDiagnosticInfoFileHL\n    NvimTreeLspDiagnosticsHintText              NvimTreeDiagnosticHintFileHL\n\n    NvimTreeLspDiagnosticsErrorFolderText       NvimTreeDiagnosticErrorFolderHL\n    NvimTreeLspDiagnosticsWarningFolderText     NvimTreeDiagnosticWarnFolderHL\n    NvimTreeLspDiagnosticsInformationFolderText NvimTreeDiagnosticInfoFolderHL\n    NvimTreeLspDiagnosticsHintFolderText        NvimTreeDiagnosticHintFolderHL\n<\n\n\n==============================================================================\nConfig                                                      *nvim-tree-config*\n\n*nvim_tree.config*\n    Arguments to pass to |nvim-tree-setup|.\n\n    When a value is not present/nil, the default will be used.\n\n    {on_attach} Runs when creating the nvim-tree buffer. Use this to set your\n    |nvim-tree-mappings|. When not a function, |nvim-tree-mappings-default|\n    will be used.\n\n    {hijack_cursor} keep the cursor on the first letter of the filename when\n    moving in the tree.\n\n    {auto_reload_on_write} reload the explorer every time a buffer is written\n    to.\n\n    {disable_netrw} completely disables |netrw|, see |nvim-tree-netrw| for\n    details. It is strongly advised to eagerly disable netrw, due to race\n    conditions at vim startup.\n\n    {hijack_netrw} hijacks netrw windows, ignored when {disable_netrw}.\n\n    {hijack_unnamed_buffer_when_opening} opens in place of the unnamed buffer\n    if it's empty.\n\n    {root_dirs} preferred root directories, requires\n    |nvim_tree.config.update_focused_file.update_root|.\n\n    {prefer_startup_root} prefer startup root directory when updating root\n    directory of the tree. Requires\n    |nvim_tree.config.update_focused_file.update_root|.\n\n    {sync_root_with_cwd} changes the tree root directory on |DirChanged| and\n    refreshes the tree.\n\n    {reload_on_bufenter} automatically reloads the tree on |BufEnter|\n    nvim-tree.\n\n    {respect_buf_cwd} changes the |current-directory| of nvim-tree to that of\n    new buffer's when opening nvim-tree.\n\n    {select_prompts} uses |vim.ui.select()| style prompts. Necessary when\n    using a UI prompt decorator such as dressing.nvim or\n    telescope-ui-select.nvim\n\n    Fields: ~\n      • {on_attach}?                           (`\"default\"|(fun(bufnr: integer))`)\n                                               (default: `default`)\n      • {hijack_cursor}?                       (`boolean`) (default: `false`)\n      • {auto_reload_on_write}?                (`boolean`) (default: `true`)\n      • {disable_netrw}?                       (`boolean`) (default: `false`)\n      • {hijack_netrw}?                        (`boolean`) (default: `true`)\n      • {hijack_unnamed_buffer_when_opening}?  (`boolean`) (default: `false`)\n      • {root_dirs}?                           (`string[]`) (default: `{}`)\n      • {prefer_startup_root}?                 (`boolean`) (default: `false`)\n      • {sync_root_with_cwd}?                  (`boolean`) (default: `false`)\n      • {reload_on_bufenter}?                  (`boolean`) (default: `false`)\n      • {respect_buf_cwd}?                     (`boolean`) (default: `false`)\n      • {select_prompts}?                      (`boolean`) (default: `false`)\n      • {sort}?                                (`nvim_tree.config.sort`)\n                                               |nvim_tree.config.sort|\n      • {view}?                                (`nvim_tree.config.view`)\n                                               |nvim_tree.config.view|\n      • {renderer}?                            (`nvim_tree.config.renderer`)\n                                               |nvim_tree.config.renderer|\n      • {hijack_directories}?                  (`nvim_tree.config.hijack_directories`)\n                                               |nvim_tree.config.hijack_directories|\n      • {update_focused_file}?                 (`nvim_tree.config.update_focused_file`)\n                                               |nvim_tree.config.update_focused_file|\n      • {system_open}?                         (`nvim_tree.config.system_open`)\n                                               |nvim_tree.config.system_open|\n      • {git}?                                 (`nvim_tree.config.git`)\n                                               |nvim_tree.config.git|\n      • {diagnostics}?                         (`nvim_tree.config.diagnostics`)\n                                               |nvim_tree.config.diagnostics|\n      • {modified}?                            (`nvim_tree.config.modified`)\n                                               |nvim_tree.config.modified|\n      • {filters}?                             (`nvim_tree.config.filters`)\n                                               |nvim_tree.config.filters|\n      • {live_filter}?                         (`nvim_tree.config.live_filter`)\n                                               |nvim_tree.config.live_filter|\n      • {filesystem_watchers}?                 (`nvim_tree.config.filesystem_watchers`)\n                                               |nvim_tree.config.filesystem_watchers|\n      • {actions}?                             (`nvim_tree.config.actions`)\n                                               |nvim_tree.config.actions|\n      • {trash}?                               (`nvim_tree.config.trash`)\n                                               |nvim_tree.config.trash|\n      • {tab}?                                 (`nvim_tree.config.tab`)\n                                               |nvim_tree.config.tab|\n      • {bookmarks}?                           (`nvim_tree.config.bookmarks`)\n                                               |nvim_tree.config.bookmarks|\n      • {notify}?                              (`nvim_tree.config.notify`)\n                                               |nvim_tree.config.notify|\n      • {help}?                                (`nvim_tree.config.help`)\n                                               |nvim_tree.config.help|\n      • {ui}?                                  (`nvim_tree.config.ui`)\n                                               |nvim_tree.config.ui|\n      • {experimental}?                        (`nvim_tree.config.experimental`)\n                                               |nvim_tree.config.experimental|\n      • {log}?                                 (`nvim_tree.config.log`)\n                                               |nvim_tree.config.log|\n\n\n\n==============================================================================\nConfig: sort                                           *nvim-tree-config-sort*\n\n*nvim_tree.config.sort*\n    Sort files within a directory.\n\n    {sorter} presets                            *nvim_tree.config.sort.Sorter*\n    • `\"name\"`\n    • `\"case_sensitive\"` name\n    • `\"modification_time\"`\n    • `\"extension\"` uses all suffixes e.g. `foo.tar.gz` -> `.tar.gz`\n    • `\"suffix\"` uses the last e.g. `foo.tar.gz` -> `.gz`\n    • `\"filetype\"` |filetype|\n\n    {sorter} may be a function that is passed a list of `nvim_tree.api.Node`\n    to be sorted in place e.g. >lua\n\n        ---Sort by name length\n        ---@param nodes nvim_tree.api.Node[]\n        ---@return nvim_tree.config.sort.Sorter?\n        local sorter = function(nodes)\n          table.sort(nodes, function(a, b)\n            return #a.name < #b.name\n          end)\n        end\n<\n\n    {sorter} may be a function that returns a |nvim_tree.config.sort.Sorter|\n\n    Fields: ~\n      • {sorter}?         (`nvim_tree.config.sort.Sorter|(fun(nodes: nvim_tree.api.Node[]): nvim_tree.config.sort.Sorter?)`)\n                          (default: `\"name\"`)\n      • {folders_first}?  (`boolean`, default: `true`) Sort folders before\n                          files. Has no effect when {sorter} is a function.\n      • {files_first}?    (`boolean`, default: `false`) Sort files before\n                          folders. Has no effect when {sorter} is a function.\n                          Overrides {folders_first}.\n\n\n\n==============================================================================\nConfig: view                                           *nvim-tree-config-view*\n\n*nvim_tree.config.view*\n    Configures the dimensions and appearance of the nvim-tree window.\n\n    The window is \"docked\" at the left by default, however may be configured\n    to float: |nvim_tree.config.view.float|\n\n    {width} can be a |nvim_tree.config.view.width.spec| for static control or\n    a |nvim_tree.config.view.width| for fully dynamic control based on longest\n    line.\n\n                                            *nvim_tree.config.view.width.spec*\n    • `string`: `x%` string e.g. `30%`\n    • `integer`: number of columns\n    • `function`: returns one of the above\n\n    Fields: ~\n      • {centralize_selection}?         (`boolean`, default: `false`) When\n                                        entering nvim-tree, reposition the\n                                        view so that the current node is\n                                        initially centralized, see |zz|.\n      • {cursorline}?                   (`boolean`, default: `true`)\n                                        |'cursorline'|\n      • {cursorlineopt}?                (`string`, default: `both`)\n                                        |'cursorlineopt'|\n      • {debounce_delay}?               (`integer`, default: `15`) Idle\n                                        milliseconds before some reload /\n                                        refresh operations. Increase if you\n                                        experience performance issues around\n                                        screen refresh.\n      • {side}?                         (`\"left\"|\"right\"`) (default: `\"left\"`)\n      • {preserve_window_proportions}?  (`boolean`, default: `false`)\n                                        Preserves window proportions when\n                                        opening a file. If `false`, the height\n                                        and width of windows other than\n                                        nvim-tree will be equalized.\n      • {number}?                       (`boolean`, default: `false`)\n                                        |'number'|\n      • {relativenumber}?               (`boolean`, default: `false`)\n                                        |'relativenumber'|\n      • {signcolumn}?                   (`\"yes\"|\"auto\"|\"no\"`, default:\n                                        `\"yes\"`) |'signcolumn'|\n      • {width}?                        (`nvim_tree.config.view.width.spec|nvim_tree.config.view.width`)\n                                        (default: `30`)\n      • {float}?                        (`nvim_tree.config.view.float`)\n                                        |nvim_tree.config.view.float|\n\n*nvim_tree.config.view.float*\n    Configure floating window behaviour\n\n    {open_win_config} is passed to |nvim_open_win()|, default: >lua\n        {\n          relative = \"editor\",\n          border = \"rounded\",\n          width = 30,\n          height = 30,\n          row = 1,\n          col = 1,\n        }\n<\n\n    Fields: ~\n      • {enable}?              (`boolean`) (default: `false`)\n      • {quit_on_focus_loss}?  (`boolean`, default: `true`) Close the floating\n                               window when it loses focus.\n      • {open_win_config}?     (`vim.api.keyset.win_config|(fun(): vim.api.keyset.win_config)`)\n                               (default:\n                               `{ relative = \"editor\", border = \"rounded\", width = 30, height = 30, row = 1, col = 1, }`)\n\n*nvim_tree.config.view.width*\n    Configure dynamic width based on longest line.\n\n    Fields: ~\n      • {min}?             (`nvim_tree.config.view.width.spec`) (default:\n                           `30`)\n      • {max}?             (`nvim_tree.config.view.width.spec`, default: `-1`)\n                           -1 for unbounded.\n      • {lines_excluded}?  (`(\"root\")[]`, default: `{ \"root\" }`) Exclude these\n                           lines when computing width.\n      • {padding}?         (`nvim_tree.config.view.width.spec`, default: `1`)\n                           Extra padding to the right.\n\n\n\n==============================================================================\nConfig: renderer                                   *nvim-tree-config-renderer*\n\n*nvim_tree.config.renderer*\n    Controls the appearance of the tree.\n\n    {highlight_}                         *nvim_tree.config.renderer.highlight*\n\n    See |nvim-tree-icons-highlighting|\n    • `\"none\"`: no highlighting\n    • `\"icon\"`: icon only\n    • `\"name\"`: name only\n    • `\"all\"`: icon and name\n\n    {decorators}                         *nvim_tree.config.renderer.decorator*\n\n    See |nvim-tree-icons-highlighting|\n\n    A builtin decorator name `string` or |nvim_tree.api.Decorator| class.\n\n    Builtin decorators in their default order:\n    • `\"Git\"`\n    • `\"Open\"`\n    • `\"Hidden\"`\n    • `\"Modified\"`\n    • `\"Bookmark\"`\n    • `\"Diagnostics\"`\n    • `\"Copied\"`\n    • `\"Cut\"`\n\n    Specify {decorators} is a list e.g. `{ \"Git\", MyDecorator, \"Cut\" }`\n\n    {root_folder_label}          *nvim_tree.config.renderer.root_folder_label*\n\n    Controls the root folder name and visibility:\n    • `string`: |filename-modifiers| format string, default `\":~:s?$?/..?\"`\n    • `false`: to disable\n    • `fun(root_cwd: string): string`: return a literal string from root's\n      absolute path e.g. >lua\n        my_root_folder_label = function(path)\n          return \".../\" .. vim.fn.fnamemodify(path, \":t\")\n        end\n<\n\n    {hidden_display}                *nvim_tree.config.renderer.hidden_display*\n\n    Summary of hidden nodes, below the last node in the directory, highlighted\n    with `NvimTreeHiddenDisplay`.\n    • `\"none\"`: disabled, default\n    • `\"simple\"`: total number of hidden files e.g.\n      • (3 hidden)\n    • `\"all\"`: total and by reason: the filter that hid the node e.g.\n      • (14 total git: 5, dotfile: 9)\n    • `(fun(hidden_stats: nvim_tree.config.renderer.hidden_stats): string)`\n\n    See |nvim_tree.config.renderer.hidden_stats| for details and example.\n\n    Fields: ~\n      • {add_trailing}?            (`boolean`, default: `false`) Appends a\n                                   trailing slash to folder and symlink folder\n                                   target names.\n      • {group_empty}?             (`boolean|(fun(relative_path: string): string)`, default: `false`)\n                                   Compact folders that only contain a single\n                                   folder into one node. Function variant\n                                   takes the relative path of grouped folders\n                                   and returns a string to be displayed.\n      • {full_name}?               (`boolean`, default: `false`) Display nodes\n                                   whose name length is wider than the width\n                                   of nvim-tree window in floating window.\n      • {root_folder_label}?       (`nvim_tree.config.renderer.root_folder_label`, default: `\":~:s?$?/..?\"`)\n                                   |nvim_tree.config.renderer.root_folder_label|\n      • {indent_width}?            (`integer`, default: `2`) Number of spaces\n                                   for each tree nesting level. Minimum 1.\n      • {hidden_display}?          (`nvim_tree.config.renderer.hidden_display`, default: `none`)\n                                   |nvim_tree.config.renderer.hidden_display|\n      • {symlink_destination}?     (`boolean`, default: `true`) Appends an\n                                   arrow followed by the target of the\n                                   symlink.\n      • {decorators}?              (`nvim_tree.config.renderer.decorator[]`, default: `{ \"Git\", \"Open\", \"Hidden\", \"Modified\", \"Bookmark\", \"Diagnostics\", \"Copied\", \"Cut\", }`)\n                                   List in order of additive precedence.\n      • {highlight_git}?           (`nvim_tree.config.renderer.highlight`)\n                                   (default: `\"none\"`)\n      • {highlight_opened_files}?  (`nvim_tree.config.renderer.highlight`)\n                                   (default: `\"none\"`)\n      • {highlight_hidden}?        (`nvim_tree.config.renderer.highlight`)\n                                   (default: `\"none\"`)\n      • {highlight_modified}?      (`nvim_tree.config.renderer.highlight`)\n                                   (default: `\"none\"`)\n      • {highlight_bookmarks}?     (`nvim_tree.config.renderer.highlight`)\n                                   (default: `\"none\"`)\n      • {highlight_diagnostics}?   (`nvim_tree.config.renderer.highlight`)\n                                   (default: `\"none\"`)\n      • {highlight_clipboard}?     (`nvim_tree.config.renderer.highlight`)\n                                   (default: `\"name\"`)\n      • {special_files}?           (`string[]`, default: `{ \"Cargo.toml\", \"Makefile\", \"README.md\", \"readme.md\", }`)\n                                   Highlight special files and directories\n                                   with `NvimTreeSpecial*`.\n      • {indent_markers}?          (`nvim_tree.config.renderer.indent_markers`)\n                                   |nvim_tree.config.renderer.indent_markers|\n      • {icons}?                   (`nvim_tree.config.renderer.icons`)\n                                   |nvim_tree.config.renderer.icons|\n\n*nvim_tree.config.renderer.hidden_stats*\n    Number of hidden nodes in a directory by reason: the filter that hid the\n    node.\n\n    Passed to your |nvim_tree.config.renderer.hidden_display| function e.g. >lua\n\n        ---@param hidden_stats nvim_tree.config.renderer.hidden_stats\n        ---@return string? summary\n        local my_hidden_display = function(hidden_stats)\n          local total_count = 0\n          for reason, count in pairs(hidden_stats) do\n            total_count = total_count + count\n          end\n\n          if total_count > 0 then\n            return \"(\" .. tostring(total_count) .. \" hidden)\"\n          end\n          return nil\n        end\n<\n\n    Fields: ~\n      • {bookmark}     (`integer`)\n      • {buf}          (`integer`)\n      • {custom}       (`integer`)\n      • {dotfile}      (`integer`)\n      • {git}          (`integer`)\n      • {live_filter}  (`integer`)\n\n*nvim_tree.config.renderer.icons*\n    Icons and separators\n\n    {_placement}                   *nvim_tree.config.renderer.icons.placement*\n    • `\"before\"`: before file/folder, after the file/folders icons\n    • `\"after\"`: after file/folder\n    • `\"signcolumn\"`: far left, requires |nvim_tree.config.view| {signcolumn}.\n    • `\"right_align\"`: far right\n\n    Fields: ~\n      • {git_placement}?          (`nvim_tree.config.renderer.icons.placement`)\n                                  (default: `before`)\n      • {hidden_placement}?       (`nvim_tree.config.renderer.icons.placement`)\n                                  (default: `after`)\n      • {modified_placement}?     (`nvim_tree.config.renderer.icons.placement`)\n                                  (default: `after`)\n      • {bookmarks_placement}?    (`nvim_tree.config.renderer.icons.placement`)\n                                  (default: `signcolumn`)\n      • {diagnostics_placement}?  (`nvim_tree.config.renderer.icons.placement`)\n                                  (default: `signcolumn`)\n      • {padding}?                (`table`)\n                                     *nvim_tree.config.renderer.icons.padding*\n                                  • {icon}? (`string`, default: `\" \"`) Between\n                                    icon and filename.\n                                  • {folder_arrow}? (`string`, default: `\" \"`)\n                                    Between folder arrow icon and file/folder\n                                    icon.\n      • {symlink_arrow}?          (`string`, default: `\" ➛ \"`) Separator\n                                  between symlink source and target.\n      • {show}?                   (`nvim_tree.config.renderer.icons.show`)\n                                  |nvim_tree.config.renderer.icons.show|\n      • {glyphs}?                 (`nvim_tree.config.renderer.icons.glyphs`)\n                                  |nvim_tree.config.renderer.icons.glyphs|\n      • {web_devicons}?           (`nvim_tree.config.renderer.icons.web_devicons`)\n                                  |nvim_tree.config.renderer.icons.web_devicons|\n\n*nvim_tree.config.renderer.icons.glyphs*\n    See |nvim-tree-icons-highlighting|.\n\n    Glyphs that appear in the sign column must have length <= 2\n\n    Fields: ~\n      • {default}?   (`string`, default: `\"\"`) Files\n      • {symlink}?   (`string`) (default: `\"\"`)\n      • {bookmark}?  (`string`) (default: `\"󰆤\"`)\n      • {modified}?  (`string`) (default: `\"●\"`)\n      • {hidden}?    (`string`) (default: `\"󰜌\"`)\n      • {folder}?    (`table`)                     *nvim_tree.config.renderer.icons.glyphs.folder*\n                     • {arrow_closed}? (`string`) (default: left arrow)\n                     • {arrow_open}? (`string`) (default: down arrow)\n                     • {default}? (`string`) (default: `\"\"`)\n                     • {open}? (`string`) (default: `\"\"`)\n                     • {empty}? (`string`) (default: `\"\"`)\n                     • {empty_open}? (`string`) (default: `\"\"`)\n                     • {symlink}? (`string`) (default: `\"\"`)\n                     • {symlink_open}? (`string`) (default: `\"\"`)\n      • {git}?       (`table`)                        *nvim_tree.config.renderer.icons.glyphs.git*\n                     • {unstaged}? (`string`) (default: `\"✗\"`)\n                     • {staged}? (`string`) (default: `\"✓\"`)\n                     • {unmerged}? (`string`) (default: `\"\"`)\n                     • {renamed}? (`string`) (default: `\"➜\"`)\n                     • {untracked}? (`string`) (default: `\"★\"`)\n                     • {deleted}? (`string`) (default: `\"\"`)\n                     • {ignored}? (`string`) (default: `\"◌\"`)\n\n*nvim_tree.config.renderer.icons.show*\n    See |nvim-tree-icons-highlighting|.\n\n    Fields: ~\n      • {file}?          (`boolean`) (default: `true`)\n      • {folder}?        (`boolean`) (default: `true`)\n      • {git}?           (`boolean`) (default: `true`)\n      • {modified}?      (`boolean`) (default: `true`)\n      • {hidden}?        (`boolean`) (default: `false`)\n      • {diagnostics}?   (`boolean`) (default: `true`)\n      • {bookmarks}?     (`boolean`) (default: `true`)\n      • {folder_arrow}?  (`boolean`, default: `true`) Show a small arrow\n                         before the folder node. Arrow will be a part of the\n                         node when using\n                         |nvim_tree.config.renderer.indent_markers|.\n\n*nvim_tree.config.renderer.icons.web_devicons*\n    Configure optional plugin `nvim-tree/nvim-web-devicons`, see\n    |nvim-tree-icons-highlighting|.\n\n    Fields: ~\n      • {file}?    (`table`)\n                           *nvim_tree.config.renderer.icons.web_devicons.file*\n                   • {enable}? (`boolean`) (default: `true`)\n                   • {color}? (`boolean`) (default: `true`)\n      • {folder}?  (`table`)\n                         *nvim_tree.config.renderer.icons.web_devicons.folder*\n                   • {enable}? (`boolean`) (default: `false`)\n                   • {color}? (`boolean`) (default: `true`)\n\n*nvim_tree.config.renderer.indent_markers*\n\n    Fields: ~\n      • {enable}?         (`boolean`, default: `false`) Display indent markers\n                          when folders are open.\n      • {inline_arrows}?  (`boolean`, default: `true`) Display folder arrows\n                          in the same column as indent marker when using\n                          |nvim_tree.config.renderer.icons.padding|\n                          {folder_arrow}\n      • {icons}?          (`table`)\n                              *nvim_tree.config.renderer.indent_markers.icons*\n                          Before the file/directory, length 1.\n                          • {corner}? (`string`) (default: `\"└\"`)\n                          • {edge}? (`string`) (default: `\"│\"`)\n                          • {item}? (`string`) (default: `\"│\"`)\n                          • {bottom}? (`string`) (default: `\"─\"`)\n                          • {none}? (`string`) (default: `\" \"`)\n\n\n\n==============================================================================\nConfig: hijack_directories               *nvim-tree-config-hijack-directories*\n\n*nvim_tree.config.hijack_directories*\n    Hijack directory buffers by replacing the directory buffer with the tree.\n\n    Disable this option if you use vim-dirvish or dirbuf.nvim.\n\n    If |nvim_tree.config| {hijack_netrw} and {disable_netrw} are `false` this\n    feature will be disabled.\n\n    Fields: ~\n      • {enable}?     (`boolean`) (default: `true`)\n      • {auto_open}?  (`boolean`, default: `true`) Open if the tree was\n                      previously closed.\n\n\n\n==============================================================================\nConfig: update_focused_file             *nvim-tree-config-update-focused-file*\n\n*nvim_tree.config.update_focused_file*\n    Update the focused file on |BufEnter|, uncollapsing folders recursively.\n\n    Fields: ~\n      • {enable}?       (`boolean`) (default: `false`)\n      • {update_root}?  (`nvim_tree.config.update_focused_file.update_root`)\n                        |nvim_tree.config.update_focused_file.update_root|\n      • {exclude}?      (`boolean|(fun(args: vim.api.keyset.create_autocmd.callback_args): boolean)`, default: `false`)\n                        A function called on |BufEnter| that returns true if\n                        the file should not be focused when opening.\n\n*nvim_tree.config.update_focused_file.update_root*\n    Update the root directory of the tree if the file is not under the current\n    root directory.\n\n    Prefers vim's cwd and |nvim_tree.config| {root_dirs}, falling back to the\n    directory containing the file.\n\n    Requires |nvim_tree.config.update_focused_file|\n\n    Fields: ~\n      • {enable}?       (`boolean`) (default: `false`)\n      • {ignore_list}?  (`string[]`, default: `{}`) List of buffer names and\n                        filetypes that will not update the root dir of the\n                        tree if the file isn't found under the current root\n                        directory.\n\n\n\n==============================================================================\nConfig: system_open                             *nvim-tree-config-system-open*\n\n*nvim_tree.config.system_open*\n    Open files or directories via the OS.\n\n    Nvim:\n    • `>=` 0.10 uses |vim.ui.open()| unless {cmd} is specified\n    • `<` 0.10 calls external {cmd}:\n      • UNIX: `xdg-open`\n      • macOS: `open`\n      • Windows: `cmd`\n\n    Once nvim-tree minimum Nvim version is updated to 0.10, this configuration\n    will no longer be necessary and will be removed.\n\n    Fields: ~\n      • {cmd}?   (`string`) The open command itself\n      • {args}?  (`string[]`, default: `{}` or `{ \"/c\", \"start\", '\"\"' }` on\n                 windows) Optional argument list. Leave empty for OS specific\n                 default.\n\n\n\n==============================================================================\nConfig: git                                             *nvim-tree-config-git*\n\n*nvim_tree.config.git*\n    Git operations are run in the background thus status may not immediately\n    appear.\n\n    Processes will be killed if they exceed {timeout} ms. Git integration will\n    be disabled following 5 timeouts and you will be notified.\n\n    Git integration may be disabled for git top-level directories via\n    {disable_for_dirs}:\n    • A list of relative paths evaluated with |fnamemodify()| `:p` OR\n    • A function that is passed an absolute path and returns `true` to disable\n\n    See |nvim-tree-icons-highlighting|.\n\n    Fields: ~\n      • {enable}?             (`boolean`) (default: `true`)\n      • {show_on_dirs}?       (`boolean`, default: `true`) Show status icons\n                              of children when directory itself has no status\n                              icon\n      • {show_on_open_dirs}?  (`boolean`, default: `true`) Show status icons\n                              of children on directories that are open.\n                              Requires {show_on_dirs}.\n      • {disable_for_dirs}?   (`string[]|(fun(path: string): boolean)`,\n                              default: `{}`) Disable for top level paths.\n      • {timeout}?            (`integer`, default: `400`) `git` processes\n                              timeout milliseconds.\n      • {cygwin_support}?     (`boolean`, default: `false`) Use `cygpath` if\n                              available to resolve paths for git.\n\n\n\n==============================================================================\nConfig: diagnostics                             *nvim-tree-config-diagnostics*\n\n*nvim_tree.config.diagnostics*\n    Integrate with |lsp| or COC diagnostics.\n\n    See |nvim-tree-icons-highlighting|.\n\n    Fields: ~\n      • {enable}?             (`boolean`) (default: `false`)\n      • {debounce_delay}?     (`integer`, default: `500`) Idle milliseconds\n                              between diagnostic event and tree update.\n      • {show_on_dirs}?       (`boolean`, default: `false`) Show diagnostic\n                              icons on parent directories.\n      • {show_on_open_dirs}?  (`boolean`, default: `true`) Show diagnostics\n                              icons on directories that are open. Requires\n                              {show_on_dirs}.\n      • {diagnostic_opts}?    (`boolean`, default: `false`) Global\n                              |vim.diagnostic.Opts| overrides {severity} and\n                              {icons}\n      • {severity}?           (`table`)\n                                       *nvim_tree.config.diagnostics.severity*\n                              • {min}? (`vim.diagnostic.Severity`, default:\n                                HINT) |vim.diagnostic.severity|\n                              • {max}? (`vim.diagnostic.Severity`, default:\n                                ERROR) |vim.diagnostic.severity|\n      • {icons}?              (`table`)                                *nvim_tree.config.diagnostics.icons*\n                              • {hint}? (`string`) (default: `\"\"` )\n                              • {info}? (`string`) (default: `\"\"` )\n                              • {warning}? (`string`) (default: `\"\"` )\n                              • {error}? (`string`) (default: `\"\"` )\n\n\n\n==============================================================================\nConfig: modified                                   *nvim-tree-config-modified*\n\n*nvim_tree.config.modified*\n    Indicate which files have unsaved modification. To see modified status in\n    the tree you will need:\n    • |nvim_tree.config.renderer.icons.show| {modified} OR\n    • |nvim_tree.config.renderer| {highlight_modified}\n\n    See |nvim-tree-icons-highlighting|.\n\n    Fields: ~\n      • {enable}?             (`boolean`) (default: `false`)\n      • {show_on_dirs}?       (`boolean`, default: `true`) Show modified\n                              indication on directory whose children are\n                              modified.\n      • {show_on_open_dirs}?  (`boolean`, default: `false`) Show modified\n                              indication on open directories. Requires\n                              {show_on_dirs}.\n\n\n\n==============================================================================\nConfig: filters                                     *nvim-tree-config-filters*\n\n*nvim_tree.config.filters*\n    Filters may be applied to the tree to exlude the display of\n    file/directories.\n\n    Multiple filters may be applied at once.\n\n    Filters can be set at startup or toggled live via API with default\n    mappings.\n\n    `I     `{git_ignored}`         `|nvim_tree.api.filter.git.ignored.toggle()|\n    Ignore files based on `.gitignore`. Requires |nvim_tree.config.git|\n\n    `H     `{dotfiles}`            `|nvim_tree.api.filter.dotfiles.toggle()|\n    Filter dotfiles: files/directories starting with a `.`\n\n    `C     `{git_clean}`           `|nvim_tree.api.filter.git.clean.toggle()|\n    Filter files with no git status. `.gitignore` files will not be filtered\n    when {git_ignored}, as they are effectively dirty.\n\n    `B     `{no_buffer}`           `|nvim_tree.api.filter.no_buffer.toggle()|\n    Filter files that have no |buflisted()| buffer. For performance reasons\n    buffer delete/wipe may not be immediately shown. A reload or filesystem\n    event will always result in an update.\n\n    `M     `{no_bookmark}`         `|nvim_tree.api.filter.no_bookmark.toggle()|\n    Filter files that are not bookmarked. Enabling this is not useful as there\n    is no means yet to persist bookmarks.\n\n    `U     `{custom}`              `|nvim_tree.api.filter.custom.toggle()|\n    Disable specific file/directory names via:\n    • a list of backslash escaped |regular-expression| e.g. `\"^\\\\.git\"\"`\n    • a function passed the absolute path of the directory.\n\n    All filters including live filter may be disabled via {enable} and toggled\n    with |nvim_tree.api.filter.toggle()|\n\n    Files/directories may be {exclude}d from filtering: they will always be\n    shown, overriding {git_ignored}, {dotfiles} and {custom}.\n\n    Fields: ~\n      • {enable}?       (`boolean`, default: `true`) Enable all filters.\n      • {git_ignored}?  (`boolean`) (default: `true`)\n      • {dotfiles}?     (`boolean`) (default: `false`)\n      • {git_clean}?    (`boolean`) (default: `false`)\n      • {no_buffer}?    (`boolean`) (default: `false`)\n      • {no_bookmark}?  (`boolean`) (default: `false`)\n      • {custom}?       (`string[]|(fun(absolute_path: string): boolean)`)\n                        (default: `{}`)\n      • {exclude}?      (`string[]`) (default: `{}`)\n\n\n\n==============================================================================\nConfig: live_filter                             *nvim-tree-config-live-filter*\n\n*nvim_tree.config.live_filter*\n    Live filter allows you to filter the tree nodes dynamically using\n    |regular-expression| matching.\n\n    This feature is bound to the `f` key by default. The filter can be cleared\n    with the `F` key by default.\n\n    Fields: ~\n      • {prefix}?               (`string`, default: `\"[FILTER]: \"`) Prefix of\n                                the filter displayed in the buffer.\n      • {always_show_folders}?  (`boolean`, default: `true`) Whether to filter\n                                folders or not.\n\n\n\n==============================================================================\nConfig: filesystem_watchers             *nvim-tree-config-filesystem-watchers*\n\n*nvim_tree.config.filesystem_watchers*\n    Use file system watchers (libuv `uv_fs_event_t`) to monitor the filesystem\n    for changes and update the tree.\n\n    With this feature, the tree will be partially updated on specific\n    directory changes, resulting in better performance.\n\n    Watchers may be disabled for absolute directory paths via {ignore_dirs}.\n    • A list of |regular-expression| to match a path, backslash escaped e.g.\n      `\"my-proj/\\\\.build$\"` OR\n    • A function that is passed an absolute path and returns `true` to disable\n      This may be useful when a path is not in `.gitignore` or git integration\n      is disabled.\n\n    After {max_events} consecutive filesystem events on a single directory\n    with an interval < {debounce_delay}:\n    • The filesystem watcher will be disabled for that directory.\n    • A warning notification will be shown.\n    • Consider adding this directory to {ignore_dirs}\n\n    Fields: ~\n      • {enable}?          (`boolean`) (default: `true`)\n      • {debounce_delay}?  (`integer`, default: `50`) Idle milliseconds\n                           between filesystem change and tree update.\n      • {ignore_dirs}?     (`string[]|(fun(path: string): boolean)`, default: `{ \"/.ccls-cache\", \"/build\", \"/node_modules\", \"/target\", \"/.zig-cache\"}`)\n                           Disable for specific directories.\n      • {max_events}?      (`integer`, default: `0` or `1000` on windows)\n                           Disable for a single directory after {max_events}\n                           consecutive events with an interval <\n                           {debounce_delay}. Set to 0 to allow unlimited\n                           consecutive events.\n\n\n\n==============================================================================\nConfig: actions                                     *nvim-tree-config-actions*\n\n*nvim_tree.config.actions*\n\n    Fields: ~\n      • {use_system_clipboard}?  (`boolean`, default: `true`) Use the system\n                                 clipboard for copy/paste. Copied text will be\n                                 stored in registers `+` (system), otherwise,\n                                 it will be stored in `1` and `\"`\n      • {change_dir}?            (`nvim_tree.config.actions.change_dir`)\n                                 |nvim_tree.config.actions.change_dir|\n      • {expand_all}?            (`nvim_tree.config.actions.expand_all`)\n                                 |nvim_tree.config.actions.expand_all|\n      • {file_popup}?            (`nvim_tree.config.actions.file_popup`)\n                                 |nvim_tree.config.actions.file_popup|\n      • {open_file}?             (`nvim_tree.config.actions.open_file`)\n                                 |nvim_tree.config.actions.open_file|\n      • {remove_file}?           (`nvim_tree.config.actions.remove_file`)\n                                 |nvim_tree.config.actions.remove_file|\n\n*nvim_tree.config.actions.change_dir*\n    vim |current-directory| behaviour\n\n    Fields: ~\n      • {enable}?              (`boolean`, default: `true`) Change the working\n                               directory when changing directories in the tree\n      • {global}?              (`boolean`, default: `false`) Use `:cd` instead\n                               of `:lcd` when changing directories.\n      • {restrict_above_cwd}?  (`boolean`, default: `false`) Restrict changing\n                               to a directory above the global cwd.\n\n*nvim_tree.config.actions.expand_all*\n    Configure |nvim_tree.api.tree.expand_all()| and\n    |nvim_tree.api.node.expand()|\n\n    Fields: ~\n      • {max_folder_discovery}?  (`integer`, default: `300`) Limit the number\n                                 of folders being explored when expanding\n                                 every folder. Avoids hanging Nvim when\n                                 running this action on very large folders.\n      • {exclude}?               (`string[]`, default: `{}`) A list of\n                                 directories that should not be expanded\n                                 automatically e.g\n                                 `{ \".git\", \"target\", \"build\" }`\n\n*nvim_tree.config.actions.file_popup*\n    {file_popup} floating window.\n\n    {open_win_config} is passed to |nvim_open_win()|, default: >lua\n        {\n          col = 1,\n          row = 1,\n          relative = \"cursor\",\n          border = \"shadow\",\n          style = \"minimal\",\n        }\n<\n\n    You shouldn't define {width} and {height} values here. They will be\n    overridden to fit the file_popup content.\n\n    Fields: ~\n      • {open_win_config}?  (`vim.api.keyset.win_config`) (default:\n                            `{ col = 1, row = 1, relative = \"cursor\", border = \"shadow\", style = \"minimal\", }`)\n\n*nvim_tree.config.actions.open_file*\n    Opening files.\n\n    Fields: ~\n      • {quit_on_open}?   (`boolean`, default: `false`) Closes the explorer\n                          when opening a file\n      • {eject}?          (`boolean`, default: `true`) Prevent new opened file\n                          from opening in the same window as the tree.\n      • {resize_window}?  (`boolean`, default: `true`) Resizes the tree when\n                          opening a file\n      • {window_picker}?  (`nvim_tree.config.actions.open_file.window_picker`)\n                          |nvim_tree.config.actions.open_file.window_picker|\n\n*nvim_tree.config.actions.open_file.window_picker*\n    A window picker will be shown when there are multiple windows available to\n    open a file. It will show a single character identifier in each window's\n    status line.\n\n    When it is not enabled the file will open in the window from which you\n    last opened the tree, obeying {exclude}\n\n    You may define a {picker} function that should return the window id that\n    will open the node, or `nil` if an invalid window is picked or user\n    cancelled the action. The picker may create a new window.\n\n    Fields: ~\n      • {enable}?   (`boolean`) (default: `true`)\n      • {picker}?   (`\"default\"|(fun(): integer)`, default: `\"default\"`)\n                    Change the default window picker or define your own.\n      • {chars}?    (`string`, default:\n                    `\"ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890\"`) Identifier\n                    characters to use.\n      • {exclude}?  (`nvim_tree.config.actions.open_file.window_picker.exclude`)\n                    |nvim_tree.config.actions.open_file.window_picker.exclude|\n\n*nvim_tree.config.actions.open_file.window_picker.exclude*\n    Tables of buffer option names mapped to a list of option values. Windows\n    containing matching buffers will not be:\n    • available when using a window picker\n    • selected when not using a window picker\n\n    Fields: ~\n      • {filetype}?  (`string[]`) (default:\n                     `{ \"notify\", \"lazy\", \"qf\", \"diff\", \"fugitive\", \"fugitiveblame\", }`)\n      • {buftype}?   (`string[]`) (default:\n                     `{ \"nofile\", \"terminal\", \"help\", }`)\n\n*nvim_tree.config.actions.remove_file*\n    Removing files.\n\n    Fields: ~\n      • {close_window}?  (`boolean`, default: `true`) Close any window that\n                         displays a file when removing that file from the\n                         tree.\n\n\n\n==============================================================================\nConfig: trash                                         *nvim-tree-config-trash*\n\n*nvim_tree.config.trash*\n    Files may be trashed via an external command that must be installed on\n    your system.\n    • linux: `gio trash`, from linux package `glib2`\n    • macOS: `trash`, from homebrew package `trash`\n    • windows: `trash`, requires `trash-cli` or similar\n\n    Fields: ~\n      • {cmd}?  (`string`) (default: `\"gio trash\"` or `\"trash\"`)\n\n\n\n==============================================================================\nConfig: tab                                             *nvim-tree-config-tab*\n\n*nvim_tree.config.tab*\n\n    Fields: ~\n      • {sync}?  (`nvim_tree.config.tab.sync`) |nvim_tree.config.tab.sync|\n\n*nvim_tree.config.tab.sync*\n\n    Fields: ~\n      • {open}?    (`boolean`, default: `false`) Opens the tree automatically\n                   when switching tabpage or opening a new tabpage if the tree\n                   was previously open.\n      • {close}?   (`boolean`, default: `false`) Closes the tree across all\n                   tabpages when the tree is closed.\n      • {ignore}?  (`string[]`, default: `{}`) List of filetypes or buffer\n                   names on new tab that will prevent `open` and `close`\n\n\n\n==============================================================================\nConfig: notify                                       *nvim-tree-config-notify*\n\n*nvim_tree.config.notify*\n    nvim-tree |vim.log.levels|\n    • `ERROR`: hard errors e.g. failure to read from the file system.\n    • `WARN`: non-fatal errors e.g. unable to system open a file.\n    • `INFO`: information only e.g. file copy path confirmation.\n    • `DEBUG`: information for troubleshooting, e.g. failures in some window\n      closing operations.\n\n    Fields: ~\n      • {threshold}?      (`vim.log.levels`, default: `vim.log.levels.INFO`)\n                          Specify minimum notification |vim.log.levels|\n      • {absolute_path}?  (`boolean`, default: `true`) Use absolute paths in\n                          FS action notifications, otherwise item names.\n\n\n\n==============================================================================\nConfig: bookmarks                                 *nvim-tree-config-bookmarks*\n\n*nvim_tree.config.bookmarks*\n    Optionally {persist} bookmarks to a json file:\n    • `true` use default: `stdpath(\"data\") .. \"/nvim-tree-bookmarks.json\"`\n    • `false` do not persist\n    • `string` absolute path of your choice\n\n    Fields: ~\n      • {persist}?  (`boolean|string`) (default: `false`)\n\n\n\n==============================================================================\nConfig: help                                           *nvim-tree-config-help*\n\n*nvim_tree.config.help*\n\n    Fields: ~\n      • {sort_by}?  (`\"key\"|\"desc\"`, default: `\"key\"`) Alphabetically.\n\n\n\n==============================================================================\nConfig: ui                                               *nvim-tree-config-ui*\n\n*nvim_tree.config.ui*\n\n    Fields: ~\n      • {confirm}?  (`nvim_tree.config.ui.confirm`)\n                    |nvim_tree.config.ui.confirm|\n\n*nvim_tree.config.ui.confirm*\n    Confirmation prompts.\n\n    Fields: ~\n      • {remove}?       (`boolean`, default: `true`) Prompt before removing.\n      • {trash}?        (`boolean`, default: `true`) Prompt before trashing.\n      • {default_yes}?  (`boolean`, default: `false`) If `true` the prompt\n                        will be `Y/n`, otherwise `y/N`\n\n\n\n==============================================================================\nConfig: experimental                           *nvim-tree-config-experimental*\n\n*nvim_tree.config.experimental*\n    Experimental features that may become default or optional functionality.\n\n    In the event of a problem please disable the experiment and raise an\n    issue.\n\n\n\n==============================================================================\nConfig: log                                             *nvim-tree-config-log*\n\n*nvim_tree.config.log*\n    Log to a file `nvim-tree.log` in |stdpath()| `log`, usually\n    `${XDG_STATE_HOME}/nvim`\n\n    Fields: ~\n      • {enable}?    (`boolean`) (default: `false`)\n      • {truncate}?  (`boolean`, default: `false`) Remove existing log file at\n                     startup.\n      • {types}?     (`nvim_tree.config.log.types`)\n                     |nvim_tree.config.log.types|\n\n*nvim_tree.config.log.types*\n    Specify which information to log.\n\n    Fields: ~\n      • {all}?          (`boolean`, default: `false`) Everything.\n      • {profile}?      (`boolean`, default: `false`) Timing of some\n                        operations.\n      • {config}?       (`boolean`, default: `false`) Config and mappings, at\n                        startup.\n      • {copy_paste}?   (`boolean`, default: `false`) File copy and paste\n                        actions.\n      • {dev}?          (`boolean`, default: `false`) Used for local\n                        development only. Not useful for users.\n      • {diagnostics}?  (`boolean`, default: `false`) LSP and COC processing,\n                        verbose.\n      • {git}?          (`boolean`, default: `false`) Git processing, verbose.\n      • {watcher}?      (`boolean`, default: `false`)\n                        |nvim_tree.config.filesystem_watchers| processing,\n                        verbose.\n\n\n\n==============================================================================\nConfig: Default                                     *nvim-tree-config-default*\n\nFollowing is the default configuration, see |nvim_tree.config| for details. >lua\n\n    ---@type nvim_tree.config\n    local config = {\n      on_attach = \"default\",\n      hijack_cursor = false,\n      auto_reload_on_write = true,\n      disable_netrw = false,\n      hijack_netrw = true,\n      hijack_unnamed_buffer_when_opening = false,\n      root_dirs = {},\n      prefer_startup_root = false,\n      sync_root_with_cwd = false,\n      reload_on_bufenter = false,\n      respect_buf_cwd = false,\n      select_prompts = false,\n      sort = {\n        sorter = \"name\",\n        folders_first = true,\n        files_first = false,\n      },\n      view = {\n        centralize_selection = false,\n        cursorline = true,\n        cursorlineopt = \"both\",\n        debounce_delay = 15,\n        side = \"left\",\n        preserve_window_proportions = false,\n        number = false,\n        relativenumber = false,\n        signcolumn = \"yes\",\n        width = 30,\n        float = {\n          enable = false,\n          quit_on_focus_loss = true,\n          open_win_config = {\n            relative = \"editor\",\n            border = \"rounded\",\n            width = 30,\n            height = 30,\n            row = 1,\n            col = 1,\n          },\n        },\n      },\n      renderer = {\n        add_trailing = false,\n        group_empty = false,\n        full_name = false,\n        root_folder_label = \":~:s?$?/..?\",\n        indent_width = 2,\n        special_files = { \"Cargo.toml\", \"Makefile\", \"README.md\", \"readme.md\" },\n        hidden_display = \"none\",\n        symlink_destination = true,\n        decorators = { \"Git\", \"Open\", \"Hidden\", \"Modified\", \"Bookmark\", \"Diagnostics\", \"Copied\", \"Cut\", },\n        highlight_git = \"none\",\n        highlight_diagnostics = \"none\",\n        highlight_opened_files = \"none\",\n        highlight_modified = \"none\",\n        highlight_hidden = \"none\",\n        highlight_bookmarks = \"none\",\n        highlight_clipboard = \"name\",\n        indent_markers = {\n          enable = false,\n          inline_arrows = true,\n          icons = {\n            corner = \"└\",\n            edge = \"│\",\n            item = \"│\",\n            bottom = \"─\",\n            none = \" \",\n          },\n        },\n        icons = {\n          web_devicons = {\n            file = {\n              enable = true,\n              color = true,\n            },\n            folder = {\n              enable = false,\n              color = true,\n            },\n          },\n          git_placement = \"before\",\n          modified_placement = \"after\",\n          hidden_placement = \"after\",\n          diagnostics_placement = \"signcolumn\",\n          bookmarks_placement = \"signcolumn\",\n          padding = {\n            icon = \" \",\n            folder_arrow = \" \",\n          },\n          symlink_arrow = \" ➛ \",\n          show = {\n            file = true,\n            folder = true,\n            folder_arrow = true,\n            git = true,\n            modified = true,\n            hidden = false,\n            diagnostics = true,\n            bookmarks = true,\n          },\n          glyphs = {\n            default = \"\",\n            symlink = \"\",\n            bookmark = \"󰆤\",\n            modified = \"●\",\n            hidden = \"󰜌\",\n            folder = {\n              arrow_closed = \"\",\n              arrow_open = \"\",\n              default = \"\",\n              open = \"\",\n              empty = \"\",\n              empty_open = \"\",\n              symlink = \"\",\n              symlink_open = \"\",\n            },\n            git = {\n              unstaged = \"✗\",\n              staged = \"✓\",\n              unmerged = \"\",\n              renamed = \"➜\",\n              untracked = \"★\",\n              deleted = \"\",\n              ignored = \"◌\",\n            },\n          },\n        },\n      },\n      hijack_directories = {\n        enable = true,\n        auto_open = true,\n      },\n      update_focused_file = {\n        enable = false,\n        update_root = {\n          enable = false,\n          ignore_list = {},\n        },\n        exclude = false,\n      },\n      system_open = {\n        cmd = \"\",\n        args = {},\n      },\n      git = {\n        enable = true,\n        show_on_dirs = true,\n        show_on_open_dirs = true,\n        disable_for_dirs = {},\n        timeout = 400,\n        cygwin_support = false,\n      },\n      diagnostics = {\n        enable = false,\n        show_on_dirs = false,\n        show_on_open_dirs = true,\n        debounce_delay = 500,\n        severity = {\n          min = vim.diagnostic.severity.HINT,\n          max = vim.diagnostic.severity.ERROR,\n        },\n        icons = {\n          hint = \"\",\n          info = \"\",\n          warning = \"\",\n          error = \"\",\n        },\n        diagnostic_opts = false,\n      },\n      modified = {\n        enable = false,\n        show_on_dirs = true,\n        show_on_open_dirs = true,\n      },\n      filters = {\n        enable = true,\n        git_ignored = true,\n        dotfiles = false,\n        git_clean = false,\n        no_buffer = false,\n        no_bookmark = false,\n        custom = {},\n        exclude = {},\n      },\n      live_filter = {\n        prefix = \"[FILTER]: \",\n        always_show_folders = true,\n      },\n      filesystem_watchers = {\n        enable = true,\n        debounce_delay = 50,\n        max_events = 0,\n        ignore_dirs = {\n          \"/.ccls-cache\",\n          \"/build\",\n          \"/node_modules\",\n          \"/target\",\n          \"/.zig-cache\",\n        },\n      },\n      actions = {\n        use_system_clipboard = true,\n        change_dir = {\n          enable = true,\n          global = false,\n          restrict_above_cwd = false,\n        },\n        expand_all = {\n          max_folder_discovery = 300,\n          exclude = {},\n        },\n        file_popup = {\n          open_win_config = {\n            col = 1,\n            row = 1,\n            relative = \"cursor\",\n            border = \"shadow\",\n            style = \"minimal\",\n          },\n        },\n        open_file = {\n          quit_on_open = false,\n          eject = true,\n          resize_window = true,\n          relative_path = true,\n          window_picker = {\n            enable = true,\n            picker = \"default\",\n            chars = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890\",\n            exclude = {\n              filetype = { \"notify\", \"packer\", \"qf\", \"diff\", \"fugitive\", \"fugitiveblame\" },\n              buftype = { \"nofile\", \"terminal\", \"help\" },\n            },\n          },\n        },\n        remove_file = {\n          close_window = true,\n        },\n      },\n      trash = {\n        cmd = \"gio trash\",\n      },\n      tab = {\n        sync = {\n          open = false,\n          close = false,\n          ignore = {},\n        },\n      },\n      notify = {\n        threshold = vim.log.levels.INFO,\n        absolute_path = true,\n      },\n      help = {\n        sort_by = \"key\",\n      },\n      ui = {\n        confirm = {\n          remove = true,\n          trash = true,\n          default_yes = false,\n        },\n      },\n      bookmarks = {\n        persist = false,\n      },\n      experimental = {\n      },\n      log = {\n        enable = false,\n        truncate = false,\n        types = {\n          all = false,\n          config = false,\n          copy_paste = false,\n          dev = false,\n          diagnostics = false,\n          git = false,\n          profile = false,\n          watcher = false,\n        },\n      },\n    }\n<\n\n\n\n==============================================================================\nAPI                                                            *nvim-tree-api*\n\nnvim-tree exposes a public API. This is non breaking, with additions made as\nnecessary.\n\nPlease do not require or use modules other than `nvim-tree.api`, as internal\nmodules will change without notice.\n\nThe API is separated into multiple modules:\n• |nvim-tree-api-appearance|\n• |nvim-tree-api-commands|\n• |nvim-tree-api-events|\n• |nvim-tree-api-filter|\n• |nvim-tree-api-fs|\n• |nvim-tree-api-git|\n• |nvim-tree-api-map|\n• |nvim-tree-api-marks|\n• |nvim-tree-api-node|\n• |nvim-tree-api-tree|\n\nModules are accessed via `api.<module>.<function>`\n\nExample invocation of the `reload` function in the `tree` module: >lua\n\n    local api = require(\"nvim-tree.api\")\n    api.tree.reload()\n<\n\nGenerally, functions accepting a |nvim_tree.api.Node| as their first argument\nwill use the node under the cursor when that argument is not present or nil.\nSome functions are mode-dependent: when invoked in visual mode they will\noperate on all nodes in the visual selection instead of a single node. See\n|nvim-tree-mappings-default| for which mappings support visual mode.\n\ne.g. the following are functionally identical: >lua\n\n    api.node.open.edit(nil, { focus = true })\n\n    api.node.open.edit(api.tree.get_node_under_cursor(), { focus = true })\n<\n\n\n*nvim_tree.api.Node*\n    The Node class is a data class. Instances may be provided by API functions\n    for use as a:\n    • handle to pass back to API functions e.g. |nvim_tree.api.node.run.cmd()|\n    • reference in callbacks e.g. |nvim_tree.config.sort.Sorter| {sorter}\n\n    Please do not mutate the contents of any Node object.\n\n    Fields: ~\n      • {absolute_path}   (`string`) of the file or directory\n      • {name}            (`string`) file or directory name\n      • {parent}?         (`nvim_tree.api.DirectoryNode`) parent directory,\n                          nil for root\n      • {type}            (`\"file\"|\"directory\"|\"link\"`) |uv.fs_stat()| {type}\n      • {executable}      (`boolean`) file is executable\n      • {fs_stat}?        (`uv.fs_stat.result`) at time of last tree display,\n                          see |uv.fs_stat()|\n      • {git_status}      (`nvim_tree.git.Status?`) for files and directories\n      • {diag_severity}?  (`lsp.DiagnosticSeverity`) diagnostic status\n      • {hidden}          (`boolean`) node is not visible in the tree\n\n*nvim_tree.git.Status*\n    Git statuses for a single node.\n\n    `nvim_tree.git.XY`: 2 character string, see `man 1 git-status` \"Short\n    Format\"\n\n    {dir} status is derived from its contents:\n    • `direct`: inherited from child files\n    • `indirect`: inherited from child directories\n\n    Fields: ~\n      • {file}?  (`nvim_tree.git.XY`) status of a file node\n      • {dir}?   (`table<\"direct\"|\"indirect\", nvim_tree.git.XY[]>`) direct\n                 inclusive-or indirect status\n\n\n\n==============================================================================\nAPI: appearance                                     *nvim-tree-api-appearance*\n\nhi_test()                                 *nvim_tree.api.appearance.hi_test()*\n    Open a new buffer displaying all nvim-tree highlight groups, their link\n    chain and concrete definition.\n\n    Similar to `:so $VIMRUNTIME/syntax/hitest.vim` as per |:highlight|\n\n\n==============================================================================\nAPI: commands                                         *nvim-tree-api-commands*\n\nget()                                           *nvim_tree.api.commands.get()*\n    Retrieve all |nvim-tree-commands|\n\n    They have been created via |nvim_create_user_command()|, see also\n    |lua-guide-commands-create|\n\n    Return: ~\n        (`table[]`)\n        • {name} (`string`) name of the `:NvimTree*` command\n        • {command}\n          (`fun(args: vim.api.keyset.create_user_command.command_args)`)\n          function that the command will execute\n        • {opts} (`vim.api.keyset.user_command`) |command-attributes|\n\n\n==============================================================================\nAPI: config                                             *nvim-tree-api-config*\n\ndefault()                                     *nvim_tree.api.config.default()*\n    Default nvim-tree config.\n\n    Return: ~\n        (`nvim_tree.config`) immutable deep clone\n\nglobal()                                       *nvim_tree.api.config.global()*\n    Global current nvim-tree config.\n\n    Return: ~\n        (`nvim_tree.config`) immutable deep clone\n\nuser()                                           *nvim_tree.api.config.user()*\n    Reference to config passed to |nvim-tree-setup|\n\n    Return: ~\n        (`nvim_tree.config?`) nil when no config passed to setup\n\n\n==============================================================================\nAPI: events                                             *nvim-tree-api-events*\n\nsubscribe({event_type}, {callback})         *nvim_tree.api.events.subscribe()*\n    Register a handler for an event, see |nvim-tree-events|.\n\n    Parameters: ~\n      • {event_type}  (`nvim_tree.api.events.Event`) |nvim_tree_events_kind|\n      • {callback}    (`fun(payload: table?)`)\n\n\n==============================================================================\nAPI: filter                                             *nvim-tree-api-filter*\n\ncustom.toggle()                         *nvim_tree.api.filter.custom.toggle()*\n    Toggle |nvim_tree.config.filters| {custom} filter.\n\ndotfiles.toggle()                     *nvim_tree.api.filter.dotfiles.toggle()*\n    Toggle |nvim_tree.config.filters| {dotfiles} filter.\n\ngit.clean.toggle()                   *nvim_tree.api.filter.git.clean.toggle()*\n    Toggle |nvim_tree.config.filters| {git_clean} filter.\n\ngit.ignored.toggle()               *nvim_tree.api.filter.git.ignored.toggle()*\n    Toggle |nvim_tree.config.filters| {git_ignored} filter.\n\nlive.clear()                               *nvim_tree.api.filter.live.clear()*\n    Exit live filter mode.\n\nlive.start()                               *nvim_tree.api.filter.live.start()*\n    Enter live filter mode. Opens an input window with |filetype|\n    `NvimTreeFilter`\n\nno_bookmark.toggle()               *nvim_tree.api.filter.no_bookmark.toggle()*\n    Toggle |nvim_tree.config.filters| {no_bookmark} filter.\n\nno_buffer.toggle()                   *nvim_tree.api.filter.no_buffer.toggle()*\n    Toggle |nvim_tree.config.filters| {no_buffer} filter.\n\ntoggle()                                       *nvim_tree.api.filter.toggle()*\n    Toggle |nvim_tree.config.filters| {enable} which applies to ALL filters.\n\n\n==============================================================================\nAPI: fs                                                     *nvim-tree-api-fs*\n\nclear_clipboard()                         *nvim_tree.api.fs.clear_clipboard()*\n    Clear the nvim-tree clipboard.\n\ncopy.absolute_path({node})             *nvim_tree.api.fs.copy.absolute_path()*\n    Copy the absolute path to the system clipboard.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`)\n\ncopy.basename({node})                       *nvim_tree.api.fs.copy.basename()*\n    Copy the name with extension omitted to the system clipboard.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`)\n\ncopy.filename({node})                       *nvim_tree.api.fs.copy.filename()*\n    Copy the name to the system clipboard.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`)\n\ncopy.node({node})                               *nvim_tree.api.fs.copy.node()*\n    Copy to the nvim-tree clipboard. In visual mode, copies all nodes in the\n    visual selection.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`)\n\ncopy.relative_path({node})             *nvim_tree.api.fs.copy.relative_path()*\n    Copy the path relative to the tree root to the system clipboard.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`)\n\ncreate({node})                                     *nvim_tree.api.fs.create()*\n    Prompt to create a file or directory.\n\n    When {node} is a file it will be created in the parent directory.\n\n    Use a trailing `\"/\"` to create a directory e.g. `\"foo/\"`\n\n    Multiple directories/files may be created e.g. `\"foo/bar/baz\"`\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`)\n\ncut({node})                                           *nvim_tree.api.fs.cut()*\n    Cut to the nvim-tree clipboard. In visual mode, cuts all nodes in the\n    visual selection.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`)\n\npaste({node})                                       *nvim_tree.api.fs.paste()*\n    Paste from the nvim-tree clipboard.\n\n    If {node} is a file it will pasted in the parent directory.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`)\n\nprint_clipboard()                         *nvim_tree.api.fs.print_clipboard()*\n    Print the contents of the nvim-tree clipboard.\n\nremove({node})                                     *nvim_tree.api.fs.remove()*\n    Delete from the file system. In visual mode, deletes all nodes in the\n    visual selection with a single prompt.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`)\n\nrename({node})                                     *nvim_tree.api.fs.rename()*\n    Prompt to rename by name.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`)\n\nrename_basename({node})                   *nvim_tree.api.fs.rename_basename()*\n    Prompt to rename by name with extension omitted.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`)\n\nrename_full({node})                           *nvim_tree.api.fs.rename_full()*\n    Prompt to rename by absolute path.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`)\n\nrename_node({node})                           *nvim_tree.api.fs.rename_node()*\n    Prompt to rename.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`)\n\nrename_sub({node})                             *nvim_tree.api.fs.rename_sub()*\n    Prompt to rename by absolute path with name omitted.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`)\n\ntrash({node})                                       *nvim_tree.api.fs.trash()*\n    Trash as per |nvim_tree.config.trash|. In visual mode, trashes all nodes\n    in the visual selection with a single prompt.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`)\n\n\n==============================================================================\nAPI: git                                                   *nvim-tree-api-git*\n\nreload()                                          *nvim_tree.api.git.reload()*\n    Update the git status of the entire tree.\n\n\n==============================================================================\nAPI: map                                                   *nvim-tree-api-map*\n\nkeymap.current()                          *nvim_tree.api.map.keymap.current()*\n    Retrieve all buffer local mappings for nvim-tree. These are the mappings\n    that are applied by |nvim_tree.config| {on_attach}, which may include\n    default mappings.\n\n    Return: ~\n        (`vim.api.keyset.get_keymap[]`)\n\nkeymap.default()                          *nvim_tree.api.map.keymap.default()*\n    Retrieves the buffer local mappings for nvim-tree that are applied by\n    |nvim_tree.api.map.on_attach.default()|\n\n    Return: ~\n        (`vim.api.keyset.get_keymap[]`)\n\non_attach.default({bufnr})             *nvim_tree.api.map.on_attach.default()*\n    Apply all |nvim-tree-mappings-default|. Call from your |nvim_tree.config|\n    {on_attach}.\n\n    Parameters: ~\n      • {bufnr}  (`integer`) use the `bufnr` passed to {on_attach}\n\n\n==============================================================================\nAPI: marks                                               *nvim-tree-api-marks*\n\nbulk.delete()                              *nvim_tree.api.marks.bulk.delete()*\n    Delete all marked, prompting if |nvim_tree.config.ui.confirm| {remove}\n\nbulk.move()                                  *nvim_tree.api.marks.bulk.move()*\n    Prompts for a directory to move all marked nodes into.\n\nbulk.trash()                                *nvim_tree.api.marks.bulk.trash()*\n    Delete all marked, prompting if |nvim_tree.config.ui.confirm| {trash}\n\nclear()                                          *nvim_tree.api.marks.clear()*\n    Clear all marks.\n\nget({node})                                        *nvim_tree.api.marks.get()*\n    Return the node if it is marked.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`) file or directory\n\n    Return: ~\n        (`nvim_tree.api.Node?`)\n\nlist()                                            *nvim_tree.api.marks.list()*\n    Retrieve all marked nodes.\n\n    Return: ~\n        (`nvim_tree.api.Node[]`)\n\nnavigate.next()                          *nvim_tree.api.marks.navigate.next()*\n    Navigate to the next marked node, wraps.\n\nnavigate.prev()                          *nvim_tree.api.marks.navigate.prev()*\n    Navigate to the previous marked node, wraps.\n\nnavigate.select()                      *nvim_tree.api.marks.navigate.select()*\n    Prompts for selection of a marked node, sorted by absolute paths. A folder\n    will be focused, a file will be opened.\n\ntoggle({node})                                  *nvim_tree.api.marks.toggle()*\n    Toggle mark. In visual mode, toggles all nodes in the visual selection.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`) file or directory\n\n\n==============================================================================\nAPI: node                                                 *nvim-tree-api-node*\n\nbuffer.delete({node}, {opts})             *nvim_tree.api.node.buffer.delete()*\n    Deletes node's related buffer, if one exists. Executes |:bdelete| or\n    |:bdelete|!\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`) file\n      • {opts}  (`table?`)\n                • {force}? (`boolean`, default: false) Proceed even if the\n                  buffer is modified.\n\nbuffer.wipe({node}, {opts})                 *nvim_tree.api.node.buffer.wipe()*\n    Wipes node's related buffer, if one exists. Executes |:bwipe| or |:bwipe|!\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`) file\n      • {opts}  (`table?`) optional\n                • {force}? (`boolean`, default: false) Proceed even if the\n                  buffer is modified.\n\ncollapse({node}, {opts})                       *nvim_tree.api.node.collapse()*\n    Collapse the tree under a directory or a file's parent directory.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`) directory or file\n      • {opts}  (`table?`) optional\n                • {keep_buffers}? (`boolean`, default: false) Do not collapse\n                  nodes with open buffers.\n\nexpand({node}, {opts})                           *nvim_tree.api.node.expand()*\n    Recursively expand all nodes under a directory or a file's parent\n    directory.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`) directory or file\n      • {opts}  (`table?`) optional\n                • {expand_until}?\n                  (`fun(expansion_count: integer, node: Node): boolean`)\n                  Return `true` if `node` should be expanded.\n                  `expansion_count` is the total number of folders expanded.\n\n                              *nvim_tree.api.node.navigate.diagnostics.next()*\nnavigate.diagnostics.next({node})\n    Navigate to the next item showing diagnostic status.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`) directory or file\n\n                    *nvim_tree.api.node.navigate.diagnostics.next_recursive()*\nnavigate.diagnostics.next_recursive({node})\n    Navigate to the next item showing diagnostic status, recursively. Needs\n    |nvim_tree.config.diagnostics| {show_on_dirs}\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`) directory or file\n\n                              *nvim_tree.api.node.navigate.diagnostics.prev()*\nnavigate.diagnostics.prev({node})\n    Navigate to the previous item showing diagnostic status.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`) directory or file\n\n                    *nvim_tree.api.node.navigate.diagnostics.prev_recursive()*\nnavigate.diagnostics.prev_recursive({node})\n    Navigate to the previous item showing diagnostic status, recursively.\n    Needs |nvim_tree.config.diagnostics| {show_on_dirs}\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`) directory or file\n\nnavigate.git.next({node})             *nvim_tree.api.node.navigate.git.next()*\n    Navigate to the next item showing git status.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`) directory or file\n\n                            *nvim_tree.api.node.navigate.git.next_recursive()*\nnavigate.git.next_recursive({node})\n    Navigate to the next item showing git status, recursively. Needs\n    |nvim_tree.config.git| {show_on_dirs}\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`) directory or file\n\n                      *nvim_tree.api.node.navigate.git.next_skip_gitignored()*\nnavigate.git.next_skip_gitignored({node})\n    Navigate to the next item showing git status, skipping `.gitignore`\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`) directory or file\n\nnavigate.git.prev({node})             *nvim_tree.api.node.navigate.git.prev()*\n    Navigate to the previous item showing git status.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`) directory or file\n\n                            *nvim_tree.api.node.navigate.git.prev_recursive()*\nnavigate.git.prev_recursive({node})\n    Navigate to the previous item showing git status, recursively. Needs\n    |nvim_tree.config.git| {show_on_dirs}\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`) directory or file\n\n                      *nvim_tree.api.node.navigate.git.prev_skip_gitignored()*\nnavigate.git.prev_skip_gitignored({node})\n    Navigate to the previous item showing git status, skipping `.gitignore`\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`) directory or file\n\n                                   *nvim_tree.api.node.navigate.opened.next()*\nnavigate.opened.next({node})\n    Navigate to the next |bufloaded()| file.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`) directory or file\n\n                                   *nvim_tree.api.node.navigate.opened.prev()*\nnavigate.opened.prev({node})\n    Navigate to the previous |bufloaded()| file.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`) directory or file\n\nnavigate.parent({node})                 *nvim_tree.api.node.navigate.parent()*\n    Navigate to the parent directory of the node.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`) directory or file\n\n                                  *nvim_tree.api.node.navigate.parent_close()*\nnavigate.parent_close({node})\n    Navigate to the parent directory of the node, closing it.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`) directory or file\n\n                                 *nvim_tree.api.node.navigate.sibling.first()*\nnavigate.sibling.first({node})\n    Navigate to the first node in the current node's folder.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`) directory or file\n\n                                  *nvim_tree.api.node.navigate.sibling.last()*\nnavigate.sibling.last({node})\n    Navigate to the last node in the current node's folder.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`) directory or file\n\n                                  *nvim_tree.api.node.navigate.sibling.next()*\nnavigate.sibling.next({node})\n    Navigate to the next node in the current node's folder, wraps.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`) directory or file\n\n                                  *nvim_tree.api.node.navigate.sibling.prev()*\nnavigate.sibling.prev({node})\n    Navigate to the previous node in the current node's folder, wraps.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`) directory or file\n\nopen.edit({node}, {opts})                     *nvim_tree.api.node.open.edit()*\n    • file: open as per |nvim_tree.config.actions.open_file|\n    • directory: expand or collapse\n    • root: change directory up\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`) directory or file\n      • {opts}  (`table?`) optional\n                • {quit_on_open}? (`boolean`, default: false) Quits the tree\n                  when opening the file.\n                • {focus}? (`boolean`, default: false) Keep focus in the tree\n                  when opening the file.\n\nopen.horizontal({node})                 *nvim_tree.api.node.open.horizontal()*\n    Open file in a new horizontal split.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`) file\n\n                              *nvim_tree.api.node.open.horizontal_no_picker()*\nopen.horizontal_no_picker({node})\n    Open file in a new horizontal split without using the window picker.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`) file\n\n                                  *nvim_tree.api.node.open.no_window_picker()*\nopen.no_window_picker({node})\n    Open file without using the window picker.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`) file\n\nopen.preview({node})                       *nvim_tree.api.node.open.preview()*\n    Open file with |'bufhidden'| set to `delete`.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`) directory or file\n\n                                 *nvim_tree.api.node.open.preview_no_picker()*\nopen.preview_no_picker({node})\n    Open file with |'bufhidden'| set to `delete` without using the window\n    picker.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`) directory or file\n\n                               *nvim_tree.api.node.open.replace_tree_buffer()*\nopen.replace_tree_buffer({node})\n    Open file in place: in the nvim-tree window.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`) file\n\nopen.tab({node})                               *nvim_tree.api.node.open.tab()*\n    Open file in a new tab.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`) directory or file\n\nopen.tab_drop({node})                     *nvim_tree.api.node.open.tab_drop()*\n    Switch to tab containing window with selected file if it exists. Open file\n    in new tab otherwise.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`) directory or file\n\n                                *nvim_tree.api.node.open.toggle_group_empty()*\nopen.toggle_group_empty({node})\n    Toggle |nvim_tree.config.renderer| {group_empty} for a directory. Needs\n    {group_empty} set.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`) directory\n\nopen.vertical({node})                     *nvim_tree.api.node.open.vertical()*\n    Open file in a new vertical split.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`) file\n\n                                *nvim_tree.api.node.open.vertical_no_picker()*\nopen.vertical_no_picker({node})\n    Open file in a new vertical split without using the window picker.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`) file\n\nrun.cmd({node})                                 *nvim_tree.api.node.run.cmd()*\n    Enter |cmdline| with the full path of the node and the cursor at the start\n    of the line.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`) directory or file\n\nrun.system({node})                           *nvim_tree.api.node.run.system()*\n    Execute |nvim_tree.config.system_open|.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`) directory or file\n\nshow_info_popup({node})                 *nvim_tree.api.node.show_info_popup()*\n    Open a popup window showing: fullpath, size, accessed, modified, created.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`) directory or file\n\n\n==============================================================================\nAPI: tree                                                 *nvim-tree-api-tree*\n\nchange_root({path})                         *nvim_tree.api.tree.change_root()*\n    Change the tree's root to a path.\n\n    Parameters: ~\n      • {path}  (`string?`) absolute or relative path.\n\nchange_root_to_node({node})         *nvim_tree.api.tree.change_root_to_node()*\n    Change the tree's root to a folder node or the parent of a file node.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`) directory or file\n\n                                  *nvim_tree.api.tree.change_root_to_parent()*\nchange_root_to_parent({node})\n    Change the tree's root to the parent of a node.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`) directory or file\n\nclose()                                           *nvim_tree.api.tree.close()*\n    Close the tree, affecting all tabs as per |nvim_tree.config.tab.sync|\n    {close}\n\nclose_in_all_tabs()                   *nvim_tree.api.tree.close_in_all_tabs()*\n    Close the tree in all tabs.\n\nclose_in_this_tab()                   *nvim_tree.api.tree.close_in_this_tab()*\n    Close the tree in this tab only.\n\ncollapse_all({opts})                       *nvim_tree.api.tree.collapse_all()*\n    Collapse the tree.\n\n    Parameters: ~\n      • {opts}  (`table?`) optional\n                • {keep_buffers}? (`boolean`, default: false) Do not collapse\n                  nodes with open buffers.\n\nexpand_all({node}, {opts})                   *nvim_tree.api.tree.expand_all()*\n    Recursively expand all nodes under the tree root or specified folder.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node?`) directory\n      • {opts}  (`table?`) optional\n                • {expand_until}?\n                  (`fun(expansion_count: integer, node: Node): boolean`)\n                  Return `true` if `node` should be expanded.\n                  `expansion_count` is the total number of folders expanded.\n\nfind_file({opts})                             *nvim_tree.api.tree.find_file()*\n    Find and focus a file or folder in the tree. Finds current buffer unless\n    otherwise specified.\n\n    Parameters: ~\n      • {opts}  (`table?`) optional\n                • {buf}? (`string|integer`) Absolute/relative path OR `bufnr`\n                  to find.\n                • {open}? (`boolean`, default: false) Open the tree if\n                  necessary.\n                • {current_window}? (`boolean`, default: false) Requires\n                  {open}: open in the current window.\n                • {winid}? (`integer`) Open the tree in the specified\n                  |window-ID|, overrides {current_window}\n                • {update_root}? (`boolean`, default: false) Update root after\n                  find, see |nvim_tree.config.update_focused_file|\n                  {update_root}\n                • {focus}? (`boolean`, default: false) Focus the tree window.\n\nfocus()                                           *nvim_tree.api.tree.focus()*\n    Focus the tree, opening it if necessary. Retained for compatibility, use\n    |nvim_tree.api.tree.open()| with no arguments instead.\n\nget_node_under_cursor()           *nvim_tree.api.tree.get_node_under_cursor()*\n    Retrieve the currently focused node.\n\n    Return: ~\n        (`nvim_tree.api.Node?`) nil if tree is not visible.\n\nget_nodes()                                   *nvim_tree.api.tree.get_nodes()*\n    Retrieve a hierarchical list of all the nodes.\n\n    Return: ~\n        (`nvim_tree.api.Node[]`)\n\nis_tree_buf({bufnr})                        *nvim_tree.api.tree.is_tree_buf()*\n    Checks if a buffer is an nvim-tree.\n\n    Parameters: ~\n      • {bufnr}  (`integer?`) 0 or nil for current buffer.\n\n    Return: ~\n        (`boolean`)\n\nis_visible({opts})                           *nvim_tree.api.tree.is_visible()*\n    Checks if nvim-tree is visible on the current, specified or any tab.\n\n    Parameters: ~\n      • {opts}  (`table?`) optional\n                • {tabpage}? (`integer`) |tab-ID| 0 or nil for current.\n                • {any_tabpage}? (`boolean`, default: false) Visible on any\n                  tab.\n\n    Return: ~\n        (`boolean`)\n\nopen({opts})                                       *nvim_tree.api.tree.open()*\n    Open the tree, focusing it if already open.\n\n    Parameters: ~\n      • {opts}  (`table?`) optional\n                • {path}? (`string`) Root directory for the tree\n                • {current_window}? (`boolean`, default: false) Open the tree\n                  in the current window\n                • {winid}? (`integer`) Open the tree in the specified\n                  |window-ID|, overrides {current_window}\n                • {find_file}? (`boolean`, default: false) Find the current\n                  buffer.\n                • {update_root}? (`boolean`, default: false) Update root\n                  following {find_file}, see\n                  |nvim_tree.config.update_focused_file| {update_root}\n\nreload()                                         *nvim_tree.api.tree.reload()*\n    Refresh the tree. Does nothing if closed.\n\nresize({opts})                                   *nvim_tree.api.tree.resize()*\n    Resize the tree, persisting the new size. Resets to\n    |nvim_tree.config.view| {width} when no {opts} provided.\n\n    Only one option is supported, priority order: {width}, {absolute},\n    {relative}.\n\n    {absolute} and {relative} do nothing when |nvim_tree.config.view| {width}\n    is a function.\n\n    Parameters: ~\n      • {opts}  (`table?`) optional\n                • {width}?\n                  (`nvim_tree.config.view.width.spec|nvim_tree.config.view.width`)\n                  New |nvim_tree.config.view| {width} value.\n                • {absolute}? (`integer`) Set the width.\n                • {relative}? (`integer`) Increase or decrease the width.\n\nsearch_node()                               *nvim_tree.api.tree.search_node()*\n    Open the search dialogue.\n\ntoggle({opts})                                   *nvim_tree.api.tree.toggle()*\n    Open or close the tree.\n\n    Parameters: ~\n      • {opts}  (`table?`) optional\n                • {path}? (`string`) Root directory for the tree\n                • {current_window}? (`boolean`, default: false) Open the tree\n                  in the current window\n                • {winid}? (`integer`) Open the tree in the specified\n                  |window-ID|, overrides {current_window}\n                • {find_file}? (`boolean`, default: false) Find the current\n                  buffer.\n                • {update_root}? (`boolean`, default: false) Update root\n                  following {find_file}, see\n                  |nvim_tree.config.update_focused_file| {update_root}\n                • {focus}? (`boolean`, default: true) Focus the tree when\n                  opening.\n\ntoggle_help()                               *nvim_tree.api.tree.toggle_help()*\n    Toggle help view.\n\nwinid({opts})                                     *nvim_tree.api.tree.winid()*\n    Retrieve the window of the open tree.\n\n    Parameters: ~\n      • {opts}  (`table?`) optional\n                • {tabpage}? (`integer`) |tab-ID| 0 or nil for current.\n\n    Return: ~\n        (`integer?`) |window-ID|, nil if tree is not visible.\n\n\n==============================================================================\nClass: Class                                                 *nvim-tree-class*\n\nnvim-tree uses the https://github.com/rxi/classic class framework adding safe\ncasts, instanceof mixin and conventional destructors.\n\nThe key differences between classic and ordinary Lua classes:\n• The constructor |nvim_tree.Class:new()| is not responsible for allocation:\n  `self` is available when the constructor is called.\n• Instances are constructed via the `__call` meta method: `SomeClass(args)`\n\nClasses are conventionally named using camel case e.g. `MyClass`\n\nClasses are created by extending another class: >lua\n\n    local Class = require(\"nvim-tree.classic\")\n\n    ---@class (exact) Fruit: nvim_tree.Class\n    ---@field ...\n    local Fruit = Class:extend()\n\n    ---@class (exact) Apple: Fruit\n    ---@field ...\n    local Apple = Fruit:extend()\n<\n\nImplementing a constructor |nvim_tree.Class:new()| is optional, however it\nmust call the `super` constructor: >lua\n\n    ---@protected\n    ---@param args AppleArgs\n    function Apple:new(args)\n\n      ---@type FruitArgs\n      local fruit_args = ...\n\n      Apple.super.new(self, fruit_args)\n      ---\n<\n\nCreate an instance of a class using the `__call` meta method that will invoke\nthe constructor: >lua\n\n    ---@type AppleArgs\n    local args = ...\n\n    local an_apple = Apple(args)\n    -- above will call `Apple:new(args)`\n<\n\nIn order to strongly type instantiation, the following pattern is used to type\nthe meta method `__call` with arguments and return: >lua\n\n    ---@class (exact) Fruit: nvim_tree.Class\n    ---@field ...\n    local Fruit = Class:extend()\n\n    ---@class (exact) FruitArgs\n    ---@field ...\n\n    ---@class Fruit\n    ---@overload fun(args: FruitArgs): Fruit\n\n    ---@protected\n    ---@param args FruitArgs\n    function Fruit:new(args)\n<\n\n\n*nvim_tree.Class*\n\n    Fields: ~\n      • {super}      (`nvim_tree.Class`) Parent class, `Class` for base\n                     classes.\n      • {new}        (`fun(self: nvim_tree.Class, ...: any)`) See\n                     |nvim_tree.Class:new()|.\n      • {destroy}    (`fun(self: nvim_tree.Class)`) See\n                     |nvim_tree.Class:destroy()|.\n      • {extend}     (`fun(self: nvim_tree.Class): [nvim_tree.Class]`) See\n                     |nvim_tree.Class:extend()|.\n      • {implement}  (`fun(self: nvim_tree.Class, mixin: nvim_tree.Class)`)\n                     See |nvim_tree.Class:implement()|.\n      • {is}         (`fun(self: nvim_tree.Class, class: T): boolean`) See\n                     |nvim_tree.Class:is()|.\n      • {as}         (`fun(self: nvim_tree.Class, class: T): T?`) See\n                     |nvim_tree.Class:as()|.\n      • {nop}        (`fun(self: nvim_tree.Class, ...: any)`) See\n                     |nvim_tree.Class:nop()|.\n\n\nClass:as({class})                                       *nvim_tree.Class:as()*\n    Type safe cast.\n\n    If instance |nvim_tree.Class:is()|, cast to {class} and return it,\n    otherwise nil.\n\n    Parameters: ~\n      • {class}  (`any`) `<T>`\n\n    Return: ~\n        (`any?`) `<T>`\n\nClass:destroy()                                    *nvim_tree.Class:destroy()*\n    Conventional destructor, optional, must be called by the owner.\n\n    Parent destructor must be invoked using the form `Parent.destroy(self)`\n\nClass:extend()                                      *nvim_tree.Class:extend()*\n    Create a new class by extending another class.\n\n    Base classes extend `Class`\n\n    Return: ~\n        (`[nvim_tree.Class]`) child class\n\nClass:implement({mixin})                         *nvim_tree.Class:implement()*\n    Add the methods and fields of a mixin using the form\n    `SomeClass:implement(MixinClass)`\n\n    If the mixin has fields, it must implement a constructor.\n\n    Parameters: ~\n      • {mixin}  (`nvim_tree.Class`)\n\nClass:is({class})                                       *nvim_tree.Class:is()*\n    Instance of.\n\n    Test whether an object is {class}, inherits {class} or implements mixin\n    {class}.\n\n    Parameters: ~\n      • {class}  (`any`) `<T>`\n\n    Return: ~\n        (`boolean`)\n\nClass:new({...})                                       *nvim_tree.Class:new()*\n    Constructor: `self` has been allocated and is available.\n\n    Super constructor must be called using the form\n    `Child.super.new(self, parent_args)`\n\n    Parameters: ~\n      • {...}  (`any`) constructor arguments\n\nClass:nop({...})                                       *nvim_tree.Class:nop()*\n    Utility method to bypass unused param warnings in abstract methods.\n\n    Parameters: ~\n      • {...}  (`any`)\n\n\n==============================================================================\nClass: Decorator                                   *nvim-tree-class-decorator*\n\nHighlighting and icons for nodes are provided by Decorators, see\n|nvim-tree-icons-highlighting| for an overview.\n\nDecorators are rendered in |nvim_tree.config.renderer| {decorators} order of\nadditive precedence, with later decorators applying additively over earlier.\n\nYou may provide your own in addition to the builtin decorators, see\n|nvim-tree-class-decorator-example|.\n\nDecorators may:\n• Add icons\n• Set a highlight group name for the name or icons\n• Override node icon\n\nTo register your decorator:\n• Create a class that extends |nvim_tree.api.Decorator|\n• Register it by adding the class to |nvim_tree.config.renderer| {decorators}\n\nYour decorator will be constructed and executed each time the tree is\nrendered.\n\nYour class must:\n• |nvim_tree.Class:extend()| the interface |nvim_tree.api.Decorator|\n• Provide a no-arguments constructor |nvim_tree.Class:new()| that sets the\n  mandatory fields:\n  • {enabled}\n  • {highlight_range}\n  • {icon_placement}\n• Call |nvim_tree.api.Decorator:define_sign()| in your constructor when\n  {icon_placement} is `\"signcolumn\"`\n\nYour class may:\n• Implement methods to provide decorations:\n  • |nvim_tree.api.Decorator:highlight_group()|\n  • |nvim_tree.api.Decorator:icon_node()|\n  • |nvim_tree.api.Decorator:icons()|\n\n\n*nvim_tree.api.Decorator*\n    Extends: |nvim_tree.Class|\n\n    Decorator interface\n\n    Fields: ~\n      • {enabled}          (`boolean`) Enable this decorator.\n      • {highlight_range}  (`nvim_tree.config.renderer.highlight`) What to\n                           highlight: |nvim_tree.config.renderer.highlight|\n      • {icon_placement}   (`\"none\"|nvim_tree.config.renderer.icons.placement`)\n                           Where to place the icons:\n                           |nvim_tree.config.renderer.icons.placement|\n      • {icon_node}        (`fun(self: nvim_tree.api.Decorator, node: nvim_tree.api.Node): nvim_tree.api.highlighted_string?`)\n                           See |nvim_tree.api.Decorator:icon_node()|.\n      • {icons}            (`fun(self: nvim_tree.api.Decorator, node: nvim_tree.api.Node): nvim_tree.api.highlighted_string[]?`)\n                           See |nvim_tree.api.Decorator:icons()|.\n      • {highlight_group}  (`fun(self: nvim_tree.api.Decorator, node: nvim_tree.api.Node): string?`)\n                           See |nvim_tree.api.Decorator:highlight_group()|.\n      • {define_sign}      (`fun(self: nvim_tree.api.Decorator, icon: nvim_tree.api.highlighted_string?)`)\n                           See |nvim_tree.api.Decorator:define_sign()|.\n\n*nvim_tree.api.highlighted_string*\n    Text or glyphs with optional highlight group names to apply to it.\n\n    Fields: ~\n      • {str}  (`string`) One or many glyphs/characters.\n      • {hl}   (`string[]`) Highlight group names to apply in order. Empty\n               table for no highlighting.\n\n\nDecorator:define_sign({icon})          *nvim_tree.api.Decorator:define_sign()*\n    Defines a sign for an icon. This is mandatory and necessary only when\n    {icon_placement} is `\"signcolumn\"`\n\n    This must be called during your constructor for all icons that you will\n    return from |nvim_tree.api.Decorator:icons()|\n\n    Parameters: ~\n      • {icon}  (`nvim_tree.api.highlighted_string?`) does nothing if nil\n\n                                   *nvim_tree.api.Decorator:highlight_group()*\nDecorator:highlight_group({node})\n    One highlight group that applies additively to the {node} name for\n    {highlight_range}.\n\n    Abstract, optional to implement.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node`)\n\n    Return: ~\n        (`string?`) highlight group name `nil` when no highlighting to apply\n        to the node\n\nDecorator:icon_node({node})              *nvim_tree.api.Decorator:icon_node()*\n    Icon to override for the node.\n\n    Abstract, optional to implement.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node`)\n\n    Return: ~\n        (`nvim_tree.api.highlighted_string?`) icon `nil` for no override\n\nDecorator:icons({node})                      *nvim_tree.api.Decorator:icons()*\n    Icons to add to the node as per {icon_placement}\n\n    Abstract, optional to implement.\n\n    Parameters: ~\n      • {node}  (`nvim_tree.api.Node`)\n\n    Return: ~\n        (`nvim_tree.api.highlighted_string[]?`) icons `nil` or empty table for\n        no icons. Only the first glyph of {str} is used when {icon_placement}\n        is `\"signcolumn\"`\n\n\n==============================================================================\nClass: Decorator example                   *nvim-tree-class-decorator-example*\n\nA decorator class for nodes named \"example\", overriding all builtin decorators\nexcept for Cut.\n• Highlights node name with `IncSearch`\n• Creates two icons `\"1\"` and `\"2\"` placed after the node name, highlighted\n  with `DiffAdd` and `DiffText`\n• Replaces the node icon with `\"N\"`, highlighted with `Error `\n\nCreate a class file `~/.config/nvim/lua/my-decorator.lua`\n\nRequire and register it during |nvim-tree-setup|: >lua\n\n    local MyDecorator = require(\"my-decorator\")\n\n    require(\"nvim-tree\").setup({\n      renderer = {\n        decorators = {\n          \"Git\",\n          \"Open\",\n          \"Hidden\",\n          \"Modified\",\n          \"Bookmark\",\n          \"Diagnostics\",\n          \"Copied\",\n          MyDecorator,\n          \"Cut\",\n        },\n      },\n    })\n<\n\nContents of `my-decorator.lua`: >lua\n\n    ---@class (exact) MyDecorator: nvim_tree.api.Decorator\n    ---@field private my_icon1 nvim_tree.api.highlighted_string\n    ---@field private my_icon2 nvim_tree.api.highlighted_string\n    ---@field private my_icon_node nvim_tree.api.highlighted_string\n    ---@field private my_highlight_group string\n    local MyDecorator = require(\"nvim-tree.api\").Decorator:extend()\n\n    ---Mandatory constructor  :new()  will be called once per tree render, with no arguments.\n    function MyDecorator:new()\n      self.enabled            = true\n      self.highlight_range    = \"name\"\n      self.icon_placement     = \"after\"\n\n      -- create your icons and highlights once, applied to every node\n      self.my_icon1           = { str = \"1\", hl = { \"DiffAdd\" } }\n      self.my_icon2           = { str = \"2\", hl = { \"DiffText\" } }\n      self.my_icon_node       = { str = \"N\", hl = { \"Error\" } }\n      self.my_highlight_group = \"IncSearch\"\n\n      -- Define the icon signs only once\n      -- Only needed if you are using icon_placement = \"signcolumn\"\n      -- self:define_sign(self.my_icon1)\n      -- self:define_sign(self.my_icon2)\n    end\n\n    ---Override node icon\n    ---@param node nvim_tree.api.Node\n    ---@return nvim_tree.api.highlighted_string? icon_node\n    function MyDecorator:icon_node(node)\n      if node.name == \"example\" then\n        return self.my_icon_node\n      else\n        return nil\n      end\n    end\n\n    ---Return two icons for DecoratorIconPlacement \"after\"\n    ---@param node nvim_tree.api.Node\n    ---@return nvim_tree.api.highlighted_string[]? icons\n    function MyDecorator:icons(node)\n      if node.name == \"example\" then\n        return { self.my_icon1, self.my_icon2, }\n      else\n        return nil\n      end\n    end\n\n    ---Exactly one highlight group for DecoratorHighlightRange \"name\"\n    ---@param node nvim_tree.api.Node\n    ---@return string? highlight_group\n    function MyDecorator:highlight_group(node)\n      if node.name == \"example\" then\n        return self.my_highlight_group\n      else\n        return nil\n      end\n    end\n\n    return MyDecorator\n<\n\n\n\n vim:tw=78:ts=8:sw=4:sts=4:et:ft=help:norl:\n"
  },
  {
    "path": "lua/nvim-tree/_meta/api/appearance.lua",
    "content": "---@meta\nlocal nvim_tree = { api = { appearance = {} } }\n\n---\n---Open a new buffer displaying all nvim-tree highlight groups, their link chain and concrete definition.\n---\n---Similar to `:so $VIMRUNTIME/syntax/hitest.vim` as per |:highlight|\n---\nfunction nvim_tree.api.appearance.hi_test() end\n\nreturn nvim_tree.api.appearance\n"
  },
  {
    "path": "lua/nvim-tree/_meta/api/commands.lua",
    "content": "---@meta\nlocal nvim_tree = { api = { commands = {} } }\n\n---\n---@class nvim_tree.api.commands.Command\n---@inlinedoc\n---\n---@field name string name of the `:NvimTree*` command\n---@field command fun(args: vim.api.keyset.create_user_command.command_args) function that the command will execute\n---@field opts vim.api.keyset.user_command [command-attributes]\n\n---\n---Retrieve all [nvim-tree-commands]\n---\n---They have been created via [nvim_create_user_command()], see also [lua-guide-commands-create]\n---\n---@return nvim_tree.api.commands.Command[]\nfunction nvim_tree.api.commands.get() end\n\nreturn nvim_tree.api.commands\n"
  },
  {
    "path": "lua/nvim-tree/_meta/api/config.lua",
    "content": "---@meta\nlocal nvim_tree = { api = { config = {} } }\n\n---\n---Default nvim-tree config.\n---\n---@return nvim_tree.config immutable deep clone\nfunction nvim_tree.api.config.default() end\n\n---\n---Global current nvim-tree config.\n---\n---@return nvim_tree.config immutable deep clone\nfunction nvim_tree.api.config.global() end\n\n---\n---Reference to config passed to [nvim-tree-setup]\n---\n---@return nvim_tree.config? nil when no config passed to setup\nfunction nvim_tree.api.config.user() end\n\nnvim_tree.api.config.mappings = {}\n\n---@deprecated use `nvim_tree.api.map.keymap.current()`\nfunction nvim_tree.api.config.mappings.get_keymap() end\n\n---@deprecated use `nvim_tree.api.map.keymap.default()`\nfunction nvim_tree.api.config.mappings.get_keymap_default() end\n\n---@deprecated use `nvim_tree.api.map.on_attach.default()`\nfunction nvim_tree.api.config.mappings.default_on_attach(bufnr) end\n\nreturn nvim_tree.api.config\n"
  },
  {
    "path": "lua/nvim-tree/_meta/api/decorator.lua",
    "content": "---@meta\n\n---@brief\n---Highlighting and icons for nodes are provided by Decorators, see [nvim-tree-icons-highlighting] for an overview.\n---\n---Decorators are rendered in [nvim_tree.config.renderer] {decorators} order of additive precedence, with later decorators applying additively over earlier.\n---\n---You may provide your own in addition to the builtin decorators, see |nvim-tree-class-decorator-example|.\n---\n---Decorators may:\n---- Add icons\n---- Set a highlight group name for the name or icons\n---- Override node icon\n---\n---To register your decorator:\n---- Create a class that extends [nvim_tree.api.Decorator]\n---- Register it by adding the class to [nvim_tree.config.renderer] {decorators}\n---\n---Your decorator will be constructed and executed each time the tree is rendered.\n---\n---Your class must:\n---- [nvim_tree.Class:extend()] the interface [nvim_tree.api.Decorator]\n---- Provide a no-arguments constructor [nvim_tree.Class:new()] that sets the mandatory fields:\n---   - {enabled}\n---   - {highlight_range}\n---   - {icon_placement}\n---- Call [nvim_tree.api.Decorator:define_sign()] in your constructor when {icon_placement} is `\"signcolumn\"`\n---\n---Your class may:\n---- Implement methods to provide decorations:\n---   - [nvim_tree.api.Decorator:highlight_group()]\n---   - [nvim_tree.api.Decorator:icon_node()]\n---   - [nvim_tree.api.Decorator:icons()]\n\nlocal nvim_tree = { api = {} }\n\nlocal Class = require(\"nvim-tree.classic\")\n\n---\n---Text or glyphs with optional highlight group names to apply to it.\n---\n---@class nvim_tree.api.highlighted_string\n---\n---One or many glyphs/characters.\n---@field str string\n---\n---Highlight group names to apply in order. Empty table for no highlighting.\n---@field hl string[]\n\n\n---\n---Decorator interface\n---\n---@class nvim_tree.api.Decorator: nvim_tree.Class\n---\n---Enable this decorator.\n---@field enabled boolean\n---\n---What to highlight: [nvim_tree.config.renderer.highlight]\n---@field highlight_range nvim_tree.config.renderer.highlight\n---\n---Where to place the icons: [nvim_tree.config.renderer.icons.placement]\n---@field icon_placement \"none\"|nvim_tree.config.renderer.icons.placement\n---\nlocal Decorator = Class:extend()\nnvim_tree.api.Decorator = Decorator\n\n---\n---Icon to override for the node.\n---\n---Abstract, optional to implement.\n---\n---@param node nvim_tree.api.Node\n---@return nvim_tree.api.highlighted_string? icon `nil` for no override\nfunction Decorator:icon_node(node) end\n\n---\n---Icons to add to the node as per {icon_placement}\n---\n---Abstract, optional to implement.\n---\n---@param node nvim_tree.api.Node\n---@return nvim_tree.api.highlighted_string[]? icons `nil` or empty table for no icons. Only the first glyph of {str} is used when {icon_placement} is `\"signcolumn\"`\nfunction Decorator:icons(node) end\n\n---\n---One highlight group that applies additively to the {node} name for {highlight_range}.\n---\n---Abstract, optional to implement.\n---\n---@param node nvim_tree.api.Node\n---@return string? highlight group name `nil` when no highlighting to apply to the node\nfunction Decorator:highlight_group(node) end\n\n---\n---Defines a sign for an icon. This is mandatory and necessary only when {icon_placement} is `\"signcolumn\"`\n---\n---This must be called during your constructor for all icons that you will return from [nvim_tree.api.Decorator:icons()]\n---\n---@param icon nvim_tree.api.highlighted_string? does nothing if nil\nfunction Decorator:define_sign(icon) end\n\nreturn nvim_tree.api.Decorator\n"
  },
  {
    "path": "lua/nvim-tree/_meta/api/decorator_example.lua",
    "content": "---@meta\nerror(\"Cannot require a meta file\")\n\n---@brief\n---\n---A decorator class for nodes named \"example\", overriding all builtin decorators except for Cut.\n---- Highlights node name with `IncSearch`\n---- Creates two icons `\"1\"` and `\"2\"` placed after the node name, highlighted with `DiffAdd` and `DiffText`\n---- Replaces the node icon with `\"N\"`, highlighted with `Error `\n---\n---Create a class file `~/.config/nvim/lua/my-decorator.lua`\n---\n---Require and register it during |nvim-tree-setup|:\n---```lua\n---\n--- local MyDecorator = require(\"my-decorator\")\n---\n--- require(\"nvim-tree\").setup({\n---   renderer = {\n---     decorators = {\n---       \"Git\",\n---       \"Open\",\n---       \"Hidden\",\n---       \"Modified\",\n---       \"Bookmark\",\n---       \"Diagnostics\",\n---       \"Copied\",\n---       MyDecorator,\n---       \"Cut\",\n---     },\n---   },\n--- })\n---```\n---Contents of `my-decorator.lua`:\n---```lua\n---\n--- ---@class (exact) MyDecorator: nvim_tree.api.Decorator\n--- ---@field private my_icon1 nvim_tree.api.highlighted_string\n--- ---@field private my_icon2 nvim_tree.api.highlighted_string\n--- ---@field private my_icon_node nvim_tree.api.highlighted_string\n--- ---@field private my_highlight_group string\n--- local MyDecorator = require(\"nvim-tree.api\").Decorator:extend()\n---\n--- ---Mandatory constructor  :new()  will be called once per tree render, with no arguments.\n--- function MyDecorator:new()\n---   self.enabled            = true\n---   self.highlight_range    = \"name\"\n---   self.icon_placement     = \"after\"\n---\n---   -- create your icons and highlights once, applied to every node\n---   self.my_icon1           = { str = \"1\", hl = { \"DiffAdd\" } }\n---   self.my_icon2           = { str = \"2\", hl = { \"DiffText\" } }\n---   self.my_icon_node       = { str = \"N\", hl = { \"Error\" } }\n---   self.my_highlight_group = \"IncSearch\"\n---\n---   -- Define the icon signs only once\n---   -- Only needed if you are using icon_placement = \"signcolumn\"\n---   -- self:define_sign(self.my_icon1)\n---   -- self:define_sign(self.my_icon2)\n--- end\n---\n--- ---Override node icon\n--- ---@param node nvim_tree.api.Node\n--- ---@return nvim_tree.api.highlighted_string? icon_node\n--- function MyDecorator:icon_node(node)\n---   if node.name == \"example\" then\n---     return self.my_icon_node\n---   else\n---     return nil\n---   end\n--- end\n---\n--- ---Return two icons for DecoratorIconPlacement \"after\"\n--- ---@param node nvim_tree.api.Node\n--- ---@return nvim_tree.api.highlighted_string[]? icons\n--- function MyDecorator:icons(node)\n---   if node.name == \"example\" then\n---     return { self.my_icon1, self.my_icon2, }\n---   else\n---     return nil\n---   end\n--- end\n---\n--- ---Exactly one highlight group for DecoratorHighlightRange \"name\"\n--- ---@param node nvim_tree.api.Node\n--- ---@return string? highlight_group\n--- function MyDecorator:highlight_group(node)\n---   if node.name == \"example\" then\n---     return self.my_highlight_group\n---   else\n---     return nil\n---   end\n--- end\n---\n--- return MyDecorator\n---```\n"
  },
  {
    "path": "lua/nvim-tree/_meta/api/deprecated.lua",
    "content": "---@meta\n-- Deprecated top level API modules.\n-- Remember to add mappings in legacy.lua `api_map`\n\nlocal nvim_tree = { api = {} }\n\nnvim_tree.api.live_filter = {}\n\n---@deprecated use `nvim_tree.api.filter.live.start()`\nfunction nvim_tree.api.live_filter.start() end\n\n---@deprecated use `nvim_tree.api.filter.live.clear()`\nfunction nvim_tree.api.live_filter.clear() end\n\nnvim_tree.api.diagnostics = {}\n\n---@deprecated use `nvim_tree.api.appearance.hi_test()`\nfunction nvim_tree.api.diagnostics.hi_test() end\n\nnvim_tree.api.decorator = {}\n\n---@class nvim_tree.api.decorator.UserDecorator: nvim_tree.api.Decorator\n---@deprecated use `nvim_tree.api.Decorator`\nnvim_tree.api.decorator.UserDecorator = nvim_tree.api.Decorator\n\nreturn nvim_tree.api\n"
  },
  {
    "path": "lua/nvim-tree/_meta/api/events.lua",
    "content": "---@meta\nlocal nvim_tree = { api = { events = {} } }\n\n---@enum nvim_tree.api.events.Event\nnvim_tree.api.events.Event = {\n  Ready = \"Ready\",\n  WillRenameNode = \"WillRenameNode\",\n  NodeRenamed = \"NodeRenamed\",\n  TreePreOpen = \"TreePreOpen\",\n  TreeOpen = \"TreeOpen\",\n  TreeClose = \"TreeClose\",\n  WillCreateFile = \"WillCreateFile\",\n  FileCreated = \"FileCreated\",\n  WillRemoveFile = \"WillRemoveFile\",\n  FileRemoved = \"FileRemoved\",\n  FolderCreated = \"FolderCreated\",\n  FolderRemoved = \"FolderRemoved\",\n  Resize = \"Resize\",\n  TreeAttachedPost = \"TreeAttachedPost\",\n  TreeRendered = \"TreeRendered\",\n}\n\n---\n---Register a handler for an event, see [nvim-tree-events].\n---\n---@param event_type nvim_tree.api.events.Event [nvim_tree_events_kind]\n---@param callback fun(payload: table?)\nfunction nvim_tree.api.events.subscribe(event_type, callback) end\n\nreturn nvim_tree.api.events\n"
  },
  {
    "path": "lua/nvim-tree/_meta/api/filter.lua",
    "content": "---@meta\nlocal nvim_tree = { api = { filter = {} } }\n\n---\n---Toggle [nvim_tree.config.filters] {enable} which applies to ALL filters.\n---\nfunction nvim_tree.api.filter.toggle() end\n\nnvim_tree.api.filter.live = {}\n\n---\n---Enter live filter mode. Opens an input window with [filetype] `NvimTreeFilter`\n---\nfunction nvim_tree.api.filter.live.start() end\n\n---\n---Exit live filter mode.\n---\nfunction nvim_tree.api.filter.live.clear() end\n\nnvim_tree.api.filter.git = {}\n\nnvim_tree.api.filter.git.clean = {}\n\n---\n---Toggle [nvim_tree.config.filters] {git_clean} filter.\n---\nfunction nvim_tree.api.filter.git.clean.toggle() end\n\nnvim_tree.api.filter.git.ignored = {}\n\n---\n---Toggle [nvim_tree.config.filters] {git_ignored} filter.\n---\nfunction nvim_tree.api.filter.git.ignored.toggle() end\n\nnvim_tree.api.filter.dotfiles = {}\n\n---\n---Toggle [nvim_tree.config.filters] {dotfiles} filter.\n---\nfunction nvim_tree.api.filter.dotfiles.toggle() end\n\nnvim_tree.api.filter.no_buffer = {}\n\n---\n---Toggle [nvim_tree.config.filters] {no_buffer} filter.\n---\nfunction nvim_tree.api.filter.no_buffer.toggle() end\n\nnvim_tree.api.filter.no_bookmark = {}\n\n---\n---Toggle [nvim_tree.config.filters] {no_bookmark} filter.\n---\nfunction nvim_tree.api.filter.no_bookmark.toggle() end\n\nnvim_tree.api.filter.custom = {}\n\n---\n---Toggle [nvim_tree.config.filters] {custom} filter.\n---\nfunction nvim_tree.api.filter.custom.toggle() end\n\nreturn nvim_tree.api.filter\n"
  },
  {
    "path": "lua/nvim-tree/_meta/api/fs.lua",
    "content": "---@meta\nlocal nvim_tree = { api = { fs = {} } }\n\n---\n---Clear the nvim-tree clipboard.\n---\nfunction nvim_tree.api.fs.clear_clipboard() end\n\nnvim_tree.api.fs.copy = {}\n\n---\n---Copy the absolute path to the system clipboard.\n---\n---@param node? nvim_tree.api.Node\nfunction nvim_tree.api.fs.copy.absolute_path(node) end\n\n---\n---Copy the name with extension omitted to the system clipboard.\n---\n---@param node? nvim_tree.api.Node\nfunction nvim_tree.api.fs.copy.basename(node) end\n\n---\n---Copy the name to the system clipboard.\n---\n---@param node? nvim_tree.api.Node\nfunction nvim_tree.api.fs.copy.filename(node) end\n\n---\n---Copy to the nvim-tree clipboard.\n---In visual mode, copies all nodes in the visual selection.\n---\n---@param node? nvim_tree.api.Node\nfunction nvim_tree.api.fs.copy.node(node) end\n\n---\n---Copy the path relative to the tree root to the system clipboard.\n---\n---@param node? nvim_tree.api.Node\nfunction nvim_tree.api.fs.copy.relative_path(node) end\n\n---\n---Prompt to create a file or directory.\n---\n---When {node} is a file it will be created in the parent directory.\n---\n---Use a trailing `\"/\"` to create a directory e.g. `\"foo/\"`\n---\n---Multiple directories/files may be created e.g. `\"foo/bar/baz\"`\n---\n---@param node? nvim_tree.api.Node\nfunction nvim_tree.api.fs.create(node) end\n\n---\n---Cut to the nvim-tree clipboard.\n---In visual mode, cuts all nodes in the visual selection.\n---\n---@param node? nvim_tree.api.Node\nfunction nvim_tree.api.fs.cut(node) end\n\n---\n---Paste from the nvim-tree clipboard.\n---\n---If {node} is a file it will pasted in the parent directory.\n---\n---@param node? nvim_tree.api.Node\nfunction nvim_tree.api.fs.paste(node) end\n\n---\n---Print the contents of the nvim-tree clipboard.\n---\nfunction nvim_tree.api.fs.print_clipboard() end\n\n---\n---Delete from the file system.\n---In visual mode, deletes all nodes in the visual selection with a single prompt.\n---\n---@param node? nvim_tree.api.Node\nfunction nvim_tree.api.fs.remove(node) end\n\n---\n---Prompt to rename by name.\n---\n---@param node? nvim_tree.api.Node\nfunction nvim_tree.api.fs.rename(node) end\n\n---\n---Prompt to rename by name with extension omitted.\n---\n---@param node? nvim_tree.api.Node\nfunction nvim_tree.api.fs.rename_basename(node) end\n\n---\n---Prompt to rename by absolute path.\n---\n---@param node? nvim_tree.api.Node\nfunction nvim_tree.api.fs.rename_full(node) end\n\n---\n---Prompt to rename.\n---\n---@param node? nvim_tree.api.Node\nfunction nvim_tree.api.fs.rename_node(node) end\n\n---\n---Prompt to rename by absolute path with name omitted.\n---\n---@param node? nvim_tree.api.Node\nfunction nvim_tree.api.fs.rename_sub(node) end\n\n---\n---Trash as per |nvim_tree.config.trash|.\n---In visual mode, trashes all nodes in the visual selection with a single prompt.\n---\n---@param node? nvim_tree.api.Node\nfunction nvim_tree.api.fs.trash(node) end\n\nreturn nvim_tree.api.fs\n"
  },
  {
    "path": "lua/nvim-tree/_meta/api/git.lua",
    "content": "---@meta\nlocal nvim_tree = { api = { git = {} } }\n\n---\n---Update the git status of the entire tree.\n---\nfunction nvim_tree.api.git.reload() end\n\nreturn nvim_tree.api.git\n"
  },
  {
    "path": "lua/nvim-tree/_meta/api/map.lua",
    "content": "---@meta\nlocal nvim_tree = { api = { map = {} } }\n\nnvim_tree.api.map.keymap = {}\n\n---\n---Retrieve all buffer local mappings for nvim-tree. These are the mappings that are applied by [nvim_tree.config] {on_attach}, which may include default mappings.\n---\n---@return vim.api.keyset.get_keymap[]\nfunction nvim_tree.api.map.keymap.current() end\n\n---\n--- Retrieves the buffer local mappings for nvim-tree that are applied by [nvim_tree.api.map.on_attach.default()]\n---\n---@return vim.api.keyset.get_keymap[]\nfunction nvim_tree.api.map.keymap.default() end\n\nnvim_tree.api.map.on_attach = {}\n\n---\n---Apply all [nvim-tree-mappings-default]. Call from your [nvim_tree.config] {on_attach}.\n---\n---@param bufnr integer use the `bufnr` passed to {on_attach}\nfunction nvim_tree.api.map.on_attach.default(bufnr) end\n\nreturn nvim_tree.api.map\n"
  },
  {
    "path": "lua/nvim-tree/_meta/api/marks.lua",
    "content": "---@meta\nlocal nvim_tree = { api = { marks = {} } }\n\n---\n---Return the node if it is marked.\n---\n---@param node? nvim_tree.api.Node file or directory\n---@return nvim_tree.api.Node?\nfunction nvim_tree.api.marks.get(node) end\n\n---\n---Retrieve all marked nodes.\n---\n---@return nvim_tree.api.Node[]\nfunction nvim_tree.api.marks.list() end\n\n---\n---Toggle mark. In visual mode, toggles all nodes in the visual selection.\n---\n---@param node? nvim_tree.api.Node file or directory\nfunction nvim_tree.api.marks.toggle(node) end\n\n---\n---Clear all marks.\n---\nfunction nvim_tree.api.marks.clear() end\n\nnvim_tree.api.marks.bulk = {}\n\n---\n---Delete all marked, prompting if [nvim_tree.config.ui.confirm] {remove}\n---\nfunction nvim_tree.api.marks.bulk.delete() end\n\n---\n---Delete all marked, prompting if [nvim_tree.config.ui.confirm] {trash}\n---\nfunction nvim_tree.api.marks.bulk.trash() end\n\n---\n---Prompts for a directory to move all marked nodes into.\n---\nfunction nvim_tree.api.marks.bulk.move() end\n\nnvim_tree.api.marks.navigate = {}\n\n---\n---Navigate to the next marked node, wraps.\n---\nfunction nvim_tree.api.marks.navigate.next() end\n\n---\n---Navigate to the previous marked node, wraps.\n---\nfunction nvim_tree.api.marks.navigate.prev() end\n\n---\n---Prompts for selection of a marked node, sorted by absolute paths.\n---A folder will be focused, a file will be opened.\n---\nfunction nvim_tree.api.marks.navigate.select() end\n\nreturn nvim_tree.api.marks\n"
  },
  {
    "path": "lua/nvim-tree/_meta/api/node.lua",
    "content": "---@meta\nlocal nvim_tree = { api = { node = {} } }\n\nnvim_tree.api.node.open = {}\n\n---\n---@class nvim_tree.api.node.open.Opts\n---@inlinedoc\n---\n---Quits the tree when opening the file.\n---(default: false)\n---@field quit_on_open? boolean\n---\n---Keep focus in the tree when opening the file.\n---(default: false)\n---@field focus? boolean\n\n\n---\n---- file: open as per [nvim_tree.config.actions.open_file]\n---- directory: expand or collapse\n---- root: change directory up\n---\n---@param node? nvim_tree.api.Node directory or file\n---@param opts? nvim_tree.api.node.open.Opts optional\nfunction nvim_tree.api.node.open.edit(node, opts) end\n\n---\n---Open file in a new horizontal split.\n---\n---@param node? nvim_tree.api.Node file\nfunction nvim_tree.api.node.open.horizontal(node) end\n\n---\n---Open file in a new horizontal split without using the window picker.\n---\n---@param node? nvim_tree.api.Node file\nfunction nvim_tree.api.node.open.horizontal_no_picker(node) end\n\n---\n---Open file without using the window picker.\n---\n---@param node? nvim_tree.api.Node file\nfunction nvim_tree.api.node.open.no_window_picker(node) end\n\n---\n---Open file with ['bufhidden'] set to `delete`.\n---\n---@param node? nvim_tree.api.Node directory or file\nfunction nvim_tree.api.node.open.preview(node) end\n\n---\n---Open file with ['bufhidden'] set to `delete` without using the window picker.\n---\n---@param node? nvim_tree.api.Node directory or file\nfunction nvim_tree.api.node.open.preview_no_picker(node) end\n\n---\n---Open file in place: in the nvim-tree window.\n---\n---@param node? nvim_tree.api.Node file\nfunction nvim_tree.api.node.open.replace_tree_buffer(node) end\n\n---\n---Open file in a new tab.\n---\n---@param node? nvim_tree.api.Node directory or file\nfunction nvim_tree.api.node.open.tab(node) end\n\n---\n---Switch to tab containing window with selected file if it exists. Open file in new tab otherwise.\n---\n---@param node? nvim_tree.api.Node directory or file\nfunction nvim_tree.api.node.open.tab_drop(node) end\n\n---\n---Toggle [nvim_tree.config.renderer] {group_empty} for a directory. Needs {group_empty} set.\n---\n---@param node? nvim_tree.api.Node directory\nfunction nvim_tree.api.node.open.toggle_group_empty(node) end\n\n---\n---Open file in a new vertical split.\n---\n---@param node? nvim_tree.api.Node file\nfunction nvim_tree.api.node.open.vertical(node) end\n\n---\n---Open file in a new vertical split without using the window picker.\n---\n---@param node? nvim_tree.api.Node file\nfunction nvim_tree.api.node.open.vertical_no_picker(node) end\n\n---\n---@class nvim_tree.api.node.buffer.RemoveOpts\n---@inlinedoc\n---\n---Proceed even if the buffer is modified.\n---(default: false)\n---@field force? boolean\n\n\nnvim_tree.api.node.buffer = {}\n\n---\n---Deletes node's related buffer, if one exists. Executes [:bdelete] or [:bdelete]!\n---\n---@param node? nvim_tree.api.Node file\n---@param opts? nvim_tree.api.node.buffer.RemoveOpts\nfunction nvim_tree.api.node.buffer.delete(node, opts) end\n\n---\n---Wipes node's related buffer, if one exists. Executes [:bwipe] or [:bwipe]!\n---\n---@param node? nvim_tree.api.Node file\n---@param opts? nvim_tree.api.node.buffer.RemoveOpts optional\nfunction nvim_tree.api.node.buffer.wipe(node, opts) end\n\n---\n---Collapse the tree under a directory or a file's parent directory.\n---\n---@param node? nvim_tree.api.Node directory or file\n---@param opts? nvim_tree.api.node.collapse.Opts optional\nfunction nvim_tree.api.node.collapse(node, opts) end\n\n---@class nvim_tree.api.node.collapse.Opts\n---@inlinedoc\n---\n---Do not collapse nodes with open buffers.\n---(default: false)\n---@field keep_buffers? boolean\n\n---\n---Recursively expand all nodes under a directory or a file's parent directory.\n---\n---@param node? nvim_tree.api.Node directory or file\n---@param opts? nvim_tree.api.node.expand.Opts optional\nfunction nvim_tree.api.node.expand(node, opts) end\n\n---@class nvim_tree.api.node.expand.Opts\n---@inlinedoc\n---\n---Return `true` if `node` should be expanded. `expansion_count` is the total number of folders expanded.\n---@field expand_until? fun(expansion_count: integer, node: Node): boolean\n\n\nnvim_tree.api.node.navigate = {}\n\n---\n---Navigate to the parent directory of the node.\n---\n---@param node? nvim_tree.api.Node directory or file\nfunction nvim_tree.api.node.navigate.parent(node) end\n\n---\n---Navigate to the parent directory of the node, closing it.\n---\n---@param node? nvim_tree.api.Node directory or file\nfunction nvim_tree.api.node.navigate.parent_close(node) end\n\nnvim_tree.api.node.navigate.diagnostics = {}\n\n---\n---Navigate to the next item showing diagnostic status.\n---\n---@param node? nvim_tree.api.Node directory or file\nfunction nvim_tree.api.node.navigate.diagnostics.next(node) end\n\n---\n---Navigate to the next item showing diagnostic status, recursively. Needs [nvim_tree.config.diagnostics] {show_on_dirs}\n---\n---@param node? nvim_tree.api.Node directory or file\nfunction nvim_tree.api.node.navigate.diagnostics.next_recursive(node) end\n\n---\n---Navigate to the previous item showing diagnostic status.\n---\n---@param node? nvim_tree.api.Node directory or file\nfunction nvim_tree.api.node.navigate.diagnostics.prev(node) end\n\n---\n---Navigate to the previous item showing diagnostic status, recursively. Needs [nvim_tree.config.diagnostics] {show_on_dirs}\n---\n---@param node? nvim_tree.api.Node directory or file\nfunction nvim_tree.api.node.navigate.diagnostics.prev_recursive(node) end\n\nnvim_tree.api.node.navigate.git = {}\n\n---\n---Navigate to the next item showing git status.\n---\n---@param node? nvim_tree.api.Node directory or file\nfunction nvim_tree.api.node.navigate.git.next(node) end\n\n---\n---Navigate to the next item showing git status, recursively. Needs [nvim_tree.config.git] {show_on_dirs}\n---\n---@param node? nvim_tree.api.Node directory or file\nfunction nvim_tree.api.node.navigate.git.next_recursive(node) end\n\n---\n---Navigate to the next item showing git status, skipping `.gitignore`\n---\n---@param node? nvim_tree.api.Node directory or file\nfunction nvim_tree.api.node.navigate.git.next_skip_gitignored(node) end\n\n---\n---Navigate to the previous item showing git status.\n---\n---@param node? nvim_tree.api.Node directory or file\nfunction nvim_tree.api.node.navigate.git.prev(node) end\n\n---\n---Navigate to the previous item showing git status, recursively. Needs [nvim_tree.config.git] {show_on_dirs}\n---\n---@param node? nvim_tree.api.Node directory or file\nfunction nvim_tree.api.node.navigate.git.prev_recursive(node) end\n\n---\n---Navigate to the previous item showing git status, skipping `.gitignore`\n---\n---@param node? nvim_tree.api.Node directory or file\nfunction nvim_tree.api.node.navigate.git.prev_skip_gitignored(node) end\n\nnvim_tree.api.node.navigate.opened = {}\n\n---\n---Navigate to the next [bufloaded()] file.\n---\n---@param node? nvim_tree.api.Node directory or file\nfunction nvim_tree.api.node.navigate.opened.next(node) end\n\n---\n---Navigate to the previous [bufloaded()] file.\n---\n---@param node? nvim_tree.api.Node directory or file\nfunction nvim_tree.api.node.navigate.opened.prev(node) end\n\nnvim_tree.api.node.navigate.sibling = {}\n\n---\n---Navigate to the first node in the current node's folder.\n---\n---@param node? nvim_tree.api.Node directory or file\nfunction nvim_tree.api.node.navigate.sibling.first(node) end\n\n---\n---Navigate to the last node in the current node's folder.\n---\n---@param node? nvim_tree.api.Node directory or file\nfunction nvim_tree.api.node.navigate.sibling.last(node) end\n\n---\n---Navigate to the next node in the current node's folder, wraps.\n---\n---@param node? nvim_tree.api.Node directory or file\nfunction nvim_tree.api.node.navigate.sibling.next(node) end\n\n---\n---Navigate to the previous node in the current node's folder, wraps.\n---\n---@param node? nvim_tree.api.Node directory or file\nfunction nvim_tree.api.node.navigate.sibling.prev(node) end\n\nnvim_tree.api.node.run = {}\n\n---\n---Enter [cmdline] with the full path of the node and the cursor at the start of the line.\n---\n---@param node? nvim_tree.api.Node directory or file\nfunction nvim_tree.api.node.run.cmd(node) end\n\n---\n---Execute [nvim_tree.config.system_open].\n---\n---@param node? nvim_tree.api.Node directory or file\nfunction nvim_tree.api.node.run.system(node) end\n\n---\n---Open a popup window showing: fullpath, size, accessed, modified, created.\n---\n---@param node? nvim_tree.api.Node directory or file\nfunction nvim_tree.api.node.show_info_popup(node) end\n\nreturn nvim_tree.api.node\n"
  },
  {
    "path": "lua/nvim-tree/_meta/api/tree.lua",
    "content": "---@meta\nlocal nvim_tree = { api = { tree = {} } }\n\n---\n---Open the tree, focusing it if already open.\n---\n---@param opts? nvim_tree.api.tree.open.Opts optional\nfunction nvim_tree.api.tree.open(opts) end\n\n---@class nvim_tree.api.tree.open.Opts\n---@inlinedoc\n---\n---Root directory for the tree\n---@field path? string\n---\n---Open the tree in the current window\n---(default: false)\n---@field current_window? boolean\n---\n---Open the tree in the specified [window-ID], overrides {current_window}\n---@field winid? integer\n---\n---Find the current buffer.\n---(default: false)\n---@field find_file? boolean\n---\n---Update root following {find_file}, see [nvim_tree.config.update_focused_file] {update_root}\n---(default: false)\n---@field update_root? boolean\n\n---\n---Open or close the tree.\n---\n---@param opts? nvim_tree.api.tree.toggle.Opts optional\nfunction nvim_tree.api.tree.toggle(opts) end\n\n---@class nvim_tree.api.tree.toggle.Opts\n---@inlinedoc\n---\n---Root directory for the tree\n---@field path? string\n---\n---Open the tree in the current window\n---(default: false)\n---@field current_window? boolean\n---\n---Open the tree in the specified [window-ID], overrides {current_window}\n---@field winid? integer\n---\n---Find the current buffer.\n---(default: false)\n---@field find_file? boolean\n---\n---Update root following {find_file}, see [nvim_tree.config.update_focused_file] {update_root}\n---(default: false)\n---@field update_root? boolean\n---\n---Focus the tree when opening.\n---(default: true)\n---@field focus? boolean\n\n---\n---Close the tree, affecting all tabs as per [nvim_tree.config.tab.sync] {close}\n---\nfunction nvim_tree.api.tree.close() end\n\n---\n---Close the tree in this tab only.\n---\nfunction nvim_tree.api.tree.close_in_this_tab() end\n\n---\n---Close the tree in all tabs.\n---\nfunction nvim_tree.api.tree.close_in_all_tabs() end\n\n---\n---Focus the tree, opening it if necessary. Retained for compatibility, use [nvim_tree.api.tree.open()] with no arguments instead.\n---\nfunction nvim_tree.api.tree.focus() end\n\n---\n---Refresh the tree. Does nothing if closed.\n---\nfunction nvim_tree.api.tree.reload() end\n\n---\n---Resize the tree, persisting the new size. Resets to [nvim_tree.config.view] {width} when no {opts} provided.\n---\n---Only one option is supported, priority order: {width}, {absolute}, {relative}.\n---\n---{absolute} and {relative} do nothing when [nvim_tree.config.view] {width} is a function.\n---\n---@param opts? nvim_tree.api.tree.resize.Opts optional\nfunction nvim_tree.api.tree.resize(opts) end\n\n---@class nvim_tree.api.tree.resize.Opts\n---@inlinedoc\n---\n---New [nvim_tree.config.view] {width} value.\n---@field width? nvim_tree.config.view.width.spec|nvim_tree.config.view.width\n---\n---Set the width.\n---@field absolute? integer\n---\n---Increase or decrease the width.\n---@field relative? integer\n\n---\n---Change the tree's root to a path.\n---\n---@param path? string absolute or relative path.\nfunction nvim_tree.api.tree.change_root(path) end\n\n---\n---Change the tree's root to a folder node or the parent of a file node.\n---\n---@param node? nvim_tree.api.Node directory or file\nfunction nvim_tree.api.tree.change_root_to_node(node) end\n\n---\n---Change the tree's root to the parent of a node.\n---\n---@param node? nvim_tree.api.Node directory or file\nfunction nvim_tree.api.tree.change_root_to_parent(node) end\n\n---\n---Retrieve the currently focused node.\n---\n---@return nvim_tree.api.Node? nil if tree is not visible.\nfunction nvim_tree.api.tree.get_node_under_cursor() end\n\n---\n---Retrieve a hierarchical list of all the nodes.\n---\n---@return nvim_tree.api.Node[]\nfunction nvim_tree.api.tree.get_nodes() end\n\n---\n---Find and focus a file or folder in the tree. Finds current buffer unless otherwise specified.\n---\n---@param opts? nvim_tree.api.tree.find_file.Opts optional\nfunction nvim_tree.api.tree.find_file(opts) end\n\n---@class nvim_tree.api.tree.find_file.Opts\n---@inlinedoc\n---\n---Absolute/relative path OR `bufnr` to find.\n---@field buf? string|integer\n---\n---Open the tree if necessary.\n---(default: false)\n---@field open? boolean\n---\n---Requires {open}: open in the current window.\n---(default: false)\n---@field current_window? boolean\n---\n---Open the tree in the specified [window-ID], overrides {current_window}\n---@field winid? integer\n---\n---Update root after find, see [nvim_tree.config.update_focused_file] {update_root}\n---(default: false)\n---@field update_root? boolean\n---\n---Focus the tree window.\n---(default: false)\n---@field focus? boolean\n\n---\n---Open the search dialogue.\n---\nfunction nvim_tree.api.tree.search_node() end\n\n---\n---Collapse the tree.\n---\n---@param opts? nvim_tree.api.node.collapse.Opts optional\nfunction nvim_tree.api.tree.collapse_all(opts) end\n\n---\n---Recursively expand all nodes under the tree root or specified folder.\n---\n---@param node? nvim_tree.api.Node directory\n---@param opts? nvim_tree.api.node.expand.Opts optional\nfunction nvim_tree.api.tree.expand_all(node, opts) end\n\n---\n---Toggle help view.\n---\nfunction nvim_tree.api.tree.toggle_help() end\n\n---\n---Checks if a buffer is an nvim-tree.\n---\n---@param bufnr? integer 0 or nil for current buffer.\n---\n---@return boolean\nfunction nvim_tree.api.tree.is_tree_buf(bufnr) end\n\n---\n---Checks if nvim-tree is visible on the current, specified or any tab.\n---\n---@param opts? nvim_tree.api.tree.is_visible.Opts optional\n---@return boolean\nfunction nvim_tree.api.tree.is_visible(opts) end\n\n---@class nvim_tree.api.tree.is_visible.Opts\n---@inlinedoc\n---\n--- [tab-ID] 0 or nil for current.\n---@field tabpage? integer\n---\n---Visible on any tab.\n---(default: false)\n---@field any_tabpage? boolean\n\n---\n---Retrieve the window of the open tree.\n---\n---@param opts? nvim_tree.api.tree.winid.Opts optional\n---@return integer? [window-ID], nil if tree is not visible.\nfunction nvim_tree.api.tree.winid(opts) end\n\n---@class nvim_tree.api.tree.winid.Opts\n---@inlinedoc\n---\n---[tab-ID] 0 or nil for current.\n---@field tabpage? integer\n\n\n---@deprecated use `nvim_tree.api.filter.toggle()`\nfunction nvim_tree.api.tree.toggle_enable_filters() end\n\n---@deprecated use `nvim_tree.api.filter.git.ignored.toggle()`\nfunction nvim_tree.api.tree.toggle_gitignore_filter() end\n\n---@deprecated use `nvim_tree.api.filter.git.clean.toggle()`\nfunction nvim_tree.api.tree.toggle_git_clean_filter() end\n\n---@deprecated use `nvim_tree.api.filter.no_buffer.toggle()`\nfunction nvim_tree.api.tree.toggle_no_buffer_filter() end\n\n---@deprecated use `nvim_tree.api.filter.custom.toggle()`\nfunction nvim_tree.api.tree.toggle_custom_filter() end\n\n---@deprecated use `nvim_tree.api.filter.dotfiles.toggle()`\nfunction nvim_tree.api.tree.toggle_hidden_filter() end\n\n---@deprecated use `nvim_tree.api.filter.no_bookmark.toggle()`\nfunction nvim_tree.api.tree.toggle_no_bookmark_filter() end\n\nreturn nvim_tree.api.tree\n"
  },
  {
    "path": "lua/nvim-tree/_meta/classes.lua",
    "content": "---@meta\nerror(\"Cannot require a meta file\")\n\n---\n---File\n---\n---@class (exact) nvim_tree.api.FileNode: nvim_tree.api.Node\n---@field extension string\n\n---\n---Directory\n---\n---@class (exact) nvim_tree.api.DirectoryNode: nvim_tree.api.Node\n---@field has_children boolean\n---@field nodes nvim_tree.api.Node[]\n---@field open boolean\n\n---\n---Root Directory\n---\n---@class (exact) nvim_tree.api.RootNode: nvim_tree.api.DirectoryNode\n\n---\n---Link mixin\n---\n---@class (exact) nvim_tree.api.LinkNode\n---@field link_to string\n---@field fs_stat_target uv.fs_stat.result\n\n---\n---File Link\n---\n---@class (exact) nvim_tree.api.FileLinkNode: nvim_tree.api.FileNode, nvim_tree.api.LinkNode\n\n---\n---DirectoryLink\n---\n---@class (exact) nvim_tree.api.DirectoryLinkNode: nvim_tree.api.DirectoryNode, nvim_tree.api.LinkNode\n"
  },
  {
    "path": "lua/nvim-tree/_meta/config/actions.lua",
    "content": "---@meta\nerror(\"Cannot require a meta file\")\n\n\n\n---@class nvim_tree.config.actions\n---\n---Use the system clipboard for copy/paste. Copied text will be stored in registers `+` (system), otherwise, it will be stored in `1` and `\"`\n---(default: `true`)\n---@field use_system_clipboard? boolean\n---\n---[nvim_tree.config.actions.change_dir]\n---@field change_dir? nvim_tree.config.actions.change_dir\n---\n---[nvim_tree.config.actions.expand_all]\n---@field expand_all? nvim_tree.config.actions.expand_all\n---\n---[nvim_tree.config.actions.file_popup]\n---@field file_popup? nvim_tree.config.actions.file_popup\n---\n---[nvim_tree.config.actions.open_file]\n---@field open_file? nvim_tree.config.actions.open_file\n---\n---[nvim_tree.config.actions.remove_file]\n---@field remove_file? nvim_tree.config.actions.remove_file\n\n\n\n--- vim [current-directory] behaviour\n---@class nvim_tree.config.actions.change_dir\n---\n---Change the working directory when changing directories in the tree\n---(default: `true`)\n---@field enable? boolean\n---\n---Use `:cd` instead of `:lcd` when changing directories.\n---(default: `false`)\n---@field global? boolean\n---\n--- Restrict changing to a directory above the global cwd.\n---(default: `false`)\n---@field restrict_above_cwd? boolean\n\n\n\n---Configure [nvim_tree.api.tree.expand_all()] and [nvim_tree.api.node.expand()]\n---@class nvim_tree.config.actions.expand_all\n---\n---Limit the number of folders being explored when expanding every folder. Avoids hanging Nvim when running this action on very large folders.\n---(default: `300`)\n---@field max_folder_discovery? integer\n---\n---A list of directories that should not be expanded automatically e.g `{ \".git\", \"target\", \"build\" }`\n---(default: `{}`)\n---@field exclude? string[]\n\n\n\n---{file_popup} floating window.\n---\n---{open_win_config} is passed to [nvim_open_win()], default:\n---```lua\n--- {\n---   col = 1,\n---   row = 1,\n---   relative = \"cursor\",\n---   border = \"shadow\",\n---   style = \"minimal\",\n--- }\n---```\n---You shouldn't define {width} and {height} values here. They will be overridden to fit the file_popup content.\n---@class nvim_tree.config.actions.file_popup\n---\n---(default: `{ col = 1, row = 1, relative = \"cursor\", border = \"shadow\", style = \"minimal\", }`)\n---@field open_win_config? vim.api.keyset.win_config\n\n\n\n---Opening files.\n---@class nvim_tree.config.actions.open_file\n---\n---Closes the explorer when opening a file\n---(default: `false`)\n---@field quit_on_open? boolean\n---\n---Prevent new opened file from opening in the same window as the tree.\n---(default: `true`)\n---@field eject? boolean\n---\n---Resizes the tree when opening a file\n---(default: `true`)\n---@field resize_window? boolean\n---\n---[nvim_tree.config.actions.open_file.window_picker]\n---@field window_picker? nvim_tree.config.actions.open_file.window_picker\n\n\n\n---A window picker will be shown when there are multiple windows available to open a file. It will show a single character identifier in each window's status line.\n---\n---When it is not enabled the file will open in the window from which you last opened the tree, obeying {exclude}\n---\n---You may define a {picker} function that should return the window id that will open the node, or `nil` if an invalid window is picked or user cancelled the action. The picker may create a new window.\n---\n---@class nvim_tree.config.actions.open_file.window_picker\n---\n---(default: `true`)\n---@field enable? boolean\n---\n---Change the default window picker or define your own.\n---(default: `\"default\"`)\n---@field picker? \"default\"|(fun(): integer)\n---\n---Identifier characters to use.\n---(default: `\"ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890\"`)\n---@field chars? string\n---\n---[nvim_tree.config.actions.open_file.window_picker.exclude]\n---@field exclude? nvim_tree.config.actions.open_file.window_picker.exclude\n\n\n\n---Tables of buffer option names mapped to a list of option values. Windows containing matching buffers will not be:\n--- - available when using a window picker\n--- - selected when not using a window picker\n---@class nvim_tree.config.actions.open_file.window_picker.exclude\n---\n---(default: `{ \"notify\", \"lazy\", \"qf\", \"diff\", \"fugitive\", \"fugitiveblame\", }`)\n---@field filetype? string[]\n---\n---(default: `{ \"nofile\", \"terminal\", \"help\", }`)\n---@field buftype? string[]\n\n\n---Removing files.\n---@class nvim_tree.config.actions.remove_file\n---\n---Close any window that displays a file when removing that file from the tree.\n---(default: `true`)\n---@field close_window? boolean\n"
  },
  {
    "path": "lua/nvim-tree/_meta/config/bookmarks.lua",
    "content": "---@meta\nerror(\"Cannot require a meta file\")\n\n\n---Optionally {persist} bookmarks to a json file:\n---- `true` use default: `stdpath(\"data\") .. \"/nvim-tree-bookmarks.json\"`\n---- `false` do not persist\n---- `string` absolute path of your choice\n---\n---@class nvim_tree.config.bookmarks\n---\n---(default: `false`)\n---@field persist? boolean|string\n"
  },
  {
    "path": "lua/nvim-tree/_meta/config/default.lua",
    "content": "---@brief\n---Following is the default configuration, see |nvim_tree.config| for details.\n---\n---```lua\n---\n--- ---@type nvim_tree.config\n--- local config = {\n--- config-default-injection-placeholder\n--- }\n---```\n"
  },
  {
    "path": "lua/nvim-tree/_meta/config/diagnostics.lua",
    "content": "---@meta\nerror(\"Cannot require a meta file\")\n\n\n\n---Integrate with [lsp] or COC diagnostics.\n---\n---See [nvim-tree-icons-highlighting].\n---\n---@class nvim_tree.config.diagnostics\n---\n---(default: `false`)\n---@field enable? boolean\n---\n---Idle milliseconds between diagnostic event and tree update.\n---(default: `500`)\n---@field debounce_delay? integer\n---\n---Show diagnostic icons on parent directories.\n---(default: `false`)\n---@field show_on_dirs? boolean\n---\n---Show diagnostics icons on directories that are open. Requires {show_on_dirs}.\n---(default: `true`)\n---@field show_on_open_dirs? boolean\n---\n---Global [vim.diagnostic.Opts] overrides {severity} and {icons}\n---(default: `false`)\n---@field diagnostic_opts? boolean\n---\n---@field severity? nvim_tree.config.diagnostics.severity\n---\n---@field icons? nvim_tree.config.diagnostics.icons\n\n\n\n---[nvim_tree.config.diagnostics.severity]()\n---@class nvim_tree.config.diagnostics.severity\n---@inlinedoc\n---\n---[vim.diagnostic.severity]\n---(default: HINT)\n---@field min? vim.diagnostic.Severity\n---\n---[vim.diagnostic.severity]\n---(default: ERROR)\n---@field max? vim.diagnostic.Severity\n\n\n\n---[nvim_tree.config.diagnostics.icons]()\n---@class nvim_tree.config.diagnostics.icons\n---@inlinedoc\n---\n---(default: `\"\"` )\n---@field hint? string\n---\n---(default: `\"\"` )\n---@field info? string\n---\n---(default: `\"\"` )\n---@field warning? string\n---\n---(default: `\"\"` )\n---@field error? string\n"
  },
  {
    "path": "lua/nvim-tree/_meta/config/experimental.lua",
    "content": "---@meta\nerror(\"Cannot require a meta file\")\n\n\n\n---Experimental features that may become default or optional functionality.\n---\n---In the event of a problem please disable the experiment and raise an issue.\n---\n---@class nvim_tree.config.experimental\n---\n--Example below for future reference:\n--\n--Buffers opened by nvim-tree will use with relative paths instead of absolute.\n--(default: false)\n--@field relative_path? boolean\n"
  },
  {
    "path": "lua/nvim-tree/_meta/config/filesystem_watchers.lua",
    "content": "---@meta\nerror(\"Cannot require a meta file\")\n\n\n\n---Use file system watchers (libuv `uv_fs_event_t`) to monitor the filesystem for changes and update the tree.\n---\n---With this feature, the tree will be partially updated on specific directory changes, resulting in better performance.\n---\n---Watchers may be disabled for absolute directory paths via {ignore_dirs}.\n--- - A list of [regular-expression] to match a path, backslash escaped e.g. `\"my-proj/\\\\.build$\"` OR\n--- - A function that is passed an absolute path and returns `true` to disable\n---This may be useful when a path is not in `.gitignore` or git integration is disabled.\n---\n---After {max_events} consecutive filesystem events on a single directory with an interval < {debounce_delay}:\n---- The filesystem watcher will be disabled for that directory.\n---- A warning notification will be shown.\n---- Consider adding this directory to {ignore_dirs}\n---\n---@class nvim_tree.config.filesystem_watchers\n---\n---(default: `true`)\n---@field enable? boolean\n---\n---Idle milliseconds between filesystem change and tree update.\n---(default: `50`)\n---@field debounce_delay? integer\n---\n---Disable for specific directories.\n---(default: `{ \"/.ccls-cache\", \"/build\", \"/node_modules\", \"/target\", \"/.zig-cache\"}`)\n---@field ignore_dirs? string[]|(fun(path: string): boolean)\n---\n---Disable for a single directory after {max_events} consecutive events with an interval < {debounce_delay}.\n---Set to 0 to allow unlimited consecutive events.\n---(default: `0` or `1000` on windows)\n---@field max_events? integer\n"
  },
  {
    "path": "lua/nvim-tree/_meta/config/filters.lua",
    "content": "---@meta\nerror(\"Cannot require a meta file\")\n\n\n\n---Filters may be applied to the tree to exlude the display of file/directories.\n---\n---Multiple filters may be applied at once.\n---\n---Filters can be set at startup or toggled live via API with default mappings.\n---\n---`I     `{git_ignored}`         `|nvim_tree.api.filter.git.ignored.toggle()|\n---Ignore files based on `.gitignore`. Requires |nvim_tree.config.git|\n---\n---`H     `{dotfiles}`            `|nvim_tree.api.filter.dotfiles.toggle()|\n---Filter dotfiles: files/directories starting with a `.`\n---\n---`C     `{git_clean}`           `|nvim_tree.api.filter.git.clean.toggle()|\n---Filter files with no git status. `.gitignore` files will not be filtered when {git_ignored}, as they are effectively dirty.\n---\n---`B     `{no_buffer}`           `|nvim_tree.api.filter.no_buffer.toggle()|\n---Filter files that have no |buflisted()| buffer. For performance reasons buffer delete/wipe may not be immediately shown. A reload or filesystem event will always result in an update.\n---\n---`M     `{no_bookmark}`         `|nvim_tree.api.filter.no_bookmark.toggle()|\n---Filter files that are not bookmarked. Enabling this is not useful as there is no means yet to persist bookmarks.\n---\n---`U     `{custom}`              `|nvim_tree.api.filter.custom.toggle()|\n---Disable specific file/directory names via:\n---- a list of backslash escaped |regular-expression| e.g. `\"^\\\\.git\"\"`\n---- a function passed the absolute path of the directory.\n---\n---All filters including live filter may be disabled via {enable} and toggled with |nvim_tree.api.filter.toggle()|\n---\n---Files/directories may be {exclude}d from filtering: they will always be shown, overriding {git_ignored}, {dotfiles} and {custom}.\n---@class nvim_tree.config.filters\n---\n---Enable all filters.\n---(default: `true`)\n---@field enable? boolean\n---\n---(default: `true`)\n---@field git_ignored? boolean\n---\n---(default: `false`)\n---@field dotfiles? boolean\n---\n---(default: `false`)\n---@field git_clean? boolean\n---\n---(default: `false`)\n---@field no_buffer? boolean\n---\n---(default: `false`)\n---@field no_bookmark? boolean\n---\n---(default: `{}`)\n---@field custom? string[]|(fun(absolute_path: string): boolean)\n---\n---(default: `{}`)\n---@field exclude? string[]\n"
  },
  {
    "path": "lua/nvim-tree/_meta/config/git.lua",
    "content": "---@meta\nerror(\"Cannot require a meta file\")\n\n\n\n---Git operations are run in the background thus status may not immediately appear.\n---\n---Processes will be killed if they exceed {timeout} ms. Git integration will be disabled following 5 timeouts and you will be notified.\n---\n---Git integration may be disabled for git top-level directories via {disable_for_dirs}:\n--- - A list of relative paths evaluated with [fnamemodify()] `:p` OR\n--- - A function that is passed an absolute path and returns `true` to disable\n---\n---See [nvim-tree-icons-highlighting].\n---\n---@class nvim_tree.config.git\n---\n---(default: `true`)\n---@field enable? boolean\n---\n---Show status icons of children when directory itself has no status icon\n---(default: `true`)\n---@field show_on_dirs? boolean\n---\n---Show status icons of children on directories that are open. Requires {show_on_dirs}.\n---(default: `true`)\n---@field show_on_open_dirs? boolean\n---\n---Disable for top level paths.\n---(default: `{}`)\n---@field disable_for_dirs? string[]|(fun(path: string): boolean)\n---\n---`git` processes timeout milliseconds.\n---(default: `400`)\n---@field timeout? integer\n---\n---Use `cygpath` if available to resolve paths for git.\n---(Default: `false`)\n---@field cygwin_support? boolean\n"
  },
  {
    "path": "lua/nvim-tree/_meta/config/help.lua",
    "content": "---@meta\nerror(\"Cannot require a meta file\")\n\n\n\n---\n---@class nvim_tree.config.help\n---\n---Alphabetically.\n---(default: `\"key\"`)\n---@field sort_by? \"key\"|\"desc\"\n"
  },
  {
    "path": "lua/nvim-tree/_meta/config/hijack_directories.lua",
    "content": "---@meta\nerror(\"Cannot require a meta file\")\n\n\n\n---Hijack directory buffers by replacing the directory buffer with the tree.\n---\n---Disable this option if you use vim-dirvish or dirbuf.nvim.\n---\n---If [nvim_tree.config] {hijack_netrw} and {disable_netrw} are `false` this feature will be disabled.\n---\n---@class nvim_tree.config.hijack_directories\n---\n---(default: `true`)\n---@field enable? boolean\n---\n---Open if the tree was previously closed.\n---(default: `true`)\n---@field auto_open? boolean\n"
  },
  {
    "path": "lua/nvim-tree/_meta/config/live_filter.lua",
    "content": "---@meta\nerror(\"Cannot require a meta file\")\n\n\n\n--- Live filter allows you to filter the tree nodes dynamically using [regular-expression] matching.\n---\n--- This feature is bound to the `f` key by default. The filter can be cleared with the `F` key by default.\n---\n---@class nvim_tree.config.live_filter\n---\n---Prefix of the filter displayed in the buffer.\n---(default: `\"[FILTER]: \"`)\n---@field prefix? string\n---\n---Whether to filter folders or not.\n---(default: `true`)\n---@field always_show_folders? boolean\n"
  },
  {
    "path": "lua/nvim-tree/_meta/config/log.lua",
    "content": "---@meta\nerror(\"Cannot require a meta file\")\n\n\n\n---Log to a file `nvim-tree.log` in [stdpath()] `log`, usually `${XDG_STATE_HOME}/nvim`\n---\n---@class nvim_tree.config.log\n---\n---(default: `false`)\n---@field enable? boolean\n---\n---Remove existing log file at startup.\n---(default: `false`)\n---@field truncate? boolean\n---\n---[nvim_tree.config.log.types]\n---@field types? nvim_tree.config.log.types\n\n---Specify which information to log.\n---@class nvim_tree.config.log.types\n---\n---Everything.\n---(default: `false`)\n---@field all? boolean\n---\n--- Timing of some operations.\n---(default: `false`)\n---@field profile? boolean\n---\n---Config and mappings, at startup.\n---(default: `false`)\n---@field config? boolean\n---\n---File copy and paste actions.\n---(default: `false`)\n---@field copy_paste? boolean\n---\n---Used for local development only. Not useful for users.\n---(default: `false`)\n---@field dev? boolean\n---\n---LSP and COC processing, verbose.\n---(default: `false`)\n---@field diagnostics? boolean\n---\n---Git processing, verbose.\n---(default: `false`)\n---@field git? boolean\n---\n---[nvim_tree.config.filesystem_watchers] processing, verbose.\n---(default: `false`)\n---@field watcher? boolean\n"
  },
  {
    "path": "lua/nvim-tree/_meta/config/modified.lua",
    "content": "---@meta\nerror(\"Cannot require a meta file\")\n\n\n\n---Indicate which files have unsaved modification.\n---To see modified status in the tree you will need:\n--- - [nvim_tree.config.renderer.icons.show] {modified} OR\n--- - [nvim_tree.config.renderer] {highlight_modified}\n---\n---See [nvim-tree-icons-highlighting].\n---\n---@class nvim_tree.config.modified\n---\n---(default: `false`)\n---@field enable? boolean\n---\n---Show modified indication on directory whose children are modified.\n---(default: `true`)\n---@field show_on_dirs? boolean\n---\n---Show modified indication on open directories. Requires {show_on_dirs}.\n---(default: `false`)\n---@field show_on_open_dirs? boolean\n"
  },
  {
    "path": "lua/nvim-tree/_meta/config/notify.lua",
    "content": "---@meta\nerror(\"Cannot require a meta file\")\n\n\n\n---nvim-tree |vim.log.levels|\n---- `ERROR`: hard errors e.g. failure to read from the file system.\n---- `WARN`: non-fatal errors e.g. unable to system open a file.\n---- `INFO`: information only e.g. file copy path confirmation.\n---- `DEBUG`: information for troubleshooting, e.g. failures in some window closing operations.\n---\n---@class nvim_tree.config.notify\n---\n---Specify minimum notification |vim.log.levels|\n---(Default: `vim.log.levels.INFO`)\n---@field threshold? vim.log.levels\n---\n---Use absolute paths in FS action notifications, otherwise item names.\n---(default: `true`)\n---@field absolute_path? boolean\n"
  },
  {
    "path": "lua/nvim-tree/_meta/config/renderer.lua",
    "content": "---@meta\nerror(\"Cannot require a meta file\")\n\n\n\n---Controls the appearance of the tree.\n---\n---{highlight_} [nvim_tree.config.renderer.highlight]()\n---\n---See [nvim-tree-icons-highlighting]\n---\n---- `\"none\"`: no highlighting\n---- `\"icon\"`: icon only\n---- `\"name\"`: name only\n---- `\"all\"`: icon and name\n---@alias nvim_tree.config.renderer.highlight \"none\"|\"icon\"|\"name\"|\"all\"\n---\n---\n---\n---{decorators} [nvim_tree.config.renderer.decorator]()\n---\n---See [nvim-tree-icons-highlighting]\n---\n---A builtin decorator name `string` or |nvim_tree.api.Decorator| class.\n---\n---Builtin decorators in their default order:\n---- `\"Git\"`\n---- `\"Open\"`\n---- `\"Hidden\"`\n---- `\"Modified\"`\n---- `\"Bookmark\"`\n---- `\"Diagnostics\"`\n---- `\"Copied\"`\n---- `\"Cut\"`\n---\n---Specify {decorators} is a list e.g. `{ \"Git\", MyDecorator, \"Cut\" }`\n---\n---@alias nvim_tree.config.renderer.decorator nvim_tree.api.Decorator|\"Git\"|\"Open\"|\"Hidden\"|\"Modified\"|\"Bookmark\"|\"Diagnostics\"|\"Copied\"|\"Cut\"\n---\n---\n---\n---{root_folder_label} [nvim_tree.config.renderer.root_folder_label]()\n---\n---Controls the root folder name and visibility:\n---- `string`: [filename-modifiers] format string, default `\":~:s?$?/..?\"`\n---- `false`: to disable\n---- `fun(root_cwd: string): string`: return a literal string from root's absolute path e.g.\n---```lua\n--- my_root_folder_label = function(path)\n---   return \".../\" .. vim.fn.fnamemodify(path, \":t\")\n--- end\n---```\n---@alias nvim_tree.config.renderer.root_folder_label string|false|(fun(root_cwd: string): string)\n---\n---\n---\n---{hidden_display} [nvim_tree.config.renderer.hidden_display]()\n---\n---Summary of hidden nodes, below the last node in the directory, highlighted with `NvimTreeHiddenDisplay`.\n---- `\"none\"`: disabled, default\n---- `\"simple\"`: total number of hidden files e.g.\n---   - (3 hidden)\n---- `\"all\"`: total and by reason: the filter that hid the node e.g.\n---   - (14 total git: 5, dotfile: 9)\n---- `(fun(hidden_stats: nvim_tree.config.renderer.hidden_stats): string)`\n---\n---See [nvim_tree.config.renderer.hidden_stats] for details and example.\n---@alias nvim_tree.config.renderer.hidden_display \"none\"|\"simple\"|\"all\"|(fun(hidden_stats: nvim_tree.config.renderer.hidden_stats): string?)\n---\n---\n---\n---@class nvim_tree.config.renderer\n---\n---Appends a trailing slash to folder and symlink folder target names.\n---(default: `false`)\n---@field add_trailing? boolean\n---\n---Compact folders that only contain a single folder into one node. Function variant takes the relative path of grouped folders and returns a string to be displayed.\n---(default: `false`)\n---@field group_empty? boolean|(fun(relative_path: string): string)\n---\n---Display nodes whose name length is wider than the width of nvim-tree window in floating window.\n---(default: `false`)\n---@field full_name? boolean\n---\n---[nvim_tree.config.renderer.root_folder_label]\n---(default: `\":~:s?$?/..?\"`)\n---@field root_folder_label? nvim_tree.config.renderer.root_folder_label\n---\n---Number of spaces for each tree nesting level. Minimum 1.\n---(default: `2`)\n---@field indent_width? integer\n---\n---[nvim_tree.config.renderer.hidden_display]\n---(default: `none`)\n---@field hidden_display? nvim_tree.config.renderer.hidden_display\n---\n---Appends an arrow followed by the target of the symlink.\n---(default: `true`)\n---@field symlink_destination? boolean\n---\n---List in order of additive precedence.\n---(default: `{ \"Git\", \"Open\", \"Hidden\", \"Modified\", \"Bookmark\", \"Diagnostics\", \"Copied\", \"Cut\", }`)\n---@field decorators? nvim_tree.config.renderer.decorator[]\n---\n---(default: `\"none\"`)\n---@field highlight_git? nvim_tree.config.renderer.highlight\n---\n---(default: `\"none\"`)\n---@field highlight_opened_files? nvim_tree.config.renderer.highlight\n---\n---(default: `\"none\"`)\n---@field highlight_hidden? nvim_tree.config.renderer.highlight\n---\n---(default: `\"none\"`)\n---@field highlight_modified? nvim_tree.config.renderer.highlight\n---\n---(default: `\"none\"`)\n---@field highlight_bookmarks? nvim_tree.config.renderer.highlight\n---\n---(default: `\"none\"`)\n---@field highlight_diagnostics? nvim_tree.config.renderer.highlight\n---\n---(default: `\"name\"`)\n---@field highlight_clipboard? nvim_tree.config.renderer.highlight\n---\n---Highlight special files and directories with `NvimTreeSpecial*`.\n---(default: `{ \"Cargo.toml\", \"Makefile\", \"README.md\", \"readme.md\", }`)\n---@field special_files? string[]\n---\n---[nvim_tree.config.renderer.indent_markers]\n---@field indent_markers? nvim_tree.config.renderer.indent_markers\n---\n---[nvim_tree.config.renderer.icons]\n---@field icons? nvim_tree.config.renderer.icons\n\n\n\n---@class nvim_tree.config.renderer.indent_markers\n---\n---Display indent markers when folders are open.\n---(default: `false`)\n---@field enable? boolean\n---\n---Display folder arrows in the same column as indent marker when using [nvim_tree.config.renderer.icons.padding] {folder_arrow}\n---(default: `true`)\n---@field inline_arrows? boolean\n---\n---@field icons? nvim_tree.config.renderer.indent_markers.icons\n\n\n\n---[nvim_tree.config.renderer.indent_markers.icons]()\n---Before the file/directory, length 1.\n---@class nvim_tree.config.renderer.indent_markers.icons\n---@inlinedoc\n---\n---(default: `\"└\"`)\n---@field corner? string\n---(default: `\"│\"`)\n---@field edge? string\n---(default: `\"│\"`)\n---@field item? string\n---(default: `\"─\"`)\n---@field bottom? string\n---(default: `\" \"`)\n---@field none? string\n\n\n\n---Icons and separators\n---\n---{_placement} [nvim_tree.config.renderer.icons.placement]()\n---- `\"before\"`: before file/folder, after the file/folders icons\n---- `\"after\"`: after file/folder\n---- `\"signcolumn\"`: far left, requires |nvim_tree.config.view| {signcolumn}.\n---- `\"right_align\"`: far right\n---@alias nvim_tree.config.renderer.icons.placement \"before\"|\"after\"|\"signcolumn\"|\"right_align\"\n---\n---\n---@class nvim_tree.config.renderer.icons\n---\n---(default: `before`)\n---@field git_placement? nvim_tree.config.renderer.icons.placement\n---\n---(default: `after`)\n---@field hidden_placement? nvim_tree.config.renderer.icons.placement\n---\n---(default: `after`)\n---@field modified_placement? nvim_tree.config.renderer.icons.placement\n---\n---(default: `signcolumn`)\n---@field bookmarks_placement? nvim_tree.config.renderer.icons.placement\n---\n---(default: `signcolumn`)\n---@field diagnostics_placement? nvim_tree.config.renderer.icons.placement\n---\n---@field padding? nvim_tree.config.renderer.icons.padding\n---\n---Separator between symlink source and target.\n---(default: `\" ➛ \"`)\n---@field symlink_arrow? string\n---\n---[nvim_tree.config.renderer.icons.show]\n---@field show? nvim_tree.config.renderer.icons.show\n---\n---[nvim_tree.config.renderer.icons.glyphs]\n---@field glyphs? nvim_tree.config.renderer.icons.glyphs\n---\n---[nvim_tree.config.renderer.icons.web_devicons]\n---@field web_devicons? nvim_tree.config.renderer.icons.web_devicons\n\n\n\n---Configure optional plugin `nvim-tree/nvim-web-devicons`, see [nvim-tree-icons-highlighting].\n---\n---@class nvim_tree.config.renderer.icons.web_devicons\n---\n---@field file? nvim_tree.config.renderer.icons.web_devicons.file\n---\n---@field folder? nvim_tree.config.renderer.icons.web_devicons.folder\n\n\n\n---[nvim_tree.config.renderer.icons.web_devicons.file]()\n---@class nvim_tree.config.renderer.icons.web_devicons.file\n---@inlinedoc\n---\n---(default: `true`)\n---@field enable? boolean\n---\n---(default: `true`)\n---@field color? boolean\n\n\n---[nvim_tree.config.renderer.icons.web_devicons.folder]()\n---@class nvim_tree.config.renderer.icons.web_devicons.folder\n---@inlinedoc\n---\n---(default: `false`)\n---@field enable? boolean\n---\n---(default: `true`)\n---@field color? boolean\n\n\n\n---[nvim_tree.config.renderer.icons.padding]()\n---@class nvim_tree.config.renderer.icons.padding\n---@inlinedoc\n---\n---Between icon and filename.\n---(default: `\" \"`)\n---@field icon? string\n---\n---Between folder arrow icon and file/folder icon.\n---(default: `\" \"`)\n---@field folder_arrow? string\n\n\n\n---See [nvim-tree-icons-highlighting].\n---@class nvim_tree.config.renderer.icons.show\n---\n---(default: `true`)\n---@field file? boolean\n---\n---(default: `true`)\n---@field folder? boolean\n---\n---(default: `true`)\n---@field git? boolean\n---\n---(default: `true`)\n---@field modified? boolean\n---\n---(default: `false`)\n---@field hidden? boolean\n---\n---(default: `true`)\n---@field diagnostics? boolean\n---\n---(default: `true`)\n---@field bookmarks? boolean\n---\n---Show a small arrow before the folder node. Arrow will be a part of the node when using [nvim_tree.config.renderer.indent_markers].\n---(default: `true`)\n---@field folder_arrow? boolean\n\n\n\n---See [nvim-tree-icons-highlighting].\n---\n---Glyphs that appear in the sign column must have length <= 2\n---@class nvim_tree.config.renderer.icons.glyphs\n---\n---Files\n---(default: `\"\"`)\n---@field default? string\n---\n---(default: `\"\"`)\n---@field symlink? string\n---\n---(default: `\"󰆤\"`)\n---@field bookmark? string\n---\n---(default: `\"●\"`)\n---@field modified? string\n---\n---(default: `\"󰜌\"`)\n---@field hidden? string\n---\n---@field folder? nvim_tree.config.renderer.icons.glyphs.folder\n---\n---@field git? nvim_tree.config.renderer.icons.glyphs.git\n\n\n\n---[nvim_tree.config.renderer.icons.glyphs.folder]()\n---@class nvim_tree.config.renderer.icons.glyphs.folder\n---@inlinedoc\n---(default: left arrow)\n---@field arrow_closed? string\n---(default: down arrow)\n---@field arrow_open? string\n---(default: `\"\"`)\n---@field default? string\n---(default: `\"\"`)\n---@field open? string\n---(default: `\"\"`)\n---@field empty? string\n---(default: `\"\"`)\n---@field empty_open? string\n---(default: `\"\"`)\n---@field symlink? string\n---(default: `\"\"`)\n---@field symlink_open? string\n\n\n\n---[nvim_tree.config.renderer.icons.glyphs.git]()\n---@class nvim_tree.config.renderer.icons.glyphs.git\n---@inlinedoc\n---(default: `\"✗\"`)\n---@field unstaged? string\n---(default: `\"✓\"`)\n---@field staged? string\n---(default: `\"\"`)\n---@field unmerged? string\n---(default: `\"➜\"`)\n---@field renamed? string\n---(default: `\"★\"`)\n---@field untracked? string\n---(default: `\"\"`)\n---@field deleted? string\n---(default: `\"◌\"`)\n---@field ignored? string\n\n---Number of hidden nodes in a directory by reason: the filter that hid the node.\n---\n---Passed to your [nvim_tree.config.renderer.hidden_display] function e.g.\n---```lua\n---\n--- ---@param hidden_stats nvim_tree.config.renderer.hidden_stats\n--- ---@return string? summary\n--- local my_hidden_display = function(hidden_stats)\n---   local total_count = 0\n---   for reason, count in pairs(hidden_stats) do\n---     total_count = total_count + count\n---   end\n---\n---   if total_count > 0 then\n---     return \"(\" .. tostring(total_count) .. \" hidden)\"\n---   end\n---   return nil\n--- end\n---```\n---\n---@class nvim_tree.config.renderer.hidden_stats\n---@field bookmark integer\n---@field buf integer\n---@field custom integer\n---@field dotfile integer\n---@field git integer\n---@field live_filter integer\n"
  },
  {
    "path": "lua/nvim-tree/_meta/config/sort.lua",
    "content": "---@meta\nerror(\"Cannot require a meta file\")\n\n\n\n---Sort files within a directory.\n---\n---{sorter} presets [nvim_tree.config.sort.Sorter]()\n---- `\"name\"`\n---- `\"case_sensitive\"` name\n---- `\"modification_time\"`\n---- `\"extension\"` uses all suffixes e.g. `foo.tar.gz` -> `.tar.gz`\n---- `\"suffix\"` uses the last e.g. `foo.tar.gz` -> `.gz`\n---- `\"filetype\"` [filetype]\n---@alias nvim_tree.config.sort.Sorter \"name\"|\"case_sensitive\"|\"modification_time\"|\"extension\"|\"suffix\"|\"filetype\"\n---\n---{sorter} may be a function that is passed a list of `nvim_tree.api.Node` to be sorted in place e.g.\n---```lua\n---\n--- ---Sort by name length\n--- ---@param nodes nvim_tree.api.Node[]\n--- ---@return nvim_tree.config.sort.Sorter?\n--- local sorter = function(nodes)\n---   table.sort(nodes, function(a, b)\n---     return #a.name < #b.name\n---   end)\n--- end\n---```\n---{sorter} may be a function that returns a [nvim_tree.config.sort.Sorter]\n---\n---@class nvim_tree.config.sort\n---\n---(default: `\"name\"`)\n---@field sorter? nvim_tree.config.sort.Sorter|(fun(nodes: nvim_tree.api.Node[]): nvim_tree.config.sort.Sorter?)\n---\n---Sort folders before files. Has no effect when {sorter} is a function.\n---(default: `true`)\n---@field folders_first? boolean\n---\n---Sort files before folders. Has no effect when {sorter} is a function. Overrides {folders_first}.\n---(default: `false`)\n---@field files_first? boolean\n"
  },
  {
    "path": "lua/nvim-tree/_meta/config/system_open.lua",
    "content": "---@meta\nerror(\"Cannot require a meta file\")\n\n\n\n---Open files or directories via the OS.\n---\n---Nvim:\n---- `>=` 0.10 uses [vim.ui.open()] unless {cmd} is specified\n---- `<` 0.10 calls external {cmd}:\n---   - UNIX: `xdg-open`\n---   - macOS: `open`\n---   - Windows: `cmd`\n---\n---Once nvim-tree minimum Nvim version is updated to 0.10, this configuration will no longer be necessary and will be removed.\n---\n---@class nvim_tree.config.system_open\n---\n---The open command itself\n---@field cmd? string\n---\n---Optional argument list. Leave empty for OS specific default.\n---(default: `{}` or `{ \"/c\", \"start\", '\"\"' }` on windows)\n---@field args? string[]\n"
  },
  {
    "path": "lua/nvim-tree/_meta/config/tab.lua",
    "content": "---@meta\nerror(\"Cannot require a meta file\")\n\n\n\n---@class nvim_tree.config.tab\n---\n---[nvim_tree.config.tab.sync]\n---@field sync? nvim_tree.config.tab.sync\n\n\n\n---@class nvim_tree.config.tab.sync\n---\n---Opens the tree automatically when switching tabpage or opening a new tabpage if the tree was previously open.\n---(default: `false`)\n---@field open? boolean\n---\n---Closes the tree across all tabpages when the tree is closed.\n---(default: `false`)\n---@field close? boolean\n---\n---\n---List of filetypes or buffer names on new tab that will prevent `open` and `close`\n---(default: `{}`)\n---@field ignore? string[]\n"
  },
  {
    "path": "lua/nvim-tree/_meta/config/trash.lua",
    "content": "---@meta\nerror(\"Cannot require a meta file\")\n\n\n\n---Files may be trashed via an external command that must be installed on your system.\n--- - linux: `gio trash`, from linux package `glib2`\n--- - macOS: `trash`, from homebrew package `trash`\n--- - windows: `trash`, requires `trash-cli` or similar\n---\n---@class nvim_tree.config.trash\n---\n---(default: `\"gio trash\"` or `\"trash\"`)\n---@field cmd? string\n"
  },
  {
    "path": "lua/nvim-tree/_meta/config/ui.lua",
    "content": "---@meta\nerror(\"Cannot require a meta file\")\n\n\n\n---@class nvim_tree.config.ui\n---\n---[nvim_tree.config.ui.confirm]\n---@field confirm? nvim_tree.config.ui.confirm\n\n\n\n---Confirmation prompts.\n---@class nvim_tree.config.ui.confirm\n---\n---Prompt before removing.\n---(default: `true`)\n---@field remove? boolean\n---\n---Prompt before trashing.\n---(default: `true`)\n---@field trash? boolean\n---\n---If `true` the prompt will be `Y/n`, otherwise `y/N`\n---(default: `false`)\n---@field default_yes? boolean\n"
  },
  {
    "path": "lua/nvim-tree/_meta/config/update_focused_file.lua",
    "content": "---@meta\nerror(\"Cannot require a meta file\")\n\n\n\n---Update the focused file on [BufEnter], uncollapsing folders recursively.\n---\n---@class nvim_tree.config.update_focused_file\n---\n---(default: `false`)\n---@field enable? boolean\n---\n---[nvim_tree.config.update_focused_file.update_root]\n---@field update_root? nvim_tree.config.update_focused_file.update_root\n---\n---A function called on [BufEnter] that returns true if the file should not be focused when opening.\n---(default: `false`)\n---@field exclude? boolean|(fun(args: vim.api.keyset.create_autocmd.callback_args): boolean)\n\n\n\n---Update the root directory of the tree if the file is not under the current root directory.\n---\n---Prefers vim's cwd and [nvim_tree.config] {root_dirs}, falling back to the directory containing the file.\n---\n---Requires [nvim_tree.config.update_focused_file]\n---\n---@class nvim_tree.config.update_focused_file.update_root\n---\n---(default: `false`)\n---@field enable? boolean\n---\n---List of buffer names and filetypes that will not update the root dir of the tree if the file isn't found under the current root directory.\n---(default: `{}`)\n---@field ignore_list? string[]\n"
  },
  {
    "path": "lua/nvim-tree/_meta/config/view.lua",
    "content": "---@meta\nerror(\"Cannot require a meta file\")\n\n\n\n---Configures the dimensions and appearance of the nvim-tree window.\n---\n---The window is \"docked\" at the left by default, however may be configured to float: [nvim_tree.config.view.float]\n---\n---{width} can be a [nvim_tree.config.view.width.spec] for static control or a [nvim_tree.config.view.width] for fully dynamic control based on longest line.\n---\n---[nvim_tree.config.view.width.spec]()\n---- `string`: `x%` string e.g. `30%`\n---- `integer`: number of columns\n---- `function`: returns one of the above\n---@alias nvim_tree.config.view.width.spec string|integer|(fun(): integer|string)\n---\n---@class nvim_tree.config.view\n---\n---When entering nvim-tree, reposition the view so that the current node is initially centralized, see [zz].\n---(default: `false`)\n---@field centralize_selection? boolean\n---\n---['cursorline']\n---(default: `true`)\n---@field cursorline? boolean\n---\n---['cursorlineopt']\n---(default: `both`)\n---@field cursorlineopt? string\n---\n---Idle milliseconds before some reload / refresh operations. Increase if you experience performance issues around screen refresh.\n---(default: `15`)\n---@field debounce_delay? integer\n---\n---(default: `\"left\"`)\n---@field side? \"left\"|\"right\"\n---\n---Preserves window proportions when opening a file. If `false`, the height and width of windows other than nvim-tree will be equalized.\n---(default: `false`)\n---@field preserve_window_proportions? boolean\n---\n---['number']\n---(default: `false`)\n---@field number? boolean\n---\n---['relativenumber']\n---(default: `false`)\n---@field relativenumber? boolean\n---\n---['signcolumn']\n---(default: `\"yes\"`)\n---@field signcolumn? \"yes\"|\"auto\"|\"no\"\n---\n---(default: `30`)\n---@field width? nvim_tree.config.view.width.spec|nvim_tree.config.view.width\n---\n---[nvim_tree.config.view.float]\n---@field float? nvim_tree.config.view.float\n\n\n\n---Configure dynamic width based on longest line.\n---\n---@class nvim_tree.config.view.width\n---\n---(default: `30`)\n---@field min? nvim_tree.config.view.width.spec\n---\n----1 for unbounded.\n---(default: `-1`)\n---@field max? nvim_tree.config.view.width.spec\n---\n---Exclude these lines when computing width.\n---(default: `{ \"root\" }`)\n---@field lines_excluded? (\"root\")[]\n---\n---Extra padding to the right.\n---(default: `1`)\n---@field padding? nvim_tree.config.view.width.spec\n\n\n\n---Configure floating window behaviour\n---\n---{open_win_config} is passed to [nvim_open_win()], default:\n---```lua\n--- {\n---   relative = \"editor\",\n---   border = \"rounded\",\n---   width = 30,\n---   height = 30,\n---   row = 1,\n---   col = 1,\n--- }\n---```\n---@class nvim_tree.config.view.float\n---\n---(default: `false`)\n---@field enable? boolean\n---\n---Close the floating window when it loses focus.\n---(default: `true`)\n---@field quit_on_focus_loss? boolean\n---\n---(default: `{ relative = \"editor\", border = \"rounded\", width = 30, height = 30, row = 1, col = 1, }`)\n---@field open_win_config? vim.api.keyset.win_config|(fun(): vim.api.keyset.win_config)\n"
  },
  {
    "path": "lua/nvim-tree/_meta/config.lua",
    "content": "---@meta\nerror(\"Cannot require a meta file\")\n\n\n\n-- Root class {field}s are documented manually above \"Fields:\" as there is insufficent room for them in the column.\n\n\n\n---Arguments to pass to [nvim-tree-setup].\n---\n---When a value is not present/nil, the default will be used.\n---\n---{on_attach} Runs when creating the nvim-tree buffer. Use this to set your [nvim-tree-mappings]. When not a function, [nvim-tree-mappings-default] will be used.\n---\n---{hijack_cursor} keep the cursor on the first letter of the filename when moving in the tree.\n---\n---{auto_reload_on_write} reload the explorer every time a buffer is written to.\n---\n---{disable_netrw} completely disables [netrw], see [nvim-tree-netrw] for details. It is strongly advised to eagerly disable netrw, due to race conditions at vim startup.\n---\n---{hijack_netrw} hijacks netrw windows, ignored when {disable_netrw}.\n---\n---{hijack_unnamed_buffer_when_opening} opens in place of the unnamed buffer if it's empty.\n---\n---{root_dirs} preferred root directories, requires [nvim_tree.config.update_focused_file.update_root].\n---\n---{prefer_startup_root} prefer startup root directory when updating root directory of the tree. Requires [nvim_tree.config.update_focused_file.update_root].\n---\n---{sync_root_with_cwd} changes the tree root directory on [DirChanged] and refreshes the tree.\n---\n---{reload_on_bufenter} automatically reloads the tree on [BufEnter] nvim-tree.\n---\n---{respect_buf_cwd} changes the [current-directory] of nvim-tree to that of new buffer's when opening nvim-tree.\n---\n---{select_prompts} uses [vim.ui.select()] style prompts. Necessary when using a UI prompt decorator such as dressing.nvim or telescope-ui-select.nvim\n---@class nvim_tree.config\n---\n---(default: `default`)\n---@field on_attach? \"default\"|(fun(bufnr: integer))\n---\n---(default: `false`)\n---@field hijack_cursor? boolean\n---\n---(default: `true`)\n---@field auto_reload_on_write? boolean\n---\n---(default: `false`)\n---@field disable_netrw? boolean\n---\n---(default: `true`)\n---@field hijack_netrw? boolean\n---\n---(default: `false`)\n---@field hijack_unnamed_buffer_when_opening? boolean\n---\n---(default: `{}`)\n---@field root_dirs? string[]\n---\n---(default: `false`)\n---@field prefer_startup_root? boolean\n---\n---(default: `false`)\n---@field sync_root_with_cwd? boolean\n---\n---(default: `false`)\n---@field reload_on_bufenter? boolean\n---\n---(default: `false`)\n---@field respect_buf_cwd? boolean\n---\n---(default: `false`)\n---@field select_prompts? boolean\n---\n---[nvim_tree.config.sort]\n---@field sort? nvim_tree.config.sort\n---\n---[nvim_tree.config.view]\n---@field view? nvim_tree.config.view\n---\n---[nvim_tree.config.renderer]\n---@field renderer? nvim_tree.config.renderer\n---\n---[nvim_tree.config.hijack_directories]\n---@field hijack_directories? nvim_tree.config.hijack_directories\n---\n---[nvim_tree.config.update_focused_file]\n---@field update_focused_file? nvim_tree.config.update_focused_file\n---\n---[nvim_tree.config.system_open]\n---@field system_open? nvim_tree.config.system_open\n---\n---[nvim_tree.config.git]\n---@field git? nvim_tree.config.git\n---\n---[nvim_tree.config.diagnostics]\n---@field diagnostics? nvim_tree.config.diagnostics\n---\n---[nvim_tree.config.modified]\n---@field modified? nvim_tree.config.modified\n---\n---[nvim_tree.config.filters]\n---@field filters? nvim_tree.config.filters\n---\n---[nvim_tree.config.live_filter]\n---@field live_filter? nvim_tree.config.live_filter\n---\n---[nvim_tree.config.filesystem_watchers]\n---@field filesystem_watchers? nvim_tree.config.filesystem_watchers\n---\n---[nvim_tree.config.actions]\n---@field actions? nvim_tree.config.actions\n---\n---[nvim_tree.config.trash]\n---@field trash? nvim_tree.config.trash\n---\n---[nvim_tree.config.tab]\n---@field tab? nvim_tree.config.tab\n---\n---[nvim_tree.config.bookmarks]\n---@field bookmarks? nvim_tree.config.bookmarks\n---\n---[nvim_tree.config.notify]\n---@field notify? nvim_tree.config.notify\n---\n---[nvim_tree.config.help]\n---@field help? nvim_tree.config.help\n---\n---[nvim_tree.config.ui]\n---@field ui? nvim_tree.config.ui\n---\n---[nvim_tree.config.experimental]\n---@field experimental? nvim_tree.config.experimental\n---\n---[nvim_tree.config.log]\n---@field log? nvim_tree.config.log\n"
  },
  {
    "path": "lua/nvim-tree/actions/finders/find-file.lua",
    "content": "local log = require(\"nvim-tree.log\")\nlocal view = require(\"nvim-tree.view\")\nlocal utils = require(\"nvim-tree.utils\")\nlocal core = require(\"nvim-tree.core\")\n\nlocal DirectoryNode = require(\"nvim-tree.node.directory\")\nlocal Iterator = require(\"nvim-tree.iterators.node-iterator\")\n\nlocal M = {}\n\nlocal running = {}\n\n---Find a path in the tree, expand it and focus it\n---@param path string relative or absolute\nfunction M.fn(path)\n  local explorer = core.get_explorer()\n  if not explorer or not view.is_visible() then\n    return\n  end\n\n  -- always match against the real path\n  local path_real = vim.loop.fs_realpath(path)\n  if not path_real then\n    return\n  end\n\n  if running[path_real] then\n    return\n  end\n  running[path_real] = true\n\n  local profile = log.profile_start(\"find file %s\", path_real)\n\n  -- refresh the contents of all parents, expanding groups as needed\n  if explorer:get_node_from_path(path_real) == nil then\n    explorer:refresh_parent_nodes_for_path(vim.fn.fnamemodify(path_real, \":h\"))\n  end\n\n  local line = core.get_nodes_starting_line()\n\n  local absolute_paths_searched = {}\n\n  local found = Iterator.builder(core.get_explorer().nodes)\n    :matcher(function(node)\n      return node.absolute_path == path_real or node.link_to == path_real\n    end)\n    :applier(function(node)\n      local incremented_line = false\n      if not node.group_next then\n        line = line + 1\n        incremented_line = true\n      end\n\n      if vim.tbl_contains(absolute_paths_searched, node.absolute_path) then\n        return\n      end\n      table.insert(absolute_paths_searched, node.absolute_path)\n\n      local abs_match = vim.startswith(path_real, node.absolute_path .. utils.path_separator)\n      local link_match = node.link_to and vim.startswith(path_real, node.link_to .. utils.path_separator)\n\n      if abs_match or link_match then\n        local dir = node:as(DirectoryNode)\n        if dir then\n          if not dir.group_next then\n            dir.open = true\n          end\n          if #dir.nodes == 0 then\n            core.get_explorer():expand_dir_node(dir)\n            if dir.group_next and incremented_line then\n              line = line - 1\n            end\n          end\n        end\n      end\n    end)\n    :recursor(function(node)\n      node = node and node:as(DirectoryNode)\n      if node then\n        return node.group_next and { node.group_next } or (node.open and #node.nodes > 0 and node.nodes)\n      else\n        return nil\n      end\n    end)\n    :iterate()\n\n  if found and view.is_visible() then\n    explorer.renderer:draw()\n    view.set_cursor({ line, 0 })\n  end\n\n  running[path_real] = false\n\n  log.profile_end(profile)\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/actions/finders/init.lua",
    "content": "local M = {}\n\nM.find_file = require(\"nvim-tree.actions.finders.find-file\")\nM.search_node = require(\"nvim-tree.actions.finders.search-node\")\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/actions/finders/search-node.lua",
    "content": "local core = require(\"nvim-tree.core\")\nlocal find_file = require(\"nvim-tree.actions.finders.find-file\").fn\n\nlocal M = {}\n\n---@param search_dir string|nil\n---@param input_path string\n---@return string|nil\nlocal function search(search_dir, input_path)\n  local realpaths_searched = {}\n  local explorer = core.get_explorer()\n\n  if not explorer then\n    return\n  end\n\n  if not search_dir then\n    return\n  end\n\n  ---@param dir string\n  ---@return string|nil\n  local function iter(dir)\n    local realpath, path, name, stat, handle, _\n\n    local filter_status = explorer.filters:prepare()\n\n    handle, _ = vim.loop.fs_scandir(dir)\n    if not handle then\n      return\n    end\n\n    realpath, _ = vim.loop.fs_realpath(dir)\n    if not realpath or vim.tbl_contains(realpaths_searched, realpath) then\n      return\n    end\n    table.insert(realpaths_searched, realpath)\n\n    name, _ = vim.loop.fs_scandir_next(handle)\n    while name do\n      path = dir .. \"/\" .. name\n\n      ---@type uv.fs_stat.result|nil\n      stat, _ = vim.loop.fs_stat(path)\n      if not stat then\n        break\n      end\n\n      if not explorer.filters:should_filter(path, stat, filter_status) then\n        if string.find(path, \"/\" .. input_path .. \"$\") then\n          return path\n        end\n\n        if stat.type == \"directory\" then\n          path = iter(path)\n          if path then\n            return path\n          end\n        end\n      end\n\n      name, _ = vim.loop.fs_scandir_next(handle)\n    end\n  end\n\n  return iter(search_dir)\nend\n\nfunction M.fn()\n  if not core.get_explorer() then\n    return\n  end\n\n  -- temporarily set &path\n  local bufnr = vim.api.nvim_get_current_buf()\n\n  local path_existed, path_opt\n  if vim.fn.has(\"nvim-0.10\") == 1 then\n    path_existed, path_opt = pcall(vim.api.nvim_get_option_value, \"path\", { buf = bufnr })\n    vim.api.nvim_set_option_value(\"path\", core.get_cwd() .. \"/**\", { buf = bufnr })\n  else\n    path_existed, path_opt = pcall(vim.api.nvim_buf_get_option, bufnr, \"path\") ---@diagnostic disable-line: deprecated\n    vim.api.nvim_buf_set_option(bufnr, \"path\", core.get_cwd() .. \"/**\") ---@diagnostic disable-line: deprecated\n  end\n\n  vim.ui.input({ prompt = \"Search: \", completion = \"file_in_path\" }, function(input_path)\n    if not input_path or input_path == \"\" then\n      return\n    end\n    -- reset &path\n    if path_existed then\n      if vim.fn.has(\"nvim-0.10\") == 1 then\n        vim.api.nvim_set_option_value(\"path\", path_opt, { buf = bufnr })\n      else\n        vim.api.nvim_buf_set_option(bufnr, \"path\", path_opt) ---@diagnostic disable-line: deprecated\n      end\n    else\n      if vim.fn.has(\"nvim-0.10\") == 1 then\n        vim.api.nvim_set_option_value(\"path\", nil, { buf = bufnr })\n      else\n        vim.api.nvim_buf_set_option(bufnr, \"path\", nil) ---@diagnostic disable-line: deprecated\n      end\n    end\n\n    -- strip trailing slash\n    input_path = string.gsub(input_path, \"/$\", \"\")\n\n    -- search under cwd\n    local found = search(core.get_cwd(), input_path)\n    if found then\n      find_file(found)\n    end\n  end)\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/actions/fs/clipboard.lua",
    "content": "local lib = require(\"nvim-tree.lib\")\nlocal log = require(\"nvim-tree.log\")\nlocal utils = require(\"nvim-tree.utils\")\nlocal core = require(\"nvim-tree.core\")\nlocal events = require(\"nvim-tree.events\")\nlocal notify = require(\"nvim-tree.notify\")\n\nlocal find_file = require(\"nvim-tree.actions.finders.find-file\").fn\n\nlocal Class = require(\"nvim-tree.classic\")\nlocal DirectoryNode = require(\"nvim-tree.node.directory\")\nlocal Node = require(\"nvim-tree.node\")\n\n---@alias ClipboardAction \"copy\" | \"cut\"\n---@alias ClipboardData table<ClipboardAction, Node[]>\n\n---@alias ClipboardActionFn fun(source: string, dest: string): boolean, string?\n\n---@class (exact) Clipboard: nvim_tree.Class\n---@field private explorer Explorer\n---@field private data ClipboardData\n---@field private clipboard_name string\n---@field private reg string\nlocal Clipboard = Class:extend()\n\n---@class Clipboard\n---@overload fun(args: ClipboardArgs): Clipboard\n\n---@class (exact) ClipboardArgs\n---@field explorer Explorer\n\n---@protected\n---@param args ClipboardArgs\nfunction Clipboard:new(args)\n  self.explorer = args.explorer\n\n  self.data = {\n    copy = {},\n    cut = {},\n  }\n\n  self.clipboard_name = self.explorer.opts.actions.use_system_clipboard and \"system\" or \"neovim\"\n  self.reg = self.explorer.opts.actions.use_system_clipboard and \"+\" or \"1\"\nend\n\n---@param source string\n---@param destination string\n---@return boolean\n---@return string|nil\nlocal function do_copy(source, destination)\n  local source_stats, err = vim.loop.fs_stat(source)\n\n  if not source_stats then\n    log.line(\"copy_paste\", \"do_copy fs_stat '%s' failed '%s'\", source, err)\n    return false, err\n  end\n\n  log.line(\"copy_paste\", \"do_copy %s '%s' -> '%s'\", source_stats.type, source, destination)\n\n  if source == destination then\n    log.line(\"copy_paste\", \"do_copy source and destination are the same, exiting early\")\n    return true\n  end\n\n  if source_stats.type == \"file\" then\n    local success\n    success, err = vim.loop.fs_copyfile(source, destination)\n    if not success then\n      log.line(\"copy_paste\", \"do_copy fs_copyfile failed '%s'\", err)\n      return false, err\n    end\n    return true\n  elseif source_stats.type == \"directory\" then\n    local handle\n    handle, err = vim.loop.fs_scandir(source)\n    if type(handle) == \"string\" then\n      return false, handle\n    elseif not handle then\n      log.line(\"copy_paste\", \"do_copy fs_scandir '%s' failed '%s'\", source, err)\n      return false, err\n    end\n\n    local success\n    success, err = vim.loop.fs_mkdir(destination, source_stats.mode)\n    if not success then\n      log.line(\"copy_paste\", \"do_copy fs_mkdir '%s' failed '%s'\", destination, err)\n      return false, err\n    end\n\n    while true do\n      local name, _ = vim.loop.fs_scandir_next(handle)\n      if not name then\n        break\n      end\n\n      local new_name = utils.path_join({ source, name })\n      local new_destination = utils.path_join({ destination, name })\n      success, err = do_copy(new_name, new_destination)\n      if not success then\n        return false, err\n      end\n    end\n  else\n    err = string.format(\"'%s' illegal file type '%s'\", source, source_stats.type)\n    log.line(\"copy_paste\", \"do_copy %s\", err)\n    return false, err\n  end\n\n  return true\nend\n\n---Paste a single item with no conflict handling.\n---@param source string\n---@param dest string\n---@param action ClipboardAction\n---@param action_fn ClipboardActionFn\nlocal function do_paste_one(source, dest, action, action_fn)\n  log.line(\"copy_paste\", \"do_paste_one '%s' -> '%s'\", source, dest)\n  local success, err = action_fn(source, dest)\n  if not success then\n    notify.error(\"Could not \" .. action .. \" \" .. notify.render_path(source) .. \" - \" .. (err or \"???\"))\n  end\n  find_file(utils.path_remove_trailing(dest))\nend\n\n---@param node Node\n---@param clip ClipboardData\nlocal function toggle(node, clip)\n  if node.name == \"..\" then\n    return\n  end\n  local notify_node = notify.render_path(node.absolute_path)\n\n  if utils.array_remove(clip, node) then\n    notify.info(notify_node .. \" removed from clipboard.\")\n    return\n  end\n\n  table.insert(clip, node)\n  notify.info(notify_node .. \" added to clipboard.\")\nend\n\n---Clear copied and cut\nfunction Clipboard:clear_clipboard()\n  self.data.copy = {}\n  self.data.cut = {}\n  notify.info(\"Clipboard has been emptied.\")\n  self.explorer.renderer:draw()\nend\n\n---Bulk add/remove nodes to/from a clipboard list.\n---@private\n---@param nodes Node[] filtered nodes to operate on\n---@param from Node[] list to remove from (the opposite clipboard)\n---@param to Node[] list to add to\n---@param verb string notification verb (\"added to\" or \"cut to\")\nfunction Clipboard:bulk_clipboard(nodes, from, to, verb)\n  local added = 0\n  local removed = 0\n  for _, node in ipairs(nodes) do\n    if node.name ~= \"..\" then\n      utils.array_remove(from, node)\n      if utils.array_remove(to, node) then\n        removed = removed + 1\n      else\n        table.insert(to, node)\n        added = added + 1\n      end\n    end\n  end\n  if added > 0 then\n    notify.info(string.format(\"%d nodes %s clipboard.\", added, verb))\n  elseif removed > 0 then\n    notify.info(string.format(\"%d nodes removed from clipboard.\", removed))\n  end\n  self.explorer.renderer:draw()\nend\n\n---Copy one or more nodes\n---@param node_or_nodes Node|Node[]\nfunction Clipboard:copy(node_or_nodes)\n  if type(node_or_nodes) == \"table\" and node_or_nodes.is and node_or_nodes:is(Node) then\n    utils.array_remove(self.data.cut, node_or_nodes)\n    toggle(node_or_nodes, self.data.copy)\n    self.explorer.renderer:draw()\n  else\n    self:bulk_clipboard(utils.filter_descendant_nodes(node_or_nodes), self.data.cut, self.data.copy, \"added to\")\n  end\nend\n\n---Cut one or more nodes\n---@param node_or_nodes Node|Node[]\nfunction Clipboard:cut(node_or_nodes)\n  if type(node_or_nodes) == \"table\" and node_or_nodes.is and node_or_nodes:is(Node) then\n    utils.array_remove(self.data.copy, node_or_nodes)\n    toggle(node_or_nodes, self.data.cut)\n    self.explorer.renderer:draw()\n  else\n    self:bulk_clipboard(utils.filter_descendant_nodes(node_or_nodes), self.data.copy, self.data.cut, \"cut to\")\n  end\nend\n\n---Clear clipboard for action and reload to reflect filesystem changes from paste.\n---@private\n---@param action ClipboardAction\nfunction Clipboard:finish_paste(action)\n  self.data[action] = {}\n  self.explorer:reload_explorer()\nend\n\n---Resolve conflicting paste items.\n---Single conflict: per-file prompt with full-path rename (pre-visual-mode behavior).\n---Multiple conflicts: batch prompt with suffix rename.\n---@private\n---@param conflict {node: Node, dest: string}[]\n---@param destination string\n---@param action ClipboardAction\n---@param action_fn ClipboardActionFn\nfunction Clipboard:resolve_conflicts(conflict, destination, action, action_fn)\n  if #conflict == 1 then\n    local source = conflict[1].node.absolute_path\n    local dest = conflict[1].dest\n\n    local function rename_prompt(default_dest)\n      vim.ui.input({ prompt = \"Rename to \", default = default_dest, completion = \"dir\" }, function(new_dest)\n        utils.clear_prompt()\n        if not new_dest or new_dest == \"\" then\n          self:finish_paste(action)\n          return\n        end\n        if vim.loop.fs_stat(new_dest) then\n          self:resolve_conflicts({ { node = conflict[1].node, dest = new_dest } }, destination, action, action_fn)\n        else\n          do_paste_one(source, new_dest, action, action_fn)\n          self:finish_paste(action)\n        end\n      end)\n    end\n\n    if source == dest then\n      rename_prompt(dest)\n    else\n      local prompt_select = \"Overwrite \" .. dest .. \" ?\"\n      lib.prompt(prompt_select .. \" R(ename)/y/n: \", prompt_select,\n        { \"\", \"y\", \"n\" }, { \"Rename\", \"Yes\", \"No\" },\n        \"nvimtree_overwrite_rename\",\n        function(item_short)\n          utils.clear_prompt()\n          if item_short == \"y\" then\n            do_paste_one(source, dest, action, action_fn)\n            self:finish_paste(action)\n          elseif item_short == \"\" or item_short == \"r\" then\n            rename_prompt(dest)\n          else\n            self:finish_paste(action)\n          end\n        end)\n    end\n    return\n  end\n\n  local prompt_select = #conflict .. \" file(s) already exist\"\n  local prompt_input = prompt_select .. \". R(ename suffix)/y/n: \"\n\n  lib.prompt(prompt_input, prompt_select,\n    { \"\", \"y\", \"n\" },\n    { \"Rename (suffix)\", \"Overwrite all\", \"Skip all\" },\n    \"nvimtree_paste_conflict\",\n    function(item_short)\n      utils.clear_prompt()\n      if item_short == \"y\" then\n        for _, item in ipairs(conflict) do\n          do_paste_one(item.node.absolute_path, item.dest, action, action_fn)\n        end\n        self:finish_paste(action)\n      elseif item_short == \"\" or item_short == \"r\" then\n        vim.ui.input({ prompt = \"Suffix: \" }, function(suffix)\n          utils.clear_prompt()\n          if not suffix or suffix == \"\" then\n            return\n          end\n          local still_conflict = {}\n          for _, item in ipairs(conflict) do\n            local basename = vim.fn.fnamemodify(item.node.name, \":r\")\n            local extension = vim.fn.fnamemodify(item.node.name, \":e\")\n            local new_name = extension ~= \"\" and (basename .. suffix .. \".\" .. extension) or (item.node.name .. suffix)\n            local new_dest = utils.path_join({ destination, new_name })\n            local stats = vim.loop.fs_stat(new_dest)\n            if stats then\n              table.insert(still_conflict, { node = item.node, dest = new_dest })\n            else\n              do_paste_one(item.node.absolute_path, new_dest, action, action_fn)\n            end\n          end\n          if #still_conflict > 0 then\n            self:resolve_conflicts(still_conflict, destination, action, action_fn)\n          else\n            self:finish_paste(action)\n          end\n        end)\n      else\n        self:finish_paste(action)\n      end\n    end)\nend\n\n---Paste cut or copy with batch conflict resolution.\n---@private\n---@param node Node\n---@param action ClipboardAction\n---@param action_fn ClipboardActionFn\nfunction Clipboard:do_paste(node, action, action_fn)\n  if node.name == \"..\" then\n    node = self.explorer\n  else\n    local dir = node:as(DirectoryNode)\n    if dir then\n      node = dir:last_group_node()\n    end\n  end\n  local clip = self.data[action]\n  if #clip == 0 then\n    return\n  end\n\n  local destination = node.absolute_path\n  local stats, err, err_name = vim.loop.fs_stat(destination)\n  if not stats and err_name ~= \"ENOENT\" then\n    log.line(\"copy_paste\", \"do_paste fs_stat '%s' failed '%s'\", destination, err)\n    notify.error(\"Could not \" .. action .. \" \" .. notify.render_path(destination) .. \" - \" .. (err or \"???\"))\n    return\n  end\n  local is_dir = stats and stats.type == \"directory\"\n  if not is_dir then\n    destination = vim.fn.fnamemodify(destination, \":p:h\")\n  end\n\n  -- Partition into conflict / no-conflict\n  local no_conflict = {}\n  local conflict = {}\n  for _, _node in ipairs(clip) do\n    local dest = utils.path_join({ destination, _node.name })\n    local dest_stats = vim.loop.fs_stat(dest)\n    if dest_stats then\n      table.insert(conflict, { node = _node, dest = dest })\n    else\n      table.insert(no_conflict, { node = _node, dest = dest })\n    end\n  end\n\n  -- Paste non-conflicting items immediately\n  for _, item in ipairs(no_conflict) do\n    do_paste_one(item.node.absolute_path, item.dest, action, action_fn)\n  end\n\n  -- Resolve conflicts in batch\n  if #conflict > 0 then\n    self:resolve_conflicts(conflict, destination, action, action_fn)\n  else\n    self:finish_paste(action)\n  end\nend\n\n---@param source string\n---@param destination string\n---@return boolean\n---@return string?\nlocal function do_cut(source, destination)\n  log.line(\"copy_paste\", \"do_cut '%s' -> '%s'\", source, destination)\n\n  if source == destination then\n    log.line(\"copy_paste\", \"do_cut source and destination are the same, exiting early\")\n    return true\n  end\n\n  events._dispatch_will_rename_node(source, destination)\n  local success, errmsg = vim.loop.fs_rename(source, destination)\n  if not success then\n    log.line(\"copy_paste\", \"do_cut fs_rename failed '%s'\", errmsg)\n    return false, errmsg\n  end\n  utils.rename_loaded_buffers(source, destination)\n  events._dispatch_node_renamed(source, destination)\n  return true\nend\n\n---Paste cut (if present) or copy (if present)\n---@param node Node\nfunction Clipboard:paste(node)\n  if self.data.cut[1] ~= nil then\n    self:do_paste(node, \"cut\", do_cut)\n  elseif self.data.copy[1] ~= nil then\n    self:do_paste(node, \"copy\", do_copy)\n  end\nend\n\nfunction Clipboard:print_clipboard()\n  local content = {}\n  if #self.data.cut > 0 then\n    table.insert(content, \"Cut\")\n    for _, node in pairs(self.data.cut) do\n      table.insert(content, \" * \" .. (notify.render_path(node.absolute_path)))\n    end\n  end\n  if #self.data.copy > 0 then\n    table.insert(content, \"Copy\")\n    for _, node in pairs(self.data.copy) do\n      table.insert(content, \" * \" .. (notify.render_path(node.absolute_path)))\n    end\n  end\n\n  notify.info(table.concat(content, \"\\n\") .. \"\\n\")\nend\n\n---@param content string\nfunction Clipboard:copy_to_reg(content)\n  -- manually firing TextYankPost does not set vim.v.event\n  -- workaround: create a scratch buffer with the clipboard contents and send a yank command\n  local temp_buf = vim.api.nvim_create_buf(false, true)\n  vim.api.nvim_buf_set_text(temp_buf, 0, 0, 0, 0, { content })\n  vim.api.nvim_buf_call(temp_buf, function()\n    vim.cmd(string.format('normal! \"%sy$', self.reg))\n  end)\n  vim.api.nvim_buf_delete(temp_buf, {})\n\n  notify.info(string.format(\"Copied %s to %s clipboard!\", content, self.clipboard_name))\nend\n\n---@param node Node\nfunction Clipboard:copy_filename(node)\n  if node.name == \"..\" then\n    -- root\n    self:copy_to_reg(vim.fn.fnamemodify(self.explorer.absolute_path, \":t\"))\n  else\n    -- node\n    self:copy_to_reg(node.name)\n  end\nend\n\n---@param node Node\nfunction Clipboard:copy_basename(node)\n  if node.name == \"..\" then\n    -- root\n    self:copy_to_reg(vim.fn.fnamemodify(self.explorer.absolute_path, \":t:r\"))\n  else\n    -- node\n    self:copy_to_reg(vim.fn.fnamemodify(node.name, \":r\"))\n  end\nend\n\n---@param node Node\nfunction Clipboard:copy_path(node)\n  if node.name == \"..\" then\n    -- root\n    self:copy_to_reg(utils.path_add_trailing(\"\"))\n  else\n    -- node\n    local absolute_path = node.absolute_path\n    local cwd = core.get_cwd()\n    if cwd == nil then\n      return\n    end\n\n    local relative_path = utils.path_relative(absolute_path, cwd)\n    if node:is(DirectoryNode) then\n      self:copy_to_reg(utils.path_add_trailing(relative_path))\n    else\n      self:copy_to_reg(relative_path)\n    end\n  end\nend\n\n---@param node Node\nfunction Clipboard:copy_absolute_path(node)\n  if node.name == \"..\" then\n    node = self.explorer\n  end\n\n  local absolute_path = node.absolute_path\n  local content = node.nodes ~= nil and utils.path_add_trailing(absolute_path) or absolute_path\n  self:copy_to_reg(content)\nend\n\n---Node is cut. Will not be copied.\n---@param node Node\n---@return boolean\nfunction Clipboard:is_cut(node)\n  return vim.tbl_contains(self.data.cut, node)\nend\n\n---Node is copied. Will not be cut.\n---@param node Node\n---@return boolean\nfunction Clipboard:is_copied(node)\n  return vim.tbl_contains(self.data.copy, node)\nend\n\nreturn Clipboard\n"
  },
  {
    "path": "lua/nvim-tree/actions/fs/create-file.lua",
    "content": "local utils = require(\"nvim-tree.utils\")\nlocal events = require(\"nvim-tree.events\")\nlocal core = require(\"nvim-tree.core\")\nlocal notify = require(\"nvim-tree.notify\")\n\nlocal find_file = require(\"nvim-tree.actions.finders.find-file\").fn\n\nlocal FileNode = require(\"nvim-tree.node.file\")\nlocal DirectoryNode = require(\"nvim-tree.node.directory\")\n\nlocal M = {}\n\n---@param file string\nlocal function create_and_notify(file)\n  events._dispatch_will_create_file(file)\n  local ok, fd = pcall(vim.loop.fs_open, file, \"w\", 420)\n  if not ok or type(fd) ~= \"number\" then\n    notify.error(\"Couldn't create file \" .. notify.render_path(file))\n    return\n  end\n  vim.loop.fs_close(fd)\n  events._dispatch_file_created(file)\nend\n\n---@param iter function iterable\n---@return integer\nlocal function get_num_nodes(iter)\n  local i = 0\n  for _ in iter do\n    i = i + 1\n  end\n  return i\nend\n\n---@param node Node?\nfunction M.fn(node)\n  node = node or core.get_explorer()\n  if not node then\n    return\n  end\n\n  local dir = node:is(FileNode) and node.parent or node:as(DirectoryNode)\n  if not dir then\n    return\n  end\n\n  dir = dir:last_group_node()\n\n  local containing_folder = utils.path_add_trailing(dir.absolute_path)\n\n  local input_opts = {\n    prompt = \"Create file \",\n    default = containing_folder,\n    completion = \"file\",\n  }\n\n  vim.ui.input(input_opts, function(new_file_path)\n    utils.clear_prompt()\n    if not new_file_path or new_file_path == containing_folder then\n      return\n    end\n\n    if utils.file_exists(new_file_path) then\n      notify.warn(\"Cannot create: file already exists\")\n      return\n    end\n\n    -- create a folder for each path element if the folder does not exist\n    -- if the answer ends with a /, create a file for the last path element\n    local is_last_path_file = not new_file_path:match(utils.path_separator .. \"$\")\n    local path_to_create = \"\"\n    local idx = 0\n\n    local num_nodes = get_num_nodes(utils.path_split(utils.path_remove_trailing(new_file_path)))\n    local is_error = false\n    for path in utils.path_split(new_file_path) do\n      idx = idx + 1\n      local p = utils.path_remove_trailing(path)\n      if #path_to_create == 0 and vim.fn.has(\"win32\") == 1 then\n        path_to_create = utils.path_join({ p, path_to_create })\n      else\n        path_to_create = utils.path_join({ path_to_create, p })\n      end\n      if is_last_path_file and idx == num_nodes then\n        create_and_notify(path_to_create)\n      elseif not utils.file_exists(path_to_create) then\n        local success = vim.loop.fs_mkdir(path_to_create, 493)\n        if not success then\n          notify.error(\"Could not create folder \" .. notify.render_path(path_to_create))\n          is_error = true\n          break\n        end\n        events._dispatch_folder_created(new_file_path)\n      end\n    end\n    if not is_error then\n      notify.info(notify.render_path(new_file_path) .. \" was properly created\")\n    end\n\n    -- synchronously refreshes as we can't wait for the watchers\n    find_file(utils.path_remove_trailing(new_file_path))\n  end)\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/actions/fs/init.lua",
    "content": "local M = {}\n\nM.create_file = require(\"nvim-tree.actions.fs.create-file\")\nM.remove_file = require(\"nvim-tree.actions.fs.remove-file\")\nM.rename_file = require(\"nvim-tree.actions.fs.rename-file\")\nM.trash = require(\"nvim-tree.actions.fs.trash\")\n\nfunction M.setup(opts)\n  M.remove_file.setup(opts)\n  M.rename_file.setup(opts)\n  M.trash.setup(opts)\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/actions/fs/remove-file.lua",
    "content": "local core = require(\"nvim-tree.core\")\nlocal utils = require(\"nvim-tree.utils\")\nlocal events = require(\"nvim-tree.events\")\nlocal view = require(\"nvim-tree.view\")\nlocal lib = require(\"nvim-tree.lib\")\nlocal notify = require(\"nvim-tree.notify\")\n\nlocal DirectoryLinkNode = require(\"nvim-tree.node.directory-link\")\nlocal DirectoryNode = require(\"nvim-tree.node.directory\")\nlocal Node = require(\"nvim-tree.node\")\nlocal RootNode = require(\"nvim-tree.node.root\")\n\nlocal M = {\n  config = {},\n}\n\n---@param windows integer[]\nlocal function close_windows(windows)\n  -- When floating, prevent closing the last non-floating window.\n  -- For details see #2503, #3187.\n  if view.View.float.enable then\n    local non_float_count = 0\n    for _, win in ipairs(vim.api.nvim_list_wins()) do\n      if vim.api.nvim_win_get_config(win).relative == \"\" then\n        non_float_count = non_float_count + 1\n      end\n    end\n    if non_float_count <= 1 then\n      return\n    end\n  end\n\n  for _, window in ipairs(windows) do\n    if vim.api.nvim_win_is_valid(window) then\n      vim.api.nvim_win_close(window, true)\n    end\n  end\nend\n\n---@param absolute_path string\nlocal function clear_buffer(absolute_path)\n  local bufs = vim.fn.getbufinfo({ bufloaded = 1, buflisted = 1 })\n  for _, buf in pairs(bufs) do\n    if buf.name == absolute_path then\n      local tree_winnr = vim.api.nvim_get_current_win()\n      if buf.hidden == 0 and (#bufs > 1 or view.View.float.enable) then\n        vim.api.nvim_set_current_win(buf.windows[1])\n        vim.cmd(\":bn\")\n      end\n      vim.api.nvim_buf_delete(buf.bufnr, { force = true })\n      if not view.View.float.quit_on_focus_loss then\n        vim.api.nvim_set_current_win(tree_winnr)\n      end\n      if M.config.actions.remove_file.close_window then\n        close_windows(buf.windows)\n      end\n      return\n    end\n  end\nend\n\n---@param cwd string\n---@return boolean|nil\nlocal function remove_dir(cwd)\n  local handle, err = vim.loop.fs_scandir(cwd)\n  if not handle then\n    notify.error(err)\n    return\n  end\n\n  while true do\n    local name, _ = vim.loop.fs_scandir_next(handle)\n    if not name then\n      break\n    end\n\n    local new_cwd = utils.path_join({ cwd, name })\n\n    -- Type must come from fs_stat and not fs_scandir_next to maintain sshfs compatibility\n    local stat = vim.loop.fs_stat(new_cwd)\n    -- TODO remove once 0.12 is the minimum neovim version\n    -- path incorrectly specified as an integer, fixed upstream for neovim 0.12 https://github.com/neovim/neovim/pull/33872\n    ---@diagnostic disable-next-line: param-type-mismatch\n    local lstat = vim.loop.fs_lstat(new_cwd)\n\n    local type = stat and stat.type or nil\n    -- Checks if file is a link file to ensure deletion of the symlink instead of the file it points to\n    local ltype = lstat and lstat.type or nil\n\n    if type == \"directory\" and ltype ~= \"link\" then\n      local success = remove_dir(new_cwd)\n      if not success then\n        return false\n      end\n    else\n      local success = vim.loop.fs_unlink(new_cwd)\n      if not success then\n        return false\n      end\n      clear_buffer(new_cwd)\n    end\n  end\n\n  return vim.loop.fs_rmdir(cwd)\nend\n\n--- Remove a node, notify errors, dispatch events\n---@param node Node\n---@return boolean success\nfunction M.remove(node)\n  local notify_node = notify.render_path(node.absolute_path)\n  if node:is(DirectoryNode) and not node:is(DirectoryLinkNode) then\n    local success = remove_dir(node.absolute_path)\n    if not success then\n      notify.error(\"Could not remove \" .. notify_node)\n      return false\n    end\n    events._dispatch_folder_removed(node.absolute_path)\n  else\n    events._dispatch_will_remove_file(node.absolute_path)\n    local success = vim.loop.fs_unlink(node.absolute_path)\n    if not success then\n      notify.error(\"Could not remove \" .. notify_node)\n      return false\n    end\n    events._dispatch_file_removed(node.absolute_path)\n    clear_buffer(node.absolute_path)\n  end\n  return true\nend\n\n---Remove a single node with confirmation.\n---@param node Node\nlocal function remove_one(node)\n  if node:is(RootNode) then\n    return\n  end\n\n  local function do_remove()\n    if M.remove(node) then\n      notify.info(notify.render_path(node.absolute_path) .. \" was properly removed.\")\n    end\n    local explorer = core.get_explorer()\n    if not M.config.filesystem_watchers.enable and explorer then\n      explorer:reload_explorer()\n    end\n  end\n\n  if M.config.ui.confirm.remove then\n    local prompt_select = \"Remove \" .. node.name .. \"?\"\n    local prompt_input, items_short, items_long = utils.confirm_prompt(prompt_select, M.config.ui.confirm.default_yes)\n\n    lib.prompt(prompt_input, prompt_select, items_short, items_long, \"nvimtree_remove\", function(item_short)\n      utils.clear_prompt()\n      if item_short == \"y\" or item_short == (M.config.ui.confirm.default_yes and \"\") then\n        do_remove()\n      end\n    end)\n  else\n    do_remove()\n  end\nend\n\n---Remove multiple nodes with a single confirmation prompt.\n---@param nodes Node[]\nlocal function remove_many(nodes)\n  if #nodes == 0 then\n    return\n  end\n\n  nodes = utils.filter_descendant_nodes(nodes)\n\n  local function execute()\n    local removed = 0\n    for _, node in ipairs(nodes) do\n      if not node:is(RootNode) and M.remove(node) then\n        removed = removed + 1\n      end\n    end\n    if removed > 0 then\n      notify.info(string.format(\"%d nodes properly removed.\", removed))\n    end\n    local explorer = core.get_explorer()\n    if not M.config.filesystem_watchers.enable and explorer then\n      explorer:reload_explorer()\n    end\n  end\n\n  if M.config.ui.confirm.remove then\n    local prompt_select = string.format(\"Remove %d selected?\", #nodes)\n    local prompt_input, items_short, items_long = utils.confirm_prompt(prompt_select, M.config.ui.confirm.default_yes)\n\n    lib.prompt(prompt_input, prompt_select, items_short, items_long, \"nvimtree_remove\", function(item_short)\n      utils.clear_prompt()\n      if item_short == \"y\" or item_short == (M.config.ui.confirm.default_yes and \"\") then\n        execute()\n      end\n    end)\n  else\n    execute()\n  end\nend\n\n---@param node_or_nodes Node|Node[]\nfunction M.fn(node_or_nodes)\n  if type(node_or_nodes) == \"table\" and node_or_nodes.is and node_or_nodes:is(Node) then\n    remove_one(node_or_nodes)\n  else\n    remove_many(node_or_nodes)\n  end\nend\n\nfunction M.setup(opts)\n  M.config.ui = opts.ui\n  M.config.actions = opts.actions\n  M.config.filesystem_watchers = opts.filesystem_watchers\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/actions/fs/rename-file.lua",
    "content": "local core = require(\"nvim-tree.core\")\nlocal utils = require(\"nvim-tree.utils\")\nlocal events = require(\"nvim-tree.events\")\nlocal notify = require(\"nvim-tree.notify\")\n\nlocal find_file = require(\"nvim-tree.actions.finders.find-file\").fn\n\nlocal DirectoryNode = require(\"nvim-tree.node.directory\")\n\nlocal M = {\n  config = {},\n}\n\n---@param iter function iterable\n---@return integer\nlocal function get_num_nodes(iter)\n  local i = 0\n  for _ in iter do\n    i = i + 1\n  end\n  return i\nend\n\nlocal ALLOWED_MODIFIERS = {\n  [\":p\"] = true,\n  [\":p:h\"] = true,\n  [\":t\"] = true,\n  [\":t:r\"] = true,\n}\n\nlocal function err_fmt(from, to, reason)\n  return string.format(\"Cannot rename %s -> %s: %s\", from, to, reason)\nend\n\nlocal function rename_file_exists(node, to)\n  if not utils.is_macos then\n    return utils.file_exists(to)\n  end\n\n  if string.lower(node) == string.lower(to) then\n    return false\n  end\n\n  return utils.file_exists(to)\nend\n\n---@param node Node\n---@param to string\nfunction M.rename(node, to)\n  local notify_from = notify.render_path(node.absolute_path)\n  local notify_to = notify.render_path(to)\n\n  if rename_file_exists(notify_from, notify_to) then\n    notify.warn(err_fmt(notify_from, notify_to, \"file already exists\"))\n    return\n  end\n\n  -- create a folder for each path element if the folder does not exist\n  local idx = 0\n  local path_to_create = \"\"\n\n  local num_nodes = get_num_nodes(utils.path_split(utils.path_remove_trailing(to)))\n  local is_error = false\n  for path in utils.path_split(to) do\n    idx = idx + 1\n\n    local p = utils.path_remove_trailing(path)\n    if #path_to_create == 0 and vim.fn.has(\"win32\") == 1 then\n      path_to_create = utils.path_join({ p, path_to_create })\n    else\n      path_to_create = utils.path_join({ path_to_create, p })\n    end\n\n    if idx == num_nodes then\n      events._dispatch_will_rename_node(node.absolute_path, to)\n      local success, err = vim.loop.fs_rename(node.absolute_path, to)\n\n      if not success then\n        notify.warn(err_fmt(notify_from, notify_to, err))\n        return\n      end\n    elseif not rename_file_exists(notify_from, path_to_create) then\n      local success = vim.loop.fs_mkdir(path_to_create, 493)\n      if not success then\n        notify.error(\"Could not create folder \" .. notify.render_path(path_to_create))\n        is_error = true\n        break\n      end\n      is_error = false\n    end\n  end\n\n  if not is_error then\n    notify.info(string.format(\"%s -> %s\", notify_from, notify_to))\n    utils.rename_loaded_buffers(node.absolute_path, to)\n    events._dispatch_node_renamed(node.absolute_path, to)\n  end\nend\n\n---@param node Node\n---@param modifier? string\nlocal function prompt_to_rename(node, modifier)\n  if not modifier or type(modifier) ~= \"string\" then\n    modifier = \":t\"\n  end\n\n  local explorer = core.get_explorer()\n  if not explorer then\n    return\n  end\n\n  if type(node) ~= \"table\" then\n    local node_at_cursor = explorer:get_node_at_cursor()\n    if not node_at_cursor then\n      return\n    end\n    node = node_at_cursor\n  end\n\n  -- support for only specific modifiers have been implemented\n  if not ALLOWED_MODIFIERS[modifier] then\n    notify.warn(\"Modifier \" .. vim.inspect(modifier) .. \" is not in allowed list : \" .. table.concat(ALLOWED_MODIFIERS, \",\"))\n    return\n  end\n\n  local dir = node:as(DirectoryNode)\n  if dir then\n    node = dir:last_group_node()\n  end\n  if node.name == \"..\" then\n    return\n  end\n\n  local namelen = node.name:len()\n  local directory = node.absolute_path:sub(0, namelen * -1 - 1)\n  local default_path\n  local prepend = \"\"\n  local append = \"\"\n  default_path = vim.fn.fnamemodify(node.absolute_path, modifier)\n  if modifier:sub(0, 2) == \":t\" then\n    prepend = directory\n  end\n  if modifier == \":t:r\" then\n    local extension = vim.fn.fnamemodify(node.name, \":e\")\n    append = extension:len() == 0 and \"\" or \".\" .. extension\n  end\n  if modifier == \":p:h\" then\n    default_path = default_path .. \"/\"\n  end\n\n  local input_opts = {\n    prompt = \"Rename to \",\n    default = default_path,\n    completion = \"file\",\n  }\n\n  vim.ui.input(input_opts, function(new_file_path)\n    utils.clear_prompt()\n    if not new_file_path then\n      return\n    end\n\n    local full_new_path = prepend .. new_file_path .. append\n\n    M.rename(node, full_new_path)\n    if not M.config.filesystem_watchers.enable then\n      explorer:reload_explorer()\n    end\n\n    find_file(utils.path_remove_trailing(full_new_path))\n  end)\nend\n\n---@param node Node\nfunction M.rename_node(node)\n  prompt_to_rename(node, \":t\")\nend\n\n---@param node Node\nfunction M.rename_sub(node)\n  prompt_to_rename(node, \":p:h\")\nend\n\n---@param node Node\nfunction M.rename_basename(node)\n  prompt_to_rename(node, \":t:r\")\nend\n\n---@param node Node\nfunction M.rename_full(node)\n  prompt_to_rename(node, \":p\")\nend\n\nfunction M.setup(opts)\n  M.config.filesystem_watchers = opts.filesystem_watchers\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/actions/fs/trash.lua",
    "content": "local core = require(\"nvim-tree.core\")\nlocal lib = require(\"nvim-tree.lib\")\nlocal notify = require(\"nvim-tree.notify\")\nlocal utils = require(\"nvim-tree.utils\")\nlocal events = require(\"nvim-tree.events\")\n\nlocal DirectoryLinkNode = require(\"nvim-tree.node.directory-link\")\nlocal DirectoryNode = require(\"nvim-tree.node.directory\")\nlocal Node = require(\"nvim-tree.node\")\nlocal RootNode = require(\"nvim-tree.node.root\")\n\nlocal M = {\n  config = {},\n}\n\n---@param absolute_path string\nlocal function clear_buffer(absolute_path)\n  local bufs = vim.fn.getbufinfo({ bufloaded = 1, buflisted = 1 })\n  for _, buf in pairs(bufs) do\n    if buf.name == absolute_path then\n      if buf.hidden == 0 and #bufs > 1 then\n        local winnr = vim.api.nvim_get_current_win()\n        vim.api.nvim_set_current_win(buf.windows[1])\n        vim.cmd(\":bn\")\n        vim.api.nvim_set_current_win(winnr)\n      end\n      vim.api.nvim_buf_delete(buf.bufnr, {})\n      return\n    end\n  end\nend\n\n---@param node Node\nfunction M.remove(node)\n  local binary = M.config.trash.cmd:gsub(\" .*$\", \"\")\n  if vim.fn.executable(binary) == 0 then\n    notify.warn(string.format(\"trash.cmd '%s' is not an executable.\", M.config.trash.cmd))\n    return\n  end\n\n  local err_msg = \"\"\n  local function on_stderr(_, data)\n    err_msg = err_msg .. (data and table.concat(data, \" \"))\n  end\n\n  -- trashes a path (file or folder)\n  local function trash_path(on_exit)\n    local need_sync_wait = utils.is_windows\n    local job = vim.fn.jobstart(M.config.trash.cmd .. \" \" .. vim.fn.shellescape(node.absolute_path), {\n      detach = not need_sync_wait,\n      on_exit = on_exit,\n      on_stderr = on_stderr,\n    })\n    if need_sync_wait then\n      vim.fn.jobwait({ job })\n    end\n  end\n\n  local explorer = core.get_explorer()\n\n  if node:is(DirectoryNode) and not node:is(DirectoryLinkNode) then\n    trash_path(function(_, rc)\n      if rc ~= 0 then\n        notify.warn(\"trash failed: \" .. err_msg .. \"; please see :help nvim-tree.trash\")\n        return\n      end\n      events._dispatch_folder_removed(node.absolute_path)\n      if not M.config.filesystem_watchers.enable and explorer then\n        explorer:reload_explorer()\n      end\n    end)\n  else\n    events._dispatch_will_remove_file(node.absolute_path)\n    trash_path(function(_, rc)\n      if rc ~= 0 then\n        notify.warn(\"trash failed: \" .. err_msg .. \"; please see :help nvim-tree.trash\")\n        return\n      end\n      events._dispatch_file_removed(node.absolute_path)\n      clear_buffer(node.absolute_path)\n      if not M.config.filesystem_watchers.enable and explorer then\n        explorer:reload_explorer()\n      end\n    end)\n  end\nend\n\n---Trash a single node with confirmation.\n---@param node Node\nlocal function trash_one(node)\n  if node:is(RootNode) then\n    return\n  end\n\n  local function do_trash()\n    M.remove(node)\n  end\n\n  if M.config.ui.confirm.trash then\n    local prompt_select = \"Trash \" .. node.name .. \"?\"\n    local prompt_input, items_short, items_long = utils.confirm_prompt(prompt_select, M.config.ui.confirm.default_yes)\n\n    lib.prompt(prompt_input, prompt_select, items_short, items_long, \"nvimtree_trash\", function(item_short)\n      utils.clear_prompt()\n      if item_short == \"y\" or item_short == (M.config.ui.confirm.default_yes and \"\") then\n        do_trash()\n      end\n    end)\n  else\n    do_trash()\n  end\nend\n\n---Trash multiple nodes with a single confirmation prompt.\n---@param nodes Node[]\nlocal function trash_many(nodes)\n  if #nodes == 0 then\n    return\n  end\n\n  nodes = utils.filter_descendant_nodes(nodes)\n\n  local function execute()\n    for _, node in ipairs(nodes) do\n      if not node:is(RootNode) then\n        M.remove(node)\n      end\n    end\n  end\n\n  if M.config.ui.confirm.trash then\n    local prompt_select = string.format(\"Trash %d selected?\", #nodes)\n    local prompt_input, items_short, items_long = utils.confirm_prompt(prompt_select, M.config.ui.confirm.default_yes)\n\n    lib.prompt(prompt_input, prompt_select, items_short, items_long, \"nvimtree_trash\", function(item_short)\n      utils.clear_prompt()\n      if item_short == \"y\" or item_short == (M.config.ui.confirm.default_yes and \"\") then\n        execute()\n      end\n    end)\n  else\n    execute()\n  end\nend\n\n---@param node_or_nodes Node|Node[]\nfunction M.fn(node_or_nodes)\n  if type(node_or_nodes) == \"table\" and node_or_nodes.is and node_or_nodes:is(Node) then\n    trash_one(node_or_nodes)\n  else\n    trash_many(node_or_nodes)\n  end\nend\n\nfunction M.setup(opts)\n  M.config.ui = opts.ui\n  M.config.trash = opts.trash\n  M.config.filesystem_watchers = opts.filesystem_watchers\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/actions/init.lua",
    "content": "local M = {}\n\nM.finders = require(\"nvim-tree.actions.finders\")\nM.fs = require(\"nvim-tree.actions.fs\")\nM.moves = require(\"nvim-tree.actions.moves\")\nM.node = require(\"nvim-tree.actions.node\")\nM.tree = require(\"nvim-tree.actions.tree\")\n\nfunction M.setup(opts)\n  M.fs.setup(opts)\n  M.node.setup(opts)\n  M.tree.setup(opts)\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/actions/moves/init.lua",
    "content": "local M = {}\n\nM.item = require(\"nvim-tree.actions.moves.item\")\nM.parent = require(\"nvim-tree.actions.moves.parent\")\nM.sibling = require(\"nvim-tree.actions.moves.sibling\")\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/actions/moves/item.lua",
    "content": "local view = require(\"nvim-tree.view\")\nlocal core = require(\"nvim-tree.core\")\nlocal diagnostics = require(\"nvim-tree.diagnostics\")\n\nlocal FileNode = require(\"nvim-tree.node.file\")\nlocal DirectoryNode = require(\"nvim-tree.node.directory\")\n\nlocal M = {}\nlocal MAX_DEPTH = 100\n\n---Return the status of the node or nil if no status, depending on the type of\n---status.\n---@param node Node to inspect\n---@param what string? type of status\n---@param skip_gitignored boolean? default false\n---@return boolean\nlocal function status_is_valid(node, what, skip_gitignored)\n  if what == \"git\" then\n    local git_xy = node:get_git_xy()\n    return git_xy ~= nil and (not skip_gitignored or git_xy[1] ~= \"!!\")\n  elseif what == \"diag\" then\n    local diag_status = diagnostics.get_diag_status(node)\n    return diag_status ~= nil and diag_status.value ~= nil\n  elseif what == \"opened\" then\n    return vim.fn.bufloaded(node.absolute_path) ~= 0\n  end\n\n  return false\nend\n\n---Move to the next node that has a valid status. If none found, don't move.\n---@param explorer Explorer\n---@param where string? where to move (forwards or backwards)\n---@param what string? type of status\n---@param skip_gitignored boolean? default false\nlocal function move(explorer, where, what, skip_gitignored)\n  local first_node_line = core.get_nodes_starting_line()\n  local nodes_by_line = explorer:get_nodes_by_line(first_node_line)\n  local iter_start, iter_end, iter_step, cur, first, nex\n\n  local cursor = explorer:get_cursor_position()\n  if cursor and cursor[1] < first_node_line then\n    cur = cursor[1]\n  end\n\n  if where == \"next\" then\n    iter_start, iter_end, iter_step = first_node_line, #nodes_by_line, 1\n  elseif where == \"prev\" then\n    iter_start, iter_end, iter_step = #nodes_by_line, first_node_line, -1\n  end\n\n  for line = iter_start, iter_end, iter_step do\n    local node = nodes_by_line[line]\n    local valid = status_is_valid(node, what, skip_gitignored)\n\n    if not first and valid then\n      first = line\n    end\n\n    if cursor and line == cursor[1] then\n      cur = line\n    elseif valid and cur then\n      nex = line\n      break\n    end\n  end\n\n  if nex then\n    view.set_cursor({ nex, 0 })\n  elseif vim.o.wrapscan and first then\n    view.set_cursor({ first, 0 })\n  end\nend\n\n---@param node DirectoryNode\nlocal function expand_node(node)\n  if not node.open then\n    -- Expand the node.\n    -- Should never collapse since we checked open.\n    node:expand_or_collapse(false)\n  end\nend\n\n--- Move to the next node recursively.\n---@param explorer Explorer\n---@param what string? type of status\n---@param skip_gitignored? boolean default false\nlocal function move_next_recursive(explorer, what, skip_gitignored)\n  -- If the current node:\n  -- * is a directory\n  -- * and is not the root node\n  -- * and has a git/diag status\n  -- * and is not opened\n  -- expand it.\n  local node_init = explorer:get_node_at_cursor()\n  if not node_init then\n    return\n  end\n  local valid = false\n  if node_init.name ~= \"..\" then -- root node cannot have a status\n    valid = status_is_valid(node_init, what, skip_gitignored)\n  end\n  local node_dir = node_init:as(DirectoryNode)\n  if node_dir and valid and not node_dir.open then\n    node_dir:expand_or_collapse(false)\n  end\n\n  move(explorer, \"next\", what, skip_gitignored)\n\n  local node_cur = explorer:get_node_at_cursor()\n  if not node_cur then\n    return\n  end\n\n  -- If we haven't moved at all at this point, return.\n  if node_init == node_cur then\n    return\n  end\n\n  -- i is used to limit iterations.\n  local i = 0\n  local dir_cur = node_cur:as(DirectoryNode)\n  while dir_cur and i < MAX_DEPTH do\n    expand_node(dir_cur)\n\n    move(explorer, \"next\", what, skip_gitignored)\n\n    -- Save current node.\n    node_cur = explorer:get_node_at_cursor()\n    dir_cur = node_cur and node_cur:as(DirectoryNode)\n\n    i = i + 1\n  end\nend\n\n--- Move to the previous node recursively.\n---\n--- move_prev_recursive:\n---\n--- 1) Save current as node_init.\n--  2) Call a non-recursive prev.\n--- 3) If current node is node_init's parent, call move_prev_recursive.\n--- 4) Else:\n--- 4.1) If current node is nil, is node_init (we didn't move), or is a file, return.\n--- 4.2) The current file is a directory, expand it.\n--- 4.3) Find node_init in current window, and move to it (if not found, return).\n---      If node_init is the root node (name = \"..\"), directly move to position 1.\n--- 4.4) Call a non-recursive prev.\n--- 4.5) Save the current node and start back from 4.1.\n---\n---@param explorer Explorer\n---@param what string? type of status\n---@param skip_gitignored boolean? default false\nlocal function move_prev_recursive(explorer, what, skip_gitignored)\n  local node_init, node_cur\n\n  -- 1)\n  node_init = explorer:get_node_at_cursor()\n  if node_init == nil then\n    return\n  end\n\n  -- 2)\n  move(explorer, \"prev\", what, skip_gitignored)\n\n  node_cur = explorer:get_node_at_cursor()\n  if node_cur == node_init.parent then\n    -- 3)\n    move_prev_recursive(explorer, what, skip_gitignored)\n  else\n    -- i is used to limit iterations.\n    local i = 0\n    while i < MAX_DEPTH do\n      -- 4.1)\n      if\n        node_cur == nil\n        or node_cur == node_init -- we didn't move\n        or node_cur:is(FileNode) -- node is a file\n      then\n        return\n      end\n\n      -- 4.2)\n      local node_dir = node_cur:as(DirectoryNode)\n      if node_dir then\n        expand_node(node_dir)\n      end\n\n      -- 4.3)\n      if node_init.name == \"..\" then -- root node\n        view.set_cursor({ 1, 0 })    -- move to root node (position 1)\n      else\n        local node_init_line = explorer:find_node_line(node_init)\n        if node_init_line < 0 then\n          return\n        end\n        view.set_cursor({ node_init_line, 0 })\n      end\n\n      -- 4.4)\n      move(explorer, \"prev\", what, skip_gitignored)\n\n      -- 4.5)\n      node_cur = explorer:get_node_at_cursor()\n\n      i = i + 1\n    end\n  end\nend\n\n---@class NavigationItemOpts\n---@field where string?\n---@field what string?\n---@field skip_gitignored boolean?\n---@field recurse boolean?\n\n---@param opts NavigationItemOpts\nlocal function item(opts)\n  local explorer = core.get_explorer()\n  if not explorer then\n    return\n  end\n\n  local recurse = false\n\n  -- recurse only valid for git and diag moves.\n  if (opts.what == \"git\" or opts.what == \"diag\") and opts.recurse ~= nil then\n    recurse = opts.recurse\n  end\n\n  if not recurse then\n    move(explorer, opts.where, opts.what, opts.skip_gitignored)\n    return\n  end\n\n  if opts.where == \"next\" then\n    move_next_recursive(explorer, opts.what, opts.skip_gitignored)\n  elseif opts.where == \"prev\" then\n    move_prev_recursive(explorer, opts.what, opts.skip_gitignored)\n  end\nend\n\nfunction M.git_next()\n  item({ where = \"next\", what = \"git\" })\nend\n\nfunction M.git_next_skip_gitignored()\n  item({ where = \"next\", what = \"git\", skip_gitignored = true })\nend\n\nfunction M.git_next_recursive()\n  item({ where = \"next\", what = \"git\", recurse = true })\nend\n\nfunction M.git_prev()\n  item({ where = \"prev\", what = \"git\" })\nend\n\nfunction M.git_prev_skip_gitignored()\n  item({ where = \"prev\", what = \"git\", skip_gitignored = true })\nend\n\nfunction M.git_prev_recursive()\n  item({ where = \"prev\", what = \"git\", recurse = true })\nend\n\nfunction M.diagnostics_next()\n  item({ where = \"next\", what = \"diag\" })\nend\n\nfunction M.diagnostics_next_recursive()\n  item({ where = \"next\", what = \"diag\", recurse = true })\nend\n\nfunction M.diagnostics_prev()\n  item({ where = \"prev\", what = \"diag\" })\nend\n\nfunction M.diagnostics_prev_recursive()\n  item({ where = \"prev\", what = \"diag\", recurse = true })\nend\n\nfunction M.opened_next()\n  item({ where = \"next\", what = \"opened\" })\nend\n\nfunction M.opened_prev()\n  item({ where = \"prev\", what = \"opened\" })\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/actions/moves/parent.lua",
    "content": "local view = require(\"nvim-tree.view\")\nlocal DirectoryNode = require(\"nvim-tree.node.directory\")\n\nlocal M = {}\n\n---@param node Node\n---@param should_close boolean\nlocal function move(node, should_close)\n  local dir = node:as(DirectoryNode)\n  if dir then\n    dir = dir:last_group_node()\n    if should_close and dir.open then\n      dir.open = false\n      dir.explorer.renderer:draw()\n      return\n    end\n  end\n\n  local parent = (node:get_parent_of_group() or node).parent\n\n  if not parent or not parent.parent then\n    view.set_cursor({ 1, 0 })\n    return\n  end\n\n  local _, line = parent.explorer:find_node(function(n)\n    return n.absolute_path == parent.absolute_path\n  end)\n\n  view.set_cursor({ line + 1, 0 })\n  if should_close then\n    parent.open = false\n    parent.explorer.renderer:draw()\n  end\nend\n\n---@param node Node\nfunction M.move(node)\n  move(node, false)\nend\n\n---@param node Node\nfunction M.move_close(node)\n  move(node, true)\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/actions/moves/sibling.lua",
    "content": "local core = require(\"nvim-tree.core\")\nlocal Iterator = require(\"nvim-tree.iterators.node-iterator\")\n\nlocal M = {}\n\n---@param node Node\n---@param direction \"next\"|\"prev\"|\"first\"|\"last\"\nlocal function move(node, direction)\n  if node.name == \"..\" or not direction then\n    return\n  end\n\n  local explorer = core.get_explorer()\n  if not explorer then\n    return\n  end\n\n  local first, last, next, prev = nil, nil, nil, nil\n  local found = false\n  local parent = node.parent or explorer\n  Iterator.builder(parent and parent.nodes or {})\n    :recursor(function()\n      return nil\n    end)\n    :applier(function(n)\n      first = first or n\n      last = n\n      if n.absolute_path == node.absolute_path then\n        found = true\n        return\n      end\n      prev = not found and n or prev\n      if found and not next then\n        next = n\n      end\n    end)\n    :iterate()\n\n  local target_node\n  if direction == \"first\" then\n    target_node = first\n  elseif direction == \"last\" then\n    target_node = last\n  elseif direction == \"next\" then\n    target_node = next or first\n  else\n    target_node = prev or last\n  end\n\n  if target_node then\n    explorer:focus_node_or_parent(target_node)\n  end\nend\n\n---@param node Node\nfunction M.next(node)\n  move(node, \"next\")\nend\n\n---@param node Node\nfunction M.prev(node)\n  move(node, \"prev\")\nend\n\n---@param node Node\nfunction M.first(node)\n  move(node, \"first\")\nend\n\n---@param node Node\nfunction M.last(node)\n  move(node, \"last\")\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/actions/node/buffer.lua",
    "content": "-- Copyright 2019 Yazdani Kiyan under MIT License\nlocal notify = require(\"nvim-tree.notify\")\n\nlocal M = {}\n\n---@param node Node\n---@param opts? nvim_tree.api.node.buffer.RemoveOpts\nfunction M.delete(node, opts)\n  M.delete_buffer(\"delete\", node.absolute_path, opts)\nend\n\n---@param node Node\n---@param opts? nvim_tree.api.node.buffer.RemoveOpts\nfunction M.wipe(node, opts)\n  M.delete_buffer(\"wipe\", node.absolute_path, opts)\nend\n\n---@alias ApiNodeDeleteWipeBufferMode '\"delete\"'|'\"wipe\"'\n\n---@param mode ApiNodeDeleteWipeBufferMode\n---@param filename string\n---@param opts? nvim_tree.api.node.buffer.RemoveOpts\nfunction M.delete_buffer(mode, filename, opts)\n  if type(mode) ~= \"string\" then\n    mode = \"delete\"\n  end\n\n  local buf_fn = vim.cmd.bdelete\n  if mode == \"wipe\" then\n    buf_fn = vim.cmd.bwipe\n  end\n\n  opts = opts or { force = false }\n\n  local notify_node = notify.render_path(filename)\n\n  -- check if buffer for file at cursor exists and if it is loaded\n  local bufnr_at_filename = vim.fn.bufnr(filename)\n  if bufnr_at_filename == -1 or vim.fn.getbufinfo(bufnr_at_filename)[1].loaded == 0 then\n    notify.info(\"No loaded buffer coincides with \" .. notify_node)\n    return\n  end\n\n  local force = opts.force\n  -- check if buffer is modified\n  local buf_modified = vim.fn.getbufinfo(bufnr_at_filename)[1].changed\n  if not force and buf_modified == 1 then\n    notify.error(\"Buffer for file \" .. notify_node .. \" is modified\")\n    return\n  end\n\n  buf_fn({ filename, bang = force })\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/actions/node/file-popup.lua",
    "content": "local utils = require(\"nvim-tree.utils\")\n\nlocal M = {}\n\n---@param node Node\n---@return table\nlocal function get_formatted_lines(node)\n  local stats = node.fs_stat\n  if stats == nil then\n    return {\n      \"\",\n      \"  Can't retrieve file information\",\n      \"\",\n    }\n  end\n\n  local fpath = \" fullpath: \" .. node.absolute_path\n  local created_at = \" created:  \" .. os.date(\"%x %X\", stats.birthtime.sec)\n  local modified_at = \" modified: \" .. os.date(\"%x %X\", stats.mtime.sec)\n  local accessed_at = \" accessed: \" .. os.date(\"%x %X\", stats.atime.sec)\n  local size = \" size:     \" .. utils.format_bytes(stats.size)\n\n  return {\n    fpath,\n    size,\n    accessed_at,\n    modified_at,\n    created_at,\n  }\nend\n\nlocal current_popup = nil\n\n---@param node Node\nlocal function setup_window(node)\n  local lines = get_formatted_lines(node)\n\n  local max_width = vim.fn.max(vim.tbl_map(function(n)\n    return #n\n  end, lines))\n  local open_win_config = vim.tbl_extend(\"force\", M.open_win_config, {\n    width = max_width + 1,\n    height = #lines,\n    noautocmd = true,\n    zindex = 60,\n  })\n  local winnr = vim.api.nvim_open_win(0, false, open_win_config)\n  current_popup = {\n    winnr = winnr,\n    file_path = node.absolute_path,\n  }\n  local bufnr = vim.api.nvim_create_buf(false, true)\n  vim.bo[bufnr].bufhidden = \"wipe\"\n  vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines)\n  vim.api.nvim_win_set_buf(winnr, bufnr)\nend\n\nfunction M.close_popup()\n  if current_popup ~= nil then\n    if vim.api.nvim_win_is_valid(current_popup.winnr) then\n      vim.api.nvim_win_close(current_popup.winnr, true)\n    end\n    vim.cmd(\"augroup NvimTreeRemoveFilePopup | au! CursorMoved | augroup END\")\n\n    current_popup = nil\n  end\nend\n\n---@param node Node\nfunction M.toggle_file_info(node)\n  if node.name == \"..\" then\n    return\n  end\n  if current_popup ~= nil then\n    local is_same_node = current_popup.file_path == node.absolute_path\n\n    M.close_popup()\n\n    if is_same_node then\n      return\n    end\n  end\n\n  setup_window(node)\n\n  vim.api.nvim_create_autocmd(\"CursorMoved\", {\n    group = vim.api.nvim_create_augroup(\"NvimTreeRemoveFilePopup\", {}),\n    callback = M.close_popup,\n  })\nend\n\nfunction M.setup(opts)\n  M.open_win_config = opts.actions.file_popup.open_win_config\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/actions/node/init.lua",
    "content": "local M = {}\n\nM.file_popup = require(\"nvim-tree.actions.node.file-popup\")\nM.open_file = require(\"nvim-tree.actions.node.open-file\")\nM.run_command = require(\"nvim-tree.actions.node.run-command\")\nM.system_open = require(\"nvim-tree.actions.node.system-open\")\nM.buffer = require(\"nvim-tree.actions.node.buffer\")\n\nfunction M.setup(opts)\n  require(\"nvim-tree.actions.node.system-open\").setup(opts)\n  require(\"nvim-tree.actions.node.file-popup\").setup(opts)\n  require(\"nvim-tree.actions.node.open-file\").setup(opts)\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/actions/node/open-file.lua",
    "content": "-- Copyright 2019 Yazdani Kiyan under MIT License\nlocal lib = require(\"nvim-tree.lib\")\nlocal notify = require(\"nvim-tree.notify\")\nlocal utils = require(\"nvim-tree.utils\")\nlocal full_name = require(\"nvim-tree.renderer.components.full-name\")\nlocal view = require(\"nvim-tree.view\")\n\nlocal DirectoryNode = require(\"nvim-tree.node.directory\")\nlocal FileLinkNode = require(\"nvim-tree.node.file-link\")\nlocal RootNode = require(\"nvim-tree.node.root\")\n\n---@class NodeEditOpts\n---@field quit_on_open boolean|nil default false\n---@field focus boolean|nil default true\n\n---@alias NodeOpenFileMode \"\"|\"change_dir\"|\"drop\"|\"edit\"|\"edit_in_place\"|\"edit_no_picker\"|\"preview\"|\"preview_no_picker\"|\"split\"|\"split_no_picker\"|\"tab_drop\"|\"tabnew\"|\"toggle_group_empty\"|\"vsplit\"|\"vsplit_no_picker\"\n\nlocal M = {}\n\n---Get single char from user input\n---@return string\nlocal function get_user_input_char()\n  local c = vim.fn.getchar()\n  while type(c) ~= \"number\" do\n    c = vim.fn.getchar()\n  end\n  return vim.fn.nr2char(c)\nend\n\n---Get all windows in the current tabpage that aren't NvimTree.\n---@return table with valid win_ids\nlocal function usable_win_ids()\n  local tabpage = vim.api.nvim_get_current_tabpage()\n  local win_ids = vim.api.nvim_tabpage_list_wins(tabpage)\n  local tree_winid = view.get_winnr(tabpage)\n\n  return vim.tbl_filter(function(id)\n    local bufid = vim.api.nvim_win_get_buf(id)\n    for option, v in pairs(M.window_picker.exclude) do\n      local ok, option_value\n      if vim.fn.has(\"nvim-0.10\") == 1 then\n        ok, option_value = pcall(vim.api.nvim_get_option_value, option, { buf = bufid })\n      else\n        ok, option_value = pcall(vim.api.nvim_buf_get_option, bufid, option) ---@diagnostic disable-line: deprecated\n      end\n\n      if ok and vim.tbl_contains(v, option_value) then\n        return false\n      end\n    end\n\n    local win_config = vim.api.nvim_win_get_config(id)\n    return id ~= tree_winid\n      and id ~= full_name.popup_win\n      and win_config.focusable\n      and not win_config.hide\n      and not win_config.external\n      or false\n  end, win_ids)\nend\n\n---Get user to pick a window in the tab that is not NvimTree.\n---@return integer|nil -- If a valid window was picked, return its id. If an\n---       invalid window was picked / user canceled, return nil. If there are\n---       no selectable windows, return -1.\nlocal function pick_win_id()\n  local selectable = usable_win_ids()\n\n  -- If there are no selectable windows: return. If there's only 1, return it without picking.\n  if #selectable == 0 then\n    return -1\n  end\n  if #selectable == 1 then\n    return selectable[1]\n  end\n\n  if #M.window_picker.chars < #selectable then\n    notify.error(string.format(\"More windows (%d) than actions.open_file.window_picker.chars (%d).\", #selectable, #M.window_picker.chars))\n    return nil\n  end\n\n  local i = 1\n  local win_opts_selectable = {}\n  local win_opts_unselectable = {}\n  local win_map = {}\n  local laststatus = vim.o.laststatus\n  vim.o.laststatus = 2\n  local fillchars = vim.opt.fillchars:get()\n  local stl = fillchars.stl\n  local stlnc = fillchars.stlnc\n  fillchars.stl = nil\n  fillchars.stlnc = nil\n  vim.opt.fillchars = fillchars\n  fillchars.stl = stl\n  fillchars.stlnc = stlnc\n\n  local tabpage = vim.api.nvim_get_current_tabpage()\n  local win_ids = vim.api.nvim_tabpage_list_wins(tabpage)\n\n  local not_selectable = vim.tbl_filter(function(id)\n    return not vim.tbl_contains(selectable, id)\n  end, win_ids)\n\n  if laststatus == 3 then\n    for _, win_id in ipairs(not_selectable) do\n      local ok_status, statusline\n\n      if vim.fn.has(\"nvim-0.10\") == 1 then\n        ok_status, statusline = pcall(vim.api.nvim_get_option_value, \"statusline\", { win = win_id })\n      else\n        ok_status, statusline = pcall(vim.api.nvim_win_get_option, win_id, \"statusline\") ---@diagnostic disable-line: deprecated\n      end\n\n      win_opts_unselectable[win_id] = {\n        statusline = ok_status and statusline or \"\",\n      }\n\n      -- Clear statusline for windows not selectable\n      if vim.fn.has(\"nvim-0.10\") == 1 then\n        vim.api.nvim_set_option_value(\"statusline\", \" \", { win = win_id })\n      else\n        vim.api.nvim_win_set_option(win_id, \"statusline\", \" \") ---@diagnostic disable-line: deprecated\n      end\n    end\n  end\n\n  -- Setup UI\n  for _, id in ipairs(selectable) do\n    local char = M.window_picker.chars:sub(i, i)\n\n    local ok_status, statusline, ok_hl, winhl\n    if vim.fn.has(\"nvim-0.10\") == 1 then\n      ok_status, statusline = pcall(vim.api.nvim_get_option_value, \"statusline\", { win = id })\n      ok_hl, winhl = pcall(vim.api.nvim_get_option_value, \"winhl\", { win = id })\n    else\n      ok_status, statusline = pcall(vim.api.nvim_win_get_option, id, \"statusline\") ---@diagnostic disable-line: deprecated\n      ok_hl, winhl = pcall(vim.api.nvim_win_get_option, id, \"winhl\") ---@diagnostic disable-line: deprecated\n    end\n\n    win_opts_selectable[id] = {\n      statusline = ok_status and statusline or \"\",\n      winhl = ok_hl and winhl or \"\",\n    }\n    win_map[char] = id\n\n    if vim.fn.has(\"nvim-0.10\") == 1 then\n      vim.api.nvim_set_option_value(\"statusline\", \"%=\" .. char .. \"%=\",                                                { win = id })\n      vim.api.nvim_set_option_value(\"winhl\",      \"StatusLine:NvimTreeWindowPicker,StatusLineNC:NvimTreeWindowPicker\", { win = id })\n    else\n      vim.api.nvim_win_set_option(id, \"statusline\", \"%=\" .. char .. \"%=\") ---@diagnostic disable-line: deprecated\n      vim.api.nvim_win_set_option(id, \"winhl\",      \"StatusLine:NvimTreeWindowPicker,StatusLineNC:NvimTreeWindowPicker\") ---@diagnostic disable-line: deprecated\n    end\n\n    i = i + 1\n    if i > #M.window_picker.chars then\n      break\n    end\n  end\n\n  vim.cmd(\"redraw\")\n  if vim.opt.cmdheight._value ~= 0 then\n    print(\"Pick window: \")\n  end\n  local _, resp = pcall(get_user_input_char)\n  resp = (resp or \"\"):upper()\n  utils.clear_prompt()\n\n  -- Restore window options\n  for _, id in ipairs(selectable) do\n    for opt, value in pairs(win_opts_selectable[id]) do\n      if vim.fn.has(\"nvim-0.10\") == 1 then\n        vim.api.nvim_set_option_value(opt, value, { win = id })\n      else\n        vim.api.nvim_win_set_option(id, opt, value) ---@diagnostic disable-line: deprecated\n      end\n    end\n  end\n\n  if laststatus == 3 then\n    for _, id in ipairs(not_selectable) do\n      -- Ensure window still exists at this point\n      if vim.api.nvim_win_is_valid(id) then\n        for opt, value in pairs(win_opts_unselectable[id]) do\n          if vim.fn.has(\"nvim-0.10\") == 1 then\n            vim.api.nvim_set_option_value(opt, value, { win = id })\n          else\n            vim.api.nvim_win_set_option(id, opt, value) ---@diagnostic disable-line: deprecated\n          end\n        end\n      end\n    end\n  end\n\n  vim.o.laststatus = laststatus\n  vim.opt.fillchars = fillchars\n\n  if not vim.tbl_contains(vim.split(M.window_picker.chars, \"\"), resp) then\n    return\n  end\n\n  return win_map[resp]\nend\n\nlocal function open_file_in_tab(filename)\n  if M.quit_on_open then\n    view.close()\n  end\n  if M.relative_path then\n    filename = utils.path_relative(filename, vim.fn.getcwd())\n  end\n  vim.cmd.tabnew()\n  vim.bo.bufhidden = \"wipe\"\n  -- Following vim.fn.tabnew the # buffer may be set to the tree buffer. There is no way to clear the # buffer via vim.fn.setreg as it requires a valid buffer. Clear # by setting it to a new temporary scratch buffer.\n  if utils.is_nvim_tree_buf(vim.fn.bufnr(\"#\")) then\n    local tmpbuf = vim.api.nvim_create_buf(false, true)\n    vim.fn.setreg(\"#\", tmpbuf)\n    vim.api.nvim_buf_delete(tmpbuf, { force = true })\n  end\n  vim.cmd.edit(vim.fn.fnameescape(filename))\nend\n\nlocal function drop(filename)\n  if M.quit_on_open then\n    view.close()\n  end\n  if M.relative_path then\n    filename = utils.path_relative(filename, vim.fn.getcwd())\n  end\n  vim.cmd(\"drop \" .. vim.fn.fnameescape(filename))\nend\n\nlocal function tab_drop(filename)\n  if M.quit_on_open then\n    view.close()\n  end\n  if M.relative_path then\n    filename = utils.path_relative(filename, vim.fn.getcwd())\n  end\n  vim.cmd(\"tab :drop \" .. vim.fn.fnameescape(filename))\nend\n\nlocal function on_preview(buf_loaded)\n  if not buf_loaded then\n    vim.bo.bufhidden = \"delete\"\n\n    vim.api.nvim_create_autocmd({ \"TextChanged\", \"TextChangedI\" }, {\n      group = vim.api.nvim_create_augroup(\"RemoveBufHidden\", {}),\n      buffer = vim.api.nvim_get_current_buf(),\n      callback = function()\n        vim.bo.bufhidden = \"\"\n      end,\n      once = true,\n    })\n  end\n  view.focus()\nend\n\nlocal function get_target_winid(mode)\n  local target_winid\n  if not M.window_picker.enable or string.find(mode, \"no_picker\") then\n    target_winid = lib.target_winid\n    local usable_wins = usable_win_ids()\n    -- first available usable window\n    if not vim.tbl_contains(usable_wins, target_winid) then\n      if #usable_wins > 0 then\n        target_winid = usable_wins[1]\n      else\n        target_winid = -1\n      end\n    end\n  else\n    -- pick a window\n    if type(M.window_picker.picker) == \"function\" then\n      target_winid = M.window_picker.picker()\n    else\n      target_winid = pick_win_id()\n    end\n    if target_winid == nil then\n      -- pick failed/cancelled\n      return\n    end\n  end\n\n  if target_winid == -1 then\n    target_winid = lib.target_winid\n  end\n  return target_winid\nend\n\n-- This is only to avoid the BufEnter for nvim-tree to trigger\n-- which would cause find-file to run on an invalid file.\nlocal function set_current_win_no_autocmd(winid, autocmd)\n  local eventignore = vim.opt.eventignore:get()\n  vim.opt.eventignore:append(autocmd)\n  vim.api.nvim_set_current_win(winid)\n  vim.opt.eventignore = eventignore\nend\n\n---@param filename string\n---@param mode NodeOpenFileMode\nlocal function open_in_new_window(filename, mode)\n  if type(mode) ~= \"string\" then\n    mode = \"\"\n  end\n\n  local target_winid = get_target_winid(mode)\n  if not target_winid then\n    return\n  end\n\n  local position = string.find(mode, \"no_picker\")\n  if position then\n    mode = string.sub(mode, 0, position - 2)\n  end\n\n  -- non-floating, non-nvim-tree windows\n  local win_ids = vim.tbl_filter(function(id)\n    local config = vim.api.nvim_win_get_config(id)\n    local bufnr = vim.api.nvim_win_get_buf(id)\n    return config and config.relative == \"\" or utils.is_nvim_tree_buf(bufnr)\n  end, vim.api.nvim_list_wins())\n\n  local create_new_window = #win_ids == 1 -- This implies that the nvim-tree window is the only one\n  local new_window_side = (view.View.side == \"right\") and \"aboveleft\" or \"belowright\"\n\n  -- Target is invalid: create new window\n  if not vim.tbl_contains(win_ids, target_winid) then\n    vim.cmd(new_window_side .. \" vsplit\")\n    target_winid = vim.api.nvim_get_current_win()\n    lib.target_winid = target_winid\n\n    -- No need to split, as we created a new window.\n    create_new_window = false\n    if mode:match(\"split$\") then\n      mode = \"edit\"\n    end\n  elseif not vim.o.hidden then\n    -- If `hidden` is not enabled, check if buffer in target window is\n    -- modified, and create new split if it is.\n    local target_bufid = vim.api.nvim_win_get_buf(target_winid)\n\n    local modified\n    if vim.fn.has(\"nvim-0.10\") == 1 then\n      modified = vim.api.nvim_get_option_value(\"modified\", { buf = target_bufid })\n    else\n      modified = vim.api.nvim_buf_get_option(target_bufid, \"modified\") ---@diagnostic disable-line: deprecated\n    end\n\n    if modified then\n      if not mode:match(\"split$\") then\n        mode = \"vsplit\"\n      end\n    end\n  end\n\n  if (mode == \"preview\" or mode == \"preview_no_picker\") and view.View.float.enable then\n    -- ignore \"WinLeave\" autocmd on preview\n    -- because the registered \"WinLeave\"\n    -- will kill the floating window immediately\n    set_current_win_no_autocmd(target_winid, { \"WinLeave\", \"BufEnter\" })\n  else\n    set_current_win_no_autocmd(target_winid, { \"BufEnter\" })\n  end\n\n  local fname\n  if M.relative_path then\n    fname = utils.escape_special_chars(vim.fn.fnameescape(utils.path_relative(filename, vim.fn.getcwd())))\n  else\n    fname = utils.escape_special_chars(vim.fn.fnameescape(filename))\n  end\n\n  local command\n  if create_new_window then\n    -- generated from vim.api.nvim_parse_cmd(\"belowright vsplit foo\", {})\n    command = { cmd = \"vsplit\", mods = { split = new_window_side }, args = { fname } }\n  elseif mode:match(\"split$\") then\n    command = { cmd = mode, args = { fname } }\n  else\n    command = { cmd = \"edit\", args = { fname } }\n  end\n\n  pcall(vim.api.nvim_cmd, command, { output = false })\n  lib.set_target_win()\nend\n\nlocal function is_already_loaded(filename)\n  for _, buf_id in ipairs(vim.api.nvim_list_bufs()) do\n    if vim.api.nvim_buf_is_loaded(buf_id) and filename == vim.api.nvim_buf_get_name(buf_id) then\n      return true\n    end\n  end\n  return false\nend\n\nlocal function edit_in_current_buf(filename)\n  require(\"nvim-tree.view\").abandon_current_window()\n  if M.relative_path then\n    filename = utils.path_relative(filename, vim.fn.getcwd())\n  end\n  vim.cmd(\"keepalt keepjumps edit \" .. vim.fn.fnameescape(filename))\nend\n\n---@param mode NodeOpenFileMode\n---@param filename string\n---@return nil\nfunction M.fn(mode, filename)\n  if type(mode) ~= \"string\" then\n    mode = \"\"\n  end\n\n  if mode == \"tabnew\" then\n    return open_file_in_tab(filename)\n  end\n\n  if mode == \"drop\" then\n    return drop(filename)\n  end\n\n  if mode == \"tab_drop\" then\n    return tab_drop(filename)\n  end\n\n  if mode == \"edit_in_place\" then\n    return edit_in_current_buf(filename)\n  end\n\n  local buf_loaded = is_already_loaded(filename)\n\n  local found_win = utils.get_win_buf_from_path(filename)\n  if found_win and (mode == \"preview\" or mode == \"preview_no_picker\") then\n    return\n  end\n\n  if not found_win then\n    open_in_new_window(filename, mode)\n  else\n    vim.api.nvim_set_current_win(found_win)\n    vim.bo.bufhidden = \"\"\n  end\n\n  if M.resize_window then\n    view.resize()\n  end\n\n  if mode == \"preview\" or mode == \"preview_no_picker\" then\n    return on_preview(buf_loaded)\n  end\n\n  if M.quit_on_open then\n    view.close()\n  end\nend\n\n---@param mode string\n---@param node Node\n---@param edit_opts NodeEditOpts?\nlocal function edit(mode, node, edit_opts)\n  local file_link = node:as(FileLinkNode)\n  local path = file_link and file_link.link_to or node.absolute_path\n  local cur_tabpage = vim.api.nvim_get_current_tabpage()\n\n  M.fn(mode, path)\n\n  edit_opts = edit_opts or {}\n\n  local mode_unsupported_quit_on_open = mode == \"drop\" or mode == \"tab_drop\" or mode == \"edit_in_place\"\n  if not mode_unsupported_quit_on_open and edit_opts.quit_on_open then\n    view.close(cur_tabpage)\n  end\n\n  local mode_unsupported_focus = mode == \"drop\" or mode == \"tab_drop\" or mode == \"edit_in_place\"\n  local focus = edit_opts.focus == nil or edit_opts.focus == true\n  if not mode_unsupported_focus and not focus then\n    -- if mode == \"tabnew\" a new tab will be opened and we need to focus back to the previous tab\n    if mode == \"tabnew\" then\n      vim.cmd(\":tabprev\")\n    end\n    view.focus()\n  end\nend\n\n---@param node Node\n---@param mode NodeOpenFileMode\n---@param toggle_group boolean?\n---@param edit_opts NodeEditOpts?\nlocal function open_or_expand_or_dir_up(node, mode, toggle_group, edit_opts)\n  local root = node:as(RootNode)\n  local dir = node:as(DirectoryNode)\n\n  if root or node.name == \"..\" then\n    local explorer = require(\"nvim-tree.core\").get_explorer()\n    if explorer then\n      explorer:change_dir(\"..\")\n    end\n  elseif dir then\n    dir:expand_or_collapse(toggle_group)\n  elseif not toggle_group then\n    edit(mode, node, edit_opts)\n  end\nend\n\n---@param node Node\nfunction M.toggle_group_empty(node)\n  open_or_expand_or_dir_up(node, \"toggle_group_empty\", true)\nend\n\n---@param node Node\nfunction M.preview(node)\n  open_or_expand_or_dir_up(node, \"preview\")\nend\n\n---@param node Node\nfunction M.preview_no_picker(node)\n  open_or_expand_or_dir_up(node, \"preview_no_picker\")\nend\n\n---@param node Node\nfunction M.edit(node)\n  open_or_expand_or_dir_up(node, \"edit\")\nend\n\n---@param node Node\nfunction M.drop(node)\n  open_or_expand_or_dir_up(node, \"drop\")\nend\n\n---@param node Node\nfunction M.tab_drop(node)\n  open_or_expand_or_dir_up(node, \"tab_drop\")\nend\n\n---@param node Node\nfunction M.replace_tree_buffer(node)\n  open_or_expand_or_dir_up(node, \"edit_in_place\")\nend\n\n---@param node Node\nfunction M.no_window_picker(node)\n  open_or_expand_or_dir_up(node, \"edit_no_picker\")\nend\n\n---@param node Node\nfunction M.vertical(node)\n  open_or_expand_or_dir_up(node, \"vsplit\")\nend\n\n---@param node Node\nfunction M.vertical_no_picker(node)\n  open_or_expand_or_dir_up(node, \"vsplit_no_picker\")\nend\n\n---@param node Node\nfunction M.horizontal(node)\n  open_or_expand_or_dir_up(node, \"split\")\nend\n\n---@param node Node\nfunction M.horizontal_no_picker(node)\n  open_or_expand_or_dir_up(node, \"split_no_picker\")\nend\n\n---@param node Node\nfunction M.tab(node)\n  open_or_expand_or_dir_up(node, \"tabnew\")\nend\n\nfunction M.setup(opts)\n  M.quit_on_open = opts.actions.open_file.quit_on_open\n  M.resize_window = opts.actions.open_file.resize_window\n  M.relative_path = opts.actions.open_file.relative_path\n  if opts.actions.open_file.window_picker.chars then\n    opts.actions.open_file.window_picker.chars = tostring(opts.actions.open_file.window_picker.chars):upper()\n  end\n  M.window_picker = opts.actions.open_file.window_picker\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/actions/node/run-command.lua",
    "content": "local utils = require(\"nvim-tree.utils\")\nlocal core = require(\"nvim-tree.core\")\n\nlocal M = {}\n\n---Retrieves the absolute path to the node.\n---Safely handles the node representing the current directory\n---(the topmost node in the nvim-tree window)\n---@param node Node\n---@return string\nlocal function get_node_path(node)\n  local cwd = core.get_cwd()\n  if node.name == \"..\" and cwd then\n    return utils.path_remove_trailing(cwd)\n  else\n    return node.absolute_path\n  end\nend\n\n---@param node Node\nfunction M.run_file_command(node)\n  local node_path = get_node_path(node)\n  vim.api.nvim_input(\": \" .. node_path .. \"<Home>\")\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/actions/node/system-open.lua",
    "content": "local notify = require(\"nvim-tree.notify\")\nlocal utils = require(\"nvim-tree.utils\")\n\nlocal M = {}\n\n---@param node Node\nlocal function user(node)\n  if #M.config.system_open.cmd == 0 then\n    require(\"nvim-tree.utils\").notify.warn(\"Cannot open file with system application. Unrecognized platform.\")\n    return\n  end\n\n  local process = {\n    cmd = M.config.system_open.cmd,\n    args = M.config.system_open.args,\n    errors = \"\\n\",\n    stderr = vim.loop.new_pipe(false),\n  }\n  table.insert(process.args, node.link_to or node.absolute_path)\n\n  local opts = {\n    args = process.args,\n    stdio = { nil, nil, process.stderr },\n    detached = true,\n  }\n\n  process.handle, process.pid = vim.loop.spawn(process.cmd, opts, function(code)\n    process.stderr:read_stop()\n    process.stderr:close()\n    process.handle:close()\n    if code ~= 0 then\n      notify.warn(string.format(\"system_open failed with return code %d: %s\", code, process.errors))\n    end\n  end)\n\n  table.remove(process.args)\n  if not process.handle then\n    notify.warn(string.format(\"system_open failed to spawn command '%s': %s\", process.cmd, process.pid))\n    return\n  end\n  vim.loop.read_start(process.stderr, function(err, data)\n    if err then\n      return\n    end\n    if data then\n      process.errors = process.errors .. data\n    end\n  end)\n  vim.loop.unref(process.handle)\nend\n\n---@param node Node\nlocal function native(node)\n  local _, err = vim.ui.open(node.link_to or node.absolute_path)\n\n  -- err only provided on opener executable not found hence logging path is not useful\n  if err then\n    notify.warn(err)\n  end\nend\n\n---@param node Node\nfunction M.fn(node)\n  M.open(node)\nend\n\n-- TODO #2430 always use native once 0.10 is the minimum neovim version\nfunction M.setup(opts)\n  M.config = {}\n  M.config.system_open = opts.system_open or {}\n\n  if vim.fn.has(\"nvim-0.10\") == 1 and #M.config.system_open.cmd == 0 then\n    M.open = native\n  else\n    M.open = user\n    if #M.config.system_open.cmd == 0 then\n      if utils.is_windows then\n        M.config.system_open = {\n          cmd = \"cmd\",\n          args = { \"/c\", \"start\", '\"\"' },\n        }\n      elseif utils.is_macos then\n        M.config.system_open.cmd = \"open\"\n      elseif utils.is_unix then\n        M.config.system_open.cmd = \"xdg-open\"\n      end\n    end\n  end\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/actions/tree/change-dir.lua",
    "content": "local core = require(\"nvim-tree.core\")\nlocal find_file = require(\"nvim-tree.actions.tree.find-file\")\n\nlocal M = {}\n\n---@param name? string\nfunction M.fn(name)\n  local explorer = core.get_explorer()\n  if name and explorer then\n    explorer:change_dir(name)\n  end\n\n  if M.config.update_focused_file.update_root.enable then\n    find_file.fn()\n  end\nend\n\n---@param config nvim_tree.config\nfunction M.setup(config)\n  M.config = config\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/actions/tree/collapse.lua",
    "content": "local utils = require(\"nvim-tree.utils\")\nlocal core = require(\"nvim-tree.core\")\nlocal Iterator = require(\"nvim-tree.iterators.node-iterator\")\n\nlocal FileNode = require(\"nvim-tree.node.file\")\nlocal DirectoryNode = require(\"nvim-tree.node.directory\")\n\nlocal M = {}\n\n---@return fun(path: string): boolean\nlocal function buf_match()\n  local buffer_paths = vim.tbl_map(function(buffer)\n    return vim.api.nvim_buf_get_name(buffer)\n  end, vim.api.nvim_list_bufs())\n\n  return function(path)\n    for _, buffer_path in ipairs(buffer_paths) do\n      local matches = utils.str_find(buffer_path, path)\n      if matches then\n        return true\n      end\n    end\n    return false\n  end\nend\n\n---Collapse a node, root if nil\n---@param node? Node\n---@param opts nvim_tree.api.node.collapse.Opts\nlocal function collapse(node, opts)\n  local explorer = core.get_explorer()\n  if not explorer then\n    return\n  end\n\n  node = node or explorer\n\n  local node_at_cursor = explorer:get_node_at_cursor()\n  if not node_at_cursor then\n    return\n  end\n\n  local matches = buf_match()\n\n  Iterator.builder({ node:is(FileNode) and node.parent or node:as(DirectoryNode) })\n    :hidden()\n    :applier(function(n)\n      local dir = n:as(DirectoryNode)\n      if dir then\n        dir.open = opts.keep_buffers == true and matches(dir.absolute_path)\n      end\n    end)\n    :recursor(function(n)\n      return n.group_next and { n.group_next } or n.nodes\n    end)\n    :iterate()\n\n  explorer.renderer:draw()\n  explorer:focus_node_or_parent(node_at_cursor)\nend\n\n\n---@param opts? nvim_tree.api.node.collapse.Opts|boolean legacy -> opts.keep_buffers\nfunction M.all(opts)\n  -- legacy arguments\n  if type(opts) == \"boolean\" then\n    opts = {\n      keep_buffers = opts,\n    }\n  end\n\n  collapse(nil, opts or {})\nend\n\n---@param node? Node\n---@param opts? nvim_tree.api.node.collapse.Opts\nfunction M.node(node, opts)\n  collapse(node, opts or {})\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/actions/tree/find-file.lua",
    "content": "local core = require(\"nvim-tree.core\")\nlocal lib = require(\"nvim-tree.lib\")\nlocal view = require(\"nvim-tree.view\")\nlocal finders_find_file = require(\"nvim-tree.actions.finders.find-file\")\n\nlocal M = {}\n\n--- Find file or buffer\n---@param opts? nvim_tree.api.tree.find_file.Opts|boolean legacy -> opts.buf\nfunction M.fn(opts)\n  -- legacy arguments\n  if type(opts) == \"string\" then\n    opts = {\n      buf = opts,\n    }\n  end\n  opts = opts or {}\n\n  -- do nothing if closed and open not requested\n  if not opts.open and not core.get_explorer() then\n    return\n  end\n\n  local bufnr, path\n\n  -- (optional) buffer number and path\n  local opts_buf = opts.buf\n  if type(opts_buf) == \"nil\" then\n    bufnr = vim.api.nvim_get_current_buf()\n    path = vim.api.nvim_buf_get_name(bufnr)\n  elseif type(opts_buf) == \"number\" then\n    if not vim.api.nvim_buf_is_valid(opts_buf) then\n      return\n    end\n    bufnr = opts_buf\n    path = vim.api.nvim_buf_get_name(bufnr)\n  elseif type(opts_buf) == \"string\" then\n    bufnr = nil\n    path = tostring(opts_buf)\n  else\n    return\n  end\n\n  if view.is_visible() then\n    -- focus\n    if opts.focus then\n      lib.set_target_win()\n      view.focus()\n    end\n  elseif opts.open then\n    -- open\n    lib.open({ current_window = opts.current_window, winid = opts.winid })\n    if not opts.focus then\n      vim.cmd(\"noautocmd wincmd p\")\n    end\n  end\n\n  -- update root\n  if opts.update_root or M.config.update_focused_file.update_root.enable then\n    require(\"nvim-tree\").change_root(path, bufnr)\n  end\n\n  -- find\n  finders_find_file.fn(path)\nend\n\nfunction M.setup(opts)\n  M.config = opts or {}\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/actions/tree/init.lua",
    "content": "local M = {}\n\nM.change_dir = require(\"nvim-tree.actions.tree.change-dir\")\nM.find_file = require(\"nvim-tree.actions.tree.find-file\")\nM.collapse = require(\"nvim-tree.actions.tree.collapse\")\nM.open = require(\"nvim-tree.actions.tree.open\")\nM.toggle = require(\"nvim-tree.actions.tree.toggle\")\nM.resize = require(\"nvim-tree.actions.tree.resize\")\n\nfunction M.setup(opts)\n  M.change_dir.setup(opts)\n  M.find_file.setup(opts)\n  M.open.setup(opts)\n  M.toggle.setup(opts)\n  M.resize.setup(opts)\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/actions/tree/open.lua",
    "content": "local lib = require(\"nvim-tree.lib\")\nlocal view = require(\"nvim-tree.view\")\nlocal finders_find_file = require(\"nvim-tree.actions.finders.find-file\")\n\nlocal M = {}\n\n---Open the tree, focusing if already open.\n---@param opts? nvim_tree.api.tree.open.Opts|string legacy -> opts.path\nfunction M.fn(opts)\n  -- legacy arguments\n  if type(opts) == \"string\" then\n    opts = {\n      path = opts,\n    }\n  end\n  opts = opts or {}\n\n  local previous_buf = vim.api.nvim_get_current_buf()\n  local previous_path = vim.api.nvim_buf_get_name(previous_buf)\n\n  -- sanitise path\n  if type(opts.path) ~= \"string\" or vim.fn.isdirectory(opts.path) == 0 then\n    opts.path = nil\n  end\n\n  if view.is_visible() then\n    -- focus\n    lib.set_target_win()\n    view.focus()\n  else\n    -- open\n    lib.open({\n      path = opts.path,\n      current_window = opts.current_window,\n      winid = opts.winid,\n    })\n  end\n\n  -- find file\n  if M.config.update_focused_file.enable or opts.find_file then\n    -- update root\n    if opts.update_root then\n      require(\"nvim-tree\").change_root(previous_path, previous_buf)\n    end\n\n    -- find\n    finders_find_file.fn(previous_path)\n  end\nend\n\nfunction M.setup(opts)\n  M.config = opts or {}\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/actions/tree/resize.lua",
    "content": "local view = require(\"nvim-tree.view\")\n\nlocal M = {}\n\n---Resize the tree, persisting the new size.\n---@param opts? nvim_tree.api.tree.resize.Opts\nfunction M.fn(opts)\n  if opts == nil then\n    -- reset to config values\n    view.configure_width()\n    view.resize()\n    return\n  end\n\n  local options = opts or {}\n  local width_cfg = options.width\n\n  if width_cfg ~= nil then\n    view.configure_width(width_cfg)\n    view.resize()\n    return\n  end\n\n  if not view.is_width_determined() then\n    -- {absolute} and {relative} do nothing when {width} is a function.\n    return\n  end\n\n  local absolute = options.absolute\n  if type(absolute) == \"number\" then\n    view.resize(absolute)\n    return\n  end\n\n  local relative = options.relative\n  if type(relative) == \"number\" then\n    local relative_size = tostring(relative)\n    if relative > 0 then\n      relative_size = \"+\" .. relative_size\n    end\n\n    view.resize(relative_size)\n    return\n  end\nend\n\nfunction M.setup(opts)\n  M.config = opts or {}\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/actions/tree/toggle.lua",
    "content": "local lib = require(\"nvim-tree.lib\")\nlocal view = require(\"nvim-tree.view\")\nlocal finders_find_file = require(\"nvim-tree.actions.finders.find-file\")\n\nlocal M = {}\n\n---Toggle the tree.\n---@param opts? nvim_tree.api.tree.toggle.Opts|boolean legacy -> opts.find_file\n---@param no_focus? string legacy -> opts.focus\n---@param cwd? boolean legacy -> opts.path\n---@param bang? boolean legacy -> opts.update_root\nfunction M.fn(opts, no_focus, cwd, bang)\n  -- legacy arguments\n  if type(opts) == \"boolean\" then\n    opts = {\n      find_file = opts,\n    }\n    if type(cwd) == \"string\" then\n      opts.path = cwd\n    end\n    if type(no_focus) == \"boolean\" then\n      opts.focus = not no_focus\n    end\n    if type(bang) == \"boolean\" then\n      opts.update_root = bang\n    end\n  end\n  opts = opts or {}\n\n  -- defaults\n  if opts.focus == nil then\n    opts.focus = true\n  end\n\n  local previous_buf = vim.api.nvim_get_current_buf()\n  local previous_path = vim.api.nvim_buf_get_name(previous_buf)\n\n  -- sanitise path\n  if type(opts.path) ~= \"string\" or vim.fn.isdirectory(opts.path) == 0 then\n    opts.path = nil\n  end\n\n  if view.is_visible() then\n    -- close\n    view.close()\n  else\n    -- open\n    lib.open({\n      path = opts.path,\n      current_window = opts.current_window,\n      winid = opts.winid,\n    })\n\n    -- find file\n    if M.config.update_focused_file.enable or opts.find_file then\n      -- update root\n      if opts.update_root then\n        require(\"nvim-tree\").change_root(previous_path, previous_buf)\n      end\n\n      -- find\n      finders_find_file.fn(previous_path)\n    end\n\n    -- restore focus\n    if not opts.focus then\n      vim.cmd(\"noautocmd wincmd p\")\n    end\n  end\nend\n\nfunction M.setup(opts)\n  M.config = opts or {}\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/api/impl.lua",
    "content": "---Hydrates API meta functions with their implementations.\n---For startup performance reasons, all API implementation's requires must be\n---done at call time, not when this module is required.\n\nlocal M = {}\n\n---Walk the entire API, hydrating all functions with the error notification.\n---Do not hydrate classes i.e. anything with a metatable.\n---@param api table not properly typed to prevent LSP from referencing implementations\nlocal function hydrate_not_setup(api)\n  for k, v in pairs(api) do\n    if type(v) == \"function\" then\n      api[k] = function()\n        require(\"nvim-tree.notify\").error(\"nvim-tree setup not called\")\n      end\n    elseif type(v) == \"table\" and not getmetatable(v) then\n      hydrate_not_setup(v)\n    end\n  end\nend\n\n---Returns the node under the cursor.\n---@return Node?\nlocal function node_at_cursor()\n  local e = require(\"nvim-tree.core\").get_explorer()\n  return e and e:get_node_at_cursor() or nil\nend\n\n---Returns the visually selected nodes, if we are in visual mode.\n---@return Node[]?\nlocal function visual_nodes()\n  local utils = require(\"nvim-tree.utils\")\n  return utils.is_visual_mode() and utils.get_visual_nodes() or nil\nend\n\n---Injects:\n---- n: n or node at cursor\n---@param fn fun(n?: Node, ...): any\n---@return fun(n?: Node, ...): any\nlocal function _n(fn)\n  return function(n, ...)\n    return fn(n or node_at_cursor(), ...)\n  end\nend\n\n---Injects:\n---- n: visual nodes or n or node at cursor\n---@param fn fun(n?: Node|Node[], ...): any\n---@return fun(n?: Node|Node[], ...): any\nlocal function _v(fn)\n  return function(n, ...)\n    return fn(visual_nodes() or n or node_at_cursor(), ...)\n  end\nend\n\n---Injects:\n---- e: Explorer instance\n---Does nothing when no explorer instance.\n---@param fn fun(e: Explorer, ...): any\n---@return fun(e: Explorer, ...): any\nlocal function e_(fn)\n  return function(...)\n    local e = require(\"nvim-tree.core\").get_explorer()\n    if e then\n      return fn(e, ...)\n    end\n  end\nend\n\n---Injects:\n---- e: Explorer instance\n---- n: n or node at cursor\n---Does nothing when no explorer instance.\n---@param fn fun(e: Explorer, n?: Node, ...): any\n---@return fun(e: Explorer, n?: Node, ...): any\nlocal function en(fn)\n  return function(n, ...)\n    local e = require(\"nvim-tree.core\").get_explorer()\n    if e then\n      return fn(e, n or node_at_cursor(), ...)\n    end\n  end\nend\n\n---Injects:\n---- e: Explorer instance\n---- n: visual nodes or n or node at cursor\n---Does nothing when no explorer instance.\n---@param fn fun(e: Explorer, n?: Node|Node[], ...): any\n---@return fun(e: Explorer, n?: Node|Node[], ...): any\nlocal function ev(fn)\n  return function(n, ...)\n    local e = require(\"nvim-tree.core\").get_explorer()\n    if e then\n      return fn(e, visual_nodes() or n or node_at_cursor(), ...)\n    end\n  end\nend\n\n---NOP function wrapper, exists for formatting purposes only.\n---@param fn fun(...): any\n---@return fun(...): any\nlocal function __(fn)\n  return function(...)\n    return fn(...)\n  end\nend\n\n---Hydrates API meta functions pre-setup:\n--- Pre-setup functions will be hydrated with their implementation.\n--- Post-setup functions will notify error: \"nvim-tree setup not called\"\n--- All classes will be hydrated with their implementations.\n---Called once when api is first required\n---@param api table not properly typed to prevent LSP from referencing implementations\nfunction M.hydrate_pre_setup(api)\n  hydrate_not_setup(api)\n\n  api.appearance.hi_test    = __(function() require(\"nvim-tree.appearance.hi-test\")() end)\n\n  api.commands.get          = __(function() return require(\"nvim-tree.commands\").get() end)\n\n  api.config.default        = __(function() return require(\"nvim-tree.config\").d_clone() end)\n\n  api.events.subscribe      = __(function(event_name, handler) require(\"nvim-tree.events\").subscribe(event_name, handler) end)\n\n  api.map.keymap.default    = __(function() return require(\"nvim-tree.keymap\").get_keymap_default() end)\n  api.map.on_attach.default = __(function(bufnr) require(\"nvim-tree.keymap\").on_attach_default(bufnr) end)\n\n  api.Decorator             = require(\"nvim-tree.renderer.decorator\")\n\n  -- Map any legacy functions to implementations above or to meta\n  require(\"nvim-tree.legacy\").map_api(api)\nend\n\n---Re-hydrates all API functions with implementations, replacing any \"nvim-tree setup not called\" error functions.\n---Called explicitly after nvim-tree setup\n---@param api table not properly typed to prevent LSP from referencing implementations\nfunction M.hydrate_post_setup(api)\n  api.config.global                            = __(function() return require(\"nvim-tree.config\").g_clone() end)\n  api.config.user                              = __(function() return require(\"nvim-tree.config\").u_clone() end)\n\n  api.filter.custom.toggle                     = e_(function(e) e.filters:toggle(\"custom\") end)\n  api.filter.dotfiles.toggle                   = e_(function(e) e.filters:toggle(\"dotfiles\") end)\n  api.filter.git.clean.toggle                  = e_(function(e) e.filters:toggle(\"git_clean\") end)\n  api.filter.git.ignored.toggle                = e_(function(e) e.filters:toggle(\"git_ignored\") end)\n  api.filter.live.clear                        = e_(function(e) e.live_filter:clear_filter() end)\n  api.filter.live.start                        = e_(function(e) e.live_filter:start_filtering() end)\n  api.filter.no_bookmark.toggle                = e_(function(e) e.filters:toggle(\"no_bookmark\") end)\n  api.filter.no_buffer.toggle                  = e_(function(e) e.filters:toggle(\"no_buffer\") end)\n  api.filter.toggle                            = e_(function(e) e.filters:toggle() end)\n\n  api.fs.clear_clipboard                       = e_(function(e) e.clipboard:clear_clipboard() end)\n  api.fs.copy.absolute_path                    = en(function(e, n) e.clipboard:copy_absolute_path(n) end)\n  api.fs.copy.basename                         = en(function(e, n) e.clipboard:copy_basename(n) end)\n  api.fs.copy.filename                         = en(function(e, n) e.clipboard:copy_filename(n) end)\n  api.fs.copy.node                             = ev(function(e, n) e.clipboard:copy(n) end)\n  api.fs.copy.relative_path                    = en(function(e, n) e.clipboard:copy_path(n) end)\n  api.fs.create                                = _n(function(n) require(\"nvim-tree.actions\").fs.create_file.fn(n) end)\n  api.fs.cut                                   = ev(function(e, n) e.clipboard:cut(n) end)\n  api.fs.paste                                 = en(function(e, n) e.clipboard:paste(n) end)\n  api.fs.print_clipboard                       = e_(function(e) e.clipboard:print_clipboard() end)\n  api.fs.remove                                = _v(function(n) require(\"nvim-tree.actions\").fs.remove_file.fn(n) end)\n  api.fs.rename                                = _n(function(n) require(\"nvim-tree.actions\").fs.rename_file.rename_node(n) end)\n  api.fs.rename_basename                       = _n(function(n) require(\"nvim-tree.actions\").fs.rename_file.rename_basename(n) end)\n  api.fs.rename_full                           = _n(function(n) require(\"nvim-tree.actions\").fs.rename_file.rename_full(n) end)\n  api.fs.rename_node                           = _n(function(n) require(\"nvim-tree.actions\").fs.rename_file.rename_node(n) end)\n  api.fs.rename_sub                            = _n(function(n) require(\"nvim-tree.actions\").fs.rename_file.rename_sub(n) end)\n  api.fs.trash                                 = _v(function(n) require(\"nvim-tree.actions\").fs.trash.fn(n) end)\n\n  api.git.reload                               = e_(function(e) e:reload_git() end)\n\n  api.map.keymap.current                       = __(function() return require(\"nvim-tree.keymap\").get_keymap() end)\n\n  api.marks.bulk.delete                        = e_(function(e) e.marks:bulk_delete() end)\n  api.marks.bulk.move                          = e_(function(e) e.marks:bulk_move() end)\n  api.marks.bulk.trash                         = e_(function(e) e.marks:bulk_trash() end)\n  api.marks.clear                              = e_(function(e) e.marks:clear() end)\n  api.marks.get                                = en(function(e, n) return e.marks:get(n) end)\n  api.marks.list                               = e_(function(e) return e.marks:list() end)\n  api.marks.navigate.next                      = e_(function(e) e.marks:navigate_next() end)\n  api.marks.navigate.prev                      = e_(function(e) e.marks:navigate_prev() end)\n  api.marks.navigate.select                    = e_(function(e) e.marks:navigate_select() end)\n  api.marks.toggle                             = ev(function(e, n) e.marks:toggle(n) end)\n\n  api.node.buffer.delete                       = _n(function(n, opts) require(\"nvim-tree.actions\").node.buffer.delete(n, opts) end)\n  api.node.buffer.wipe                         = _n(function(n, opts) require(\"nvim-tree.actions\").node.buffer.wipe(n, opts) end)\n  api.node.collapse                            = _n(function(n, opts) require(\"nvim-tree.actions\").tree.collapse.node(n, opts) end)\n  api.node.expand                              = en(function(e, n, opts) e:expand_node(n, opts) end)\n  api.node.navigate.diagnostics.next           = __(function() require(\"nvim-tree.actions\").moves.item.diagnostics_next() end)\n  api.node.navigate.diagnostics.next_recursive = __(function() require(\"nvim-tree.actions\").moves.item.diagnostics_next_recursive() end)\n  api.node.navigate.diagnostics.prev           = __(function() require(\"nvim-tree.actions\").moves.item.diagnostics_prev() end)\n  api.node.navigate.diagnostics.prev_recursive = __(function() require(\"nvim-tree.actions\").moves.item.diagnostics_prev_recursive() end)\n  api.node.navigate.git.next                   = __(function() require(\"nvim-tree.actions\").moves.item.git_next() end)\n  api.node.navigate.git.next_recursive         = __(function() require(\"nvim-tree.actions\").moves.item.git_next_recursive() end)\n  api.node.navigate.git.next_skip_gitignored   = __(function() require(\"nvim-tree.actions\").moves.item.git_next_skip_gitignored() end)\n  api.node.navigate.git.prev                   = __(function() require(\"nvim-tree.actions\").moves.item.git_prev() end)\n  api.node.navigate.git.prev_recursive         = __(function() require(\"nvim-tree.actions\").moves.item.git_prev_recursive() end)\n  api.node.navigate.git.prev_skip_gitignored   = __(function() require(\"nvim-tree.actions\").moves.item.git_prev_skip_gitignored() end)\n  api.node.navigate.opened.next                = __(function() require(\"nvim-tree.actions\").moves.item.opened_next() end)\n  api.node.navigate.opened.prev                = __(function() require(\"nvim-tree.actions\").moves.item.opened_prev() end)\n  api.node.navigate.parent                     = _n(function(n) require(\"nvim-tree.actions\").moves.parent.move(n) end)\n  api.node.navigate.parent_close               = _n(function(n) require(\"nvim-tree.actions\").moves.parent.move_close(n) end)\n  api.node.navigate.sibling.first              = _n(function(n) require(\"nvim-tree.actions\").moves.sibling.first(n) end)\n  api.node.navigate.sibling.last               = _n(function(n) require(\"nvim-tree.actions\").moves.sibling.last(n) end)\n  api.node.navigate.sibling.next               = _n(function(n) require(\"nvim-tree.actions\").moves.sibling.next(n) end)\n  api.node.navigate.sibling.prev               = _n(function(n) require(\"nvim-tree.actions\").moves.sibling.prev(n) end)\n  api.node.open.drop                           = _n(function(n) require(\"nvim-tree.actions\").node.open_file.drop(n) end)\n  api.node.open.edit                           = _n(function(n) require(\"nvim-tree.actions\").node.open_file.edit(n) end)\n  api.node.open.horizontal                     = _n(function(n) require(\"nvim-tree.actions\").node.open_file.horizontal(n) end)\n  api.node.open.horizontal_no_picker           = _n(function(n) require(\"nvim-tree.actions\").node.open_file.horizontal_no_picker(n) end)\n  api.node.open.no_window_picker               = _n(function(n) require(\"nvim-tree.actions\").node.open_file.no_window_picker(n) end)\n  api.node.open.preview                        = _n(function(n) require(\"nvim-tree.actions\").node.open_file.preview(n) end)\n  api.node.open.preview_no_picker              = _n(function(n) require(\"nvim-tree.actions\").node.open_file.preview_no_picker(n) end)\n  api.node.open.replace_tree_buffer            = _n(function(n) require(\"nvim-tree.actions\").node.open_file.replace_tree_buffer(n) end)\n  api.node.open.tab                            = _n(function(n) require(\"nvim-tree.actions\").node.open_file.tab(n) end)\n  api.node.open.tab_drop                       = _n(function(n) require(\"nvim-tree.actions\").node.open_file.tab_drop(n) end)\n  api.node.open.toggle_group_empty             = _n(function(n) require(\"nvim-tree.actions\").node.open_file.toggle_group_empty(n) end)\n  api.node.open.vertical                       = _n(function(n) require(\"nvim-tree.actions\").node.open_file.vertical(n) end)\n  api.node.open.vertical_no_picker             = _n(function(n) require(\"nvim-tree.actions\").node.open_file.vertical_no_picker(n) end)\n  api.node.run.cmd                             = _n(function(n) require(\"nvim-tree.actions\").node.run_command.run_file_command(n) end)\n  api.node.run.system                          = _n(function(n) require(\"nvim-tree.actions\").node.system_open.fn(n) end)\n  api.node.show_info_popup                     = _n(function(n) require(\"nvim-tree.actions\").node.file_popup.toggle_file_info(n) end)\n\n  api.tree.change_root                         = __(function(path) require(\"nvim-tree.actions\").tree.change_dir.fn(path) end)\n  api.tree.change_root_to_node                 = en(function(e, n) e:change_dir_to_node(n) end)\n  api.tree.change_root_to_parent               = en(function(e, n) e:dir_up(n) end)\n  api.tree.close                               = __(function() require(\"nvim-tree.view\").close() end)\n  api.tree.close_in_all_tabs                   = __(function() require(\"nvim-tree.view\").close_all_tabs() end)\n  api.tree.close_in_this_tab                   = __(function() require(\"nvim-tree.view\").close_this_tab_only() end)\n  api.tree.collapse_all                        = __(function(opts) require(\"nvim-tree.actions\").tree.collapse.all(opts) end)\n  api.tree.expand_all                          = en(function(e, n, opts) e:expand_all(n, opts) end)\n  api.tree.find_file                           = __(function(opts) require(\"nvim-tree.actions\").tree.find_file.fn(opts) end)\n  api.tree.focus                               = __(function(opts) require(\"nvim-tree.actions\").tree.open.fn(opts) end)\n  api.tree.get_node_under_cursor               = en(function(e) return e:get_node_at_cursor() end)\n  api.tree.get_nodes                           = en(function(e) return e:get_nodes() end)\n  api.tree.is_tree_buf                         = __(function(bufnr) return require(\"nvim-tree.utils\").is_nvim_tree_buf(bufnr) end)\n  api.tree.is_visible                          = __(function(opts) return require(\"nvim-tree.view\").is_visible(opts) end)\n  api.tree.open                                = __(function(opts) require(\"nvim-tree.actions\").tree.open.fn(opts) end)\n  api.tree.reload                              = e_(function(e) e:reload_explorer() end)\n  api.tree.reload_git                          = e_(function(e) e:reload_git() end)\n  api.tree.resize                              = __(function(opts) require(\"nvim-tree.actions\").tree.resize.fn(opts) end)\n  api.tree.search_node                         = __(function() require(\"nvim-tree.actions\").finders.search_node.fn() end)\n  api.tree.toggle                              = __(function(opts) require(\"nvim-tree.actions\").tree.toggle.fn(opts) end)\n  api.tree.toggle_help                         = __(function() require(\"nvim-tree.help\").toggle() end)\n  api.tree.winid                               = __(function(opts) return require(\"nvim-tree.view\").winid(opts) end)\n\n  -- Map all legacy functions to implementations\n  require(\"nvim-tree.legacy\").map_api(api)\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/api.lua",
    "content": "---@brief\n---nvim-tree exposes a public API. This is non breaking, with additions made as necessary.\n---\n---Please do not require or use modules other than `nvim-tree.api`, as internal modules will change without notice.\n---\n---The API is separated into multiple modules:\n---\n---- [nvim-tree-api-appearance]\n---- [nvim-tree-api-commands]\n---- [nvim-tree-api-events]\n---- [nvim-tree-api-filter]\n---- [nvim-tree-api-fs]\n---- [nvim-tree-api-git]\n---- [nvim-tree-api-map]\n---- [nvim-tree-api-marks]\n---- [nvim-tree-api-node]\n---- [nvim-tree-api-tree]\n---\n---Modules are accessed via `api.<module>.<function>`\n---\n---Example invocation of the `reload` function in the `tree` module:\n---```lua\n---\n---local api = require(\"nvim-tree.api\")\n---api.tree.reload()\n---```\n---Generally, functions accepting a [nvim_tree.api.Node] as their first argument will use the node under the cursor when that argument is not present or nil. Some functions are mode-dependent: when invoked in visual mode they will operate on all nodes in the visual selection instead of a single node. See |nvim-tree-mappings-default| for which mappings support visual mode.\n---\n---e.g. the following are functionally identical:\n---```lua\n---\n---api.node.open.edit(nil, { focus = true })\n---\n---api.node.open.edit(api.tree.get_node_under_cursor(), { focus = true })\n---```\n\n\n\n---The Node class is a data class. Instances may be provided by API functions for use as a:\n---- handle to pass back to API functions e.g. [nvim_tree.api.node.run.cmd()]\n---- reference in callbacks e.g. [nvim_tree.config.sort.Sorter] {sorter}\n---\n---Please do not mutate the contents of any Node object.\n---\n---@class nvim_tree.api.Node\n---@field absolute_path string of the file or directory\n---@field name string file or directory name\n---@field parent? nvim_tree.api.DirectoryNode parent directory, nil for root\n---@field type \"file\" | \"directory\" | \"link\" [uv.fs_stat()] {type}\n---@field executable boolean file is executable\n---@field fs_stat? uv.fs_stat.result at time of last tree display, see [uv.fs_stat()]\n---@field git_status nvim_tree.git.Status? for files and directories\n---@field diag_severity? lsp.DiagnosticSeverity diagnostic status\n---@field hidden boolean node is not visible in the tree\n\n\n\n---\n---Git statuses for a single node.\n---\n---`nvim_tree.git.XY`: 2 character string, see `man 1 git-status` \"Short Format\"\n---@alias nvim_tree.git.XY string\n---\n---{dir} status is derived from its contents:\n---- `direct`: inherited from child files\n---- `indirect`: inherited from child directories\n---\n---@class nvim_tree.git.Status\n---@field file? nvim_tree.git.XY status of a file node\n---@field dir? table<\"direct\" | \"indirect\", nvim_tree.git.XY[]> direct inclusive-or indirect status\n\n\n\n---\n---nvim-tree Public API\n---\n---@class nvim_tree.api\n---@nodoc\nlocal api = {\n  appearance = require(\"nvim-tree._meta.api.appearance\"),\n  commands = require(\"nvim-tree._meta.api.commands\"),\n  config = require(\"nvim-tree._meta.api.config\"),\n  events = require(\"nvim-tree._meta.api.events\"),\n  filter = require(\"nvim-tree._meta.api.filter\"),\n  fs = require(\"nvim-tree._meta.api.fs\"),\n  git = require(\"nvim-tree._meta.api.git\"),\n  map = require(\"nvim-tree._meta.api.map\"),\n  marks = require(\"nvim-tree._meta.api.marks\"),\n  node = require(\"nvim-tree._meta.api.node\"),\n  tree = require(\"nvim-tree._meta.api.tree\"),\n\n  Decorator = require(\"nvim-tree._meta.api.decorator\"),\n\n  decorator = require(\"nvim-tree._meta.api.deprecated\").decorator, ---@deprecated\n  diagnostics = require(\"nvim-tree._meta.api.deprecated\").diagnostics, ---@deprecated\n  live_filter = require(\"nvim-tree._meta.api.deprecated\").live_filter, ---@deprecated\n}\n\nrequire(\"nvim-tree.api.impl\").hydrate_pre_setup(api)\n\nreturn api\n"
  },
  {
    "path": "lua/nvim-tree/appearance/hi-test.lua",
    "content": "local appearance = require(\"nvim-tree.appearance\")\n\nlocal Class = require(\"nvim-tree.classic\")\n\n-- others with name and links less than this arbitrary value are short\nlocal SHORT_LEN = 50\n\nlocal namespace_hi_test_id = vim.api.nvim_create_namespace(\"NvimTreeHiTest\")\n\n---For :NvimTreeHiTest\n---@class (exact) HighlightDisplay: nvim_tree.Class\n---@field group string nvim-tree highlight group name\n---@field links string link chain to a concretely defined group\n---@field def string :hi concrete definition after following any links\nlocal HighlightDisplay = Class:extend()\n\n---@class HighlightDisplay\n---@overload fun(args: HighlightDisplayArgs): HighlightDisplay\n\n---@class (exact) HighlightDisplayArgs\n---@field group string nvim-tree highlight group name\n\n---@protected\n---@param args HighlightDisplayArgs\nfunction HighlightDisplay:new(args)\n  self.group = args.group\n\n  local concrete = self.group\n\n  -- maybe follow links\n  local links = {}\n  local link = vim.api.nvim_get_hl(0, { name = self.group }).link\n  while link do\n    table.insert(links, link)\n    concrete = link\n    link = vim.api.nvim_get_hl(0, { name = link }).link\n  end\n  self.links = table.concat(links, \" \")\n\n  -- concrete definition\n  local ok, res = pcall(vim.api.nvim_cmd, { cmd = \"highlight\", args = { concrete } }, { output = true })\n  if ok and type(res) == \"string\" then\n    self.def = res:gsub(\".*xxx *\", \"\")\n  else\n    self.def = \"\"\n  end\nend\n\n---Render one group.\n---@param bufnr number to render in\n---@param fmt string format string for group, links, def\n---@param l number line number to render at\n---@return number l next line number\nfunction HighlightDisplay:render(bufnr, fmt, l)\n  local text = string.format(fmt, self.group, self.links, self.def)\n\n  vim.api.nvim_buf_set_lines(bufnr, l, -1, true, { text })\n\n  if vim.fn.has(\"nvim-0.11\") == 1 and vim.hl and vim.hl.range then\n    vim.hl.range(bufnr, namespace_hi_test_id, self.group, { l, 0 }, { l, #self.group, }, {})\n  else\n    vim.api.nvim_buf_add_highlight(bufnr, -1, self.group, l, 0, #self.group) ---@diagnostic disable-line: deprecated\n  end\n\n  return l + 1\nend\n\n---Render many groups.\n---@param header string before with underline line\n---@param displays HighlightDisplay[] highlight group\n---@param bufnr number to render in\n---@param l number line number to start at\n---@return number l next line number\nlocal function render_displays(header, displays, bufnr, l)\n  local max_group_len = 0\n  local max_links_len = 0\n\n  -- build all highlight groups, using name only\n  for _, display in ipairs(displays) do\n    max_group_len = math.max(max_group_len, #display.group)\n    max_links_len = math.max(max_links_len, #display.links)\n  end\n\n  -- header\n  vim.api.nvim_buf_set_lines(bufnr, l, -1, true, { header, (header:gsub(\".\", \"-\")) })\n  l = l + 2\n\n  -- render and highlight\n  local fmt = string.format(\"%%-%d.%ds %%-%d.%ds %%s\", max_group_len, max_group_len, max_links_len, max_links_len)\n  for _, display in ipairs(displays) do\n    l = display:render(bufnr, fmt, l)\n  end\n\n  return l\nend\n\n---Run a test similar to :so $VIMRUNTIME/syntax/hitest.vim\n---Display all nvim-tree and neovim highlight groups, their link chain and actual definition\nreturn function()\n  -- create a buffer\n  local bufnr = vim.api.nvim_create_buf(false, true)\n\n  local l = 0\n\n  -- nvim-tree groups, ordered\n  local displays = {}\n  for _, highlight_group in ipairs(appearance.HIGHLIGHT_GROUPS) do\n    local display = HighlightDisplay({ group = highlight_group.group })\n    table.insert(displays, display)\n  end\n  l = render_displays(\"nvim-tree\", displays, bufnr, l)\n\n  vim.api.nvim_buf_set_lines(bufnr, l, -1, true, { \"\" })\n  l = l + 1\n\n  -- built in groups, ordered opaquely by nvim\n  local displays_short, displays_long = {}, {}\n  local ok, out = pcall(vim.api.nvim_cmd, { cmd = \"highlight\" }, { output = true })\n  if ok then\n    for group in string.gmatch(out, \"(%w*)%s+xxx\") do\n      if group:find(\"NvimTree\", 1, true) ~= 1 then\n        local display = HighlightDisplay({ group = group })\n        if #display.group + #display.links > SHORT_LEN then\n          table.insert(displays_long, display)\n        else\n          table.insert(displays_short, display)\n        end\n      end\n    end\n  end\n\n  -- short ones first\n  l = render_displays(\"other, short\", displays_short, bufnr, l)\n  vim.api.nvim_buf_set_lines(bufnr, l, -1, true, { \"\" })\n  l = l + 1\n\n  -- long\n  render_displays(\"other, long\", displays_long, bufnr, l)\n\n  -- finalise and focus the buffer\n  if vim.fn.has(\"nvim-0.10\") == 1 then\n    vim.api.nvim_set_option_value(\"modifiable\", false, { buf = bufnr })\n  else\n    vim.api.nvim_buf_set_option(bufnr, \"modifiable\", false) ---@diagnostic disable-line: deprecated\n  end\n\n  vim.cmd.buffer(bufnr)\nend\n"
  },
  {
    "path": "lua/nvim-tree/appearance/init.lua",
    "content": "local M = {}\n\n---@class HighlightGroup\n---@field group string\n---@field link string|nil\n---@field def string|nil\n\n---@type HighlightGroup[]\n-- All highlight groups: linked or directly defined.\n-- Please add new groups to help and preserve order.\n-- Please avoid directly defined groups to preserve accessibility for TUI.\nM.HIGHLIGHT_GROUPS = {\n\n  -- Standard\n  { group = \"NvimTreeNormal\",                  link = \"Normal\" },\n  { group = \"NvimTreeNormalFloat\",             link = \"NormalFloat\" },\n  { group = \"NvimTreeNormalFloatBorder\",       link = \"FloatBorder\" },\n  { group = \"NvimTreeNormalNC\",                link = \"NvimTreeNormal\" },\n\n  { group = \"NvimTreeLineNr\",                  link = \"LineNr\" },\n  { group = \"NvimTreeWinSeparator\",            link = \"WinSeparator\" },\n  { group = \"NvimTreeEndOfBuffer\",             link = \"EndOfBuffer\" },\n  { group = \"NvimTreePopup\",                   link = \"Normal\" },\n  { group = \"NvimTreeSignColumn\",              link = \"NvimTreeNormal\" },\n\n  { group = \"NvimTreeCursorColumn\",            link = \"CursorColumn\" },\n  { group = \"NvimTreeCursorLine\",              link = \"CursorLine\" },\n  { group = \"NvimTreeCursorLineNr\",            link = \"CursorLineNr\" },\n\n  { group = \"NvimTreeStatusLine\",              link = \"StatusLine\" },\n  { group = \"NvimTreeStatusLineNC\",            link = \"StatusLineNC\" },\n\n  -- File Text\n  { group = \"NvimTreeExecFile\",                link = \"Question\" },\n  { group = \"NvimTreeImageFile\",               link = \"Question\" },\n  { group = \"NvimTreeSpecialFile\",             link = \"Title\" },\n  { group = \"NvimTreeSymlink\",                 link = \"Underlined\" },\n\n  -- Folder Text\n  { group = \"NvimTreeRootFolder\",              link = \"Title\" },\n  { group = \"NvimTreeFolderName\",              link = \"Directory\" },\n  { group = \"NvimTreeEmptyFolderName\",         link = \"Directory\" },\n  { group = \"NvimTreeOpenedFolderName\",        link = \"Directory\" },\n  { group = \"NvimTreeSymlinkFolderName\",       link = \"Directory\" },\n\n  -- File Icons\n  { group = \"NvimTreeFileIcon\",                link = \"NvimTreeNormal\" },\n  { group = \"NvimTreeSymlinkIcon\",             link = \"NvimTreeNormal\" },\n\n  -- Folder Icons\n  { group = \"NvimTreeFolderIcon\",              def = \"guifg=#8094b4 ctermfg=Blue\" },\n  { group = \"NvimTreeOpenedFolderIcon\",        link = \"NvimTreeFolderIcon\" },\n  { group = \"NvimTreeClosedFolderIcon\",        link = \"NvimTreeFolderIcon\" },\n  { group = \"NvimTreeFolderArrowClosed\",       link = \"NvimTreeIndentMarker\" },\n  { group = \"NvimTreeFolderArrowOpen\",         link = \"NvimTreeIndentMarker\" },\n\n  -- Indent\n  { group = \"NvimTreeIndentMarker\",            link = \"NvimTreeFolderIcon\" },\n\n  -- Picker\n  { group = \"NvimTreeWindowPicker\",            def = \"guifg=#ededed guibg=#4493c8 gui=bold ctermfg=White ctermbg=DarkBlue\" },\n\n  -- LiveFilter\n  { group = \"NvimTreeLiveFilterPrefix\",        link = \"PreProc\" },\n  { group = \"NvimTreeLiveFilterValue\",         link = \"ModeMsg\" },\n\n  -- Clipboard\n  { group = \"NvimTreeCutHL\",                   link = \"SpellBad\" },\n  { group = \"NvimTreeCopiedHL\",                link = \"SpellRare\" },\n\n  -- Bookmark\n  { group = \"NvimTreeBookmarkIcon\",            link = \"NvimTreeFolderIcon\" },\n  { group = \"NvimTreeBookmarkHL\",              link = \"SpellLocal\" },\n\n  -- Modified\n  { group = \"NvimTreeModifiedIcon\",            link = \"Type\" },\n  { group = \"NvimTreeModifiedFileHL\",          link = \"NvimTreeModifiedIcon\" },\n  { group = \"NvimTreeModifiedFolderHL\",        link = \"NvimTreeModifiedFileHL\" },\n\n  -- Hidden\n  { group = \"NvimTreeHiddenIcon\",              link = \"Conceal\" },\n  { group = \"NvimTreeHiddenFileHL\",            link = \"NvimTreeHiddenIcon\" },\n  { group = \"NvimTreeHiddenFolderHL\",          link = \"NvimTreeHiddenFileHL\" },\n\n  -- Hidden Display\n  { group = \"NvimTreeHiddenDisplay\",           link = \"Conceal\" },\n\n  -- Opened\n  { group = \"NvimTreeOpenedHL\",                link = \"Special\" },\n\n  -- Git Icon\n  { group = \"NvimTreeGitDeletedIcon\",          link = \"Statement\" },\n  { group = \"NvimTreeGitDirtyIcon\",            link = \"Statement\" },\n  { group = \"NvimTreeGitIgnoredIcon\",          link = \"Comment\" },\n  { group = \"NvimTreeGitMergeIcon\",            link = \"Constant\" },\n  { group = \"NvimTreeGitNewIcon\",              link = \"PreProc\" },\n  { group = \"NvimTreeGitRenamedIcon\",          link = \"PreProc\" },\n  { group = \"NvimTreeGitStagedIcon\",           link = \"Constant\" },\n\n  -- Git File Highlight\n  { group = \"NvimTreeGitFileDeletedHL\",        link = \"NvimTreeGitDeletedIcon\" },\n  { group = \"NvimTreeGitFileDirtyHL\",          link = \"NvimTreeGitDirtyIcon\" },\n  { group = \"NvimTreeGitFileIgnoredHL\",        link = \"NvimTreeGitIgnoredIcon\" },\n  { group = \"NvimTreeGitFileMergeHL\",          link = \"NvimTreeGitMergeIcon\" },\n  { group = \"NvimTreeGitFileNewHL\",            link = \"NvimTreeGitNewIcon\" },\n  { group = \"NvimTreeGitFileRenamedHL\",        link = \"NvimTreeGitRenamedIcon\" },\n  { group = \"NvimTreeGitFileStagedHL\",         link = \"NvimTreeGitStagedIcon\" },\n\n  -- Git Folder Highlight\n  { group = \"NvimTreeGitFolderDeletedHL\",      link = \"NvimTreeGitFileDeletedHL\" },\n  { group = \"NvimTreeGitFolderDirtyHL\",        link = \"NvimTreeGitFileDirtyHL\" },\n  { group = \"NvimTreeGitFolderIgnoredHL\",      link = \"NvimTreeGitFileIgnoredHL\" },\n  { group = \"NvimTreeGitFolderMergeHL\",        link = \"NvimTreeGitFileMergeHL\" },\n  { group = \"NvimTreeGitFolderNewHL\",          link = \"NvimTreeGitFileNewHL\" },\n  { group = \"NvimTreeGitFolderRenamedHL\",      link = \"NvimTreeGitFileRenamedHL\" },\n  { group = \"NvimTreeGitFolderStagedHL\",       link = \"NvimTreeGitFileStagedHL\" },\n\n  -- Diagnostics Icon\n  { group = \"NvimTreeDiagnosticErrorIcon\",     link = \"DiagnosticError\" },\n  { group = \"NvimTreeDiagnosticWarnIcon\",      link = \"DiagnosticWarn\" },\n  { group = \"NvimTreeDiagnosticInfoIcon\",      link = \"DiagnosticInfo\" },\n  { group = \"NvimTreeDiagnosticHintIcon\",      link = \"DiagnosticHint\" },\n\n  -- Diagnostics File Highlight\n  { group = \"NvimTreeDiagnosticErrorFileHL\",   link = \"DiagnosticUnderlineError\" },\n  { group = \"NvimTreeDiagnosticWarnFileHL\",    link = \"DiagnosticUnderlineWarn\" },\n  { group = \"NvimTreeDiagnosticInfoFileHL\",    link = \"DiagnosticUnderlineInfo\" },\n  { group = \"NvimTreeDiagnosticHintFileHL\",    link = \"DiagnosticUnderlineHint\" },\n\n  -- Diagnostics Folder Highlight\n  { group = \"NvimTreeDiagnosticErrorFolderHL\", link = \"NvimTreeDiagnosticErrorFileHL\" },\n  { group = \"NvimTreeDiagnosticWarnFolderHL\",  link = \"NvimTreeDiagnosticWarnFileHL\" },\n  { group = \"NvimTreeDiagnosticInfoFolderHL\",  link = \"NvimTreeDiagnosticInfoFileHL\" },\n  { group = \"NvimTreeDiagnosticHintFolderHL\",  link = \"NvimTreeDiagnosticHintFileHL\" },\n}\n\n-- nvim-tree highlight groups to legacy\nM.LEGACY_LINKS = {\n  NvimTreeModifiedIcon = \"NvimTreeModifiedFile\",\n\n  NvimTreeOpenedHL = \"NvimTreeOpenedFile\",\n\n  NvimTreeBookmarkIcon = \"NvimTreeBookmark\",\n\n  NvimTreeGitDeletedIcon = \"NvimTreeGitDeleted\",\n  NvimTreeGitDirtyIcon = \"NvimTreeGitDirty\",\n  NvimTreeGitIgnoredIcon = \"NvimTreeGitIgnored\",\n  NvimTreeGitMergeIcon = \"NvimTreeGitMerge\",\n  NvimTreeGitNewIcon = \"NvimTreeGitNew\",\n  NvimTreeGitRenamedIcon = \"NvimTreeGitRenamed\",\n  NvimTreeGitStagedIcon = \"NvimTreeGitStaged\",\n\n  NvimTreeGitFileDeletedHL = \"NvimTreeFileDeleted\",\n  NvimTreeGitFileDirtyHL = \"NvimTreeFileDirty\",\n  NvimTreeGitFileIgnoredHL = \"NvimTreeFileIgnored\",\n  NvimTreeGitFileMergeHL = \"NvimTreeFileMerge\",\n  NvimTreeGitFileNewHL = \"NvimTreeFileNew\",\n  NvimTreeGitFileRenamedHL = \"NvimTreeFileRenamed\",\n  NvimTreeGitFileStagedHL = \"NvimTreeFileStaged\",\n\n  NvimTreeGitFolderDeletedHL = \"NvimTreeFolderDeleted\",\n  NvimTreeGitFolderDirtyHL = \"NvimTreeFolderDirty\",\n  NvimTreeGitFolderIgnoredHL = \"NvimTreeFolderIgnored\",\n  NvimTreeGitFolderMergeHL = \"NvimTreeFolderMerge\",\n  NvimTreeGitFolderNewHL = \"NvimTreeFolderNew\",\n  NvimTreeGitFolderRenamedHL = \"NvimTreeFolderRenamed\",\n  NvimTreeGitFolderStagedHL = \"NvimTreeFolderStaged\",\n\n  NvimTreeDiagnosticErrorIcon = \"NvimTreeLspDiagnosticsError\",\n  NvimTreeDiagnosticWarnIcon = \"NvimTreeLspDiagnosticsWarning\",\n  NvimTreeDiagnosticInfoIcon = \"NvimTreeLspDiagnosticsInformation\",\n  NvimTreeDiagnosticHintIcon = \"NvimTreeLspDiagnosticsHint\",\n\n  NvimTreeDiagnosticErrorFileHL = \"NvimTreeLspDiagnosticsErrorText\",\n  NvimTreeDiagnosticWarnFileHL = \"NvimTreeLspDiagnosticsWarningText\",\n  NvimTreeDiagnosticInfoFileHL = \"NvimTreeLspDiagnosticsInformationText\",\n  NvimTreeDiagnosticHintFileHL = \"NvimTreeLspDiagnosticsHintText\",\n\n  NvimTreeDiagnosticErrorFolderHL = \"NvimTreeLspDiagnosticsErrorFolderText\",\n  NvimTreeDiagnosticWarnFolderHL = \"NvimTreeLspDiagnosticsWarningFolderText\",\n  NvimTreeDiagnosticInfoFolderHL = \"NvimTreeLspDiagnosticsInformationFolderText\",\n  NvimTreeDiagnosticHintFolderHL = \"NvimTreeLspDiagnosticsHintFolderText\",\n}\n\nfunction M.setup()\n  -- non-linked\n  for _, g in ipairs(M.HIGHLIGHT_GROUPS) do\n    if g.def then\n      vim.api.nvim_command(\"hi def \" .. g.group .. \" \" .. g.def)\n    end\n  end\n\n  -- hard link override when legacy only is present\n  for from, to in pairs(M.LEGACY_LINKS) do\n    local hl_from = vim.api.nvim_get_hl(0, { name = from })\n    local hl_to = vim.api.nvim_get_hl(0, { name = to })\n    if vim.tbl_isempty(hl_from) and not vim.tbl_isempty(hl_to) then\n      vim.api.nvim_command(\"hi link \" .. from .. \" \" .. to)\n    end\n  end\n\n  -- default links\n  for _, g in ipairs(M.HIGHLIGHT_GROUPS) do\n    if g.link then\n      vim.api.nvim_command(\"hi def link \" .. g.group .. \" \" .. g.link)\n    end\n  end\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/buffers.lua",
    "content": "local DirectoryNode = require(\"nvim-tree.node.directory\")\n\nlocal M = {}\n\n---@type table<string, boolean> record of which file is modified\nM._modified = {}\n\n---refresh M._modified\nfunction M.reload_modified()\n  M._modified = {}\n  local bufs = vim.fn.getbufinfo({ bufmodified = 1, buflisted = 1 })\n  for _, buf in pairs(bufs) do\n    local path = buf.name\n    if path ~= \"\" then -- not a [No Name] buffer\n      -- mark all the parent as modified as well\n      while M._modified[path] ~= true do\n        -- no need to keep going if already recorded\n        -- This also prevents an infinite loop\n        M._modified[path] = true\n        path = vim.fn.fnamemodify(path, \":h\")\n      end\n    end\n  end\nend\n\n---@param node Node\n---@return boolean\nfunction M.is_modified(node)\n  if not M.config.modified.enable then\n    return false\n  end\n\n  if not M._modified[node.absolute_path] then\n    return false\n  end\n\n  local dir = node:as(DirectoryNode)\n  if dir then\n    if not M.config.modified.show_on_dirs then\n      return false\n    end\n\n    if dir.open and not M.config.modified.show_on_open_dirs then\n      return false\n    end\n  end\n\n  return true\nend\n\n---A buffer exists for the node's absolute path\n---@param node Node\n---@return boolean\nfunction M.is_opened(node)\n  return node and vim.fn.bufloaded(node.absolute_path) > 0\nend\n\n---@param opts nvim_tree.config\nfunction M.setup(opts)\n  M.config = {\n    modified = opts.modified,\n  }\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/classic.lua",
    "content": "--\n-- classic\n--\n-- Copyright (c) 2014, rxi\n--\n-- This module is free software; you can redistribute it and/or modify it under\n-- the terms of the MIT license. See LICENSE for details.\n--\n-- https://github.com/rxi/classic\n--\n\n\n---@brief\n---\n---nvim-tree uses the https://github.com/rxi/classic class framework adding safe casts, instanceof mixin and conventional destructors.\n---\n---The key differences between classic and ordinary Lua classes:\n---- The constructor [nvim_tree.Class:new()] is not responsible for allocation: `self` is available when the constructor is called.\n---- Instances are constructed via the `__call` meta method: `SomeClass(args)`\n---\n---Classes are conventionally named using camel case e.g. `MyClass`\n---\n---Classes are created by extending another class:\n---```lua\n---\n--- local Class = require(\"nvim-tree.classic\")\n---\n--- ---@class (exact) Fruit: nvim_tree.Class\n--- ---@field ...\n--- local Fruit = Class:extend()\n---\n--- ---@class (exact) Apple: Fruit\n--- ---@field ...\n--- local Apple = Fruit:extend()\n---```\n---\n---Implementing a constructor [nvim_tree.Class:new()] is optional, however it must call the `super` constructor:\n---```lua\n---\n--- ---@protected\n--- ---@param args AppleArgs\n--- function Apple:new(args)\n---\n---   ---@type FruitArgs\n---   local fruit_args = ...\n---\n---   Apple.super.new(self, fruit_args)\n---   ---\n---```\n---\n---Create an instance of a class using the `__call` meta method that will invoke the constructor:\n---```lua\n---\n--- ---@type AppleArgs\n--- local args = ...\n---\n--- local an_apple = Apple(args)\n--- -- above will call `Apple:new(args)`\n---```\n---\n---In order to strongly type instantiation, the following pattern is used to type the meta method `__call` with arguments and return:\n---```lua\n---\n--- ---@class (exact) Fruit: nvim_tree.Class\n--- ---@field ...\n--- local Fruit = Class:extend()\n---\n--- ---@class (exact) FruitArgs\n--- ---@field ...\n---\n--- ---@class Fruit\n--- ---@overload fun(args: FruitArgs): Fruit\n---\n--- ---@protected\n--- ---@param args FruitArgs\n--- function Fruit:new(args)\n---```\n\n---\n---@class nvim_tree.Class\n---\n---Parent class, `Class` for base classes.\n---@field super nvim_tree.Class\n---\n---mixin classes that are implemented.\n---@field private implements table<nvim_tree.Class, boolean>\nlocal Class = {}\nClass.__index = Class ---@diagnostic disable-line: inject-field\n\n---\n---Constructor: `self` has been allocated and is available.\n---\n---Super constructor must be called using the form `Child.super.new(self, parent_args)`\n---\n---@param ... any constructor arguments\nfunction Class:new(...) --luacheck: ignore 212\nend\n\n---\n---Conventional destructor, optional, must be called by the owner.\n---\n---Parent destructor must be invoked using the form `Parent.destroy(self)`\n---\nfunction Class:destroy()\nend\n\n---\n---Create a new class by extending another class.\n---\n---Base classes extend `Class`\n---\n---@return [nvim_tree.Class] child class\nfunction Class:extend()\n  local cls = {}\n  for k, v in pairs(self) do\n    if k:find(\"__\") == 1 then\n      cls[k] = v\n    end\n  end\n  cls.__index = cls\n  cls.super = self\n  setmetatable(cls, self)\n  return cls\nend\n\n---\n---Add the methods and fields of a mixin using the form `SomeClass:implement(MixinClass)`\n---\n---If the mixin has fields, it must implement a constructor.\n---\n---@param mixin nvim_tree.Class\nfunction Class:implement(mixin)\n  if not rawget(self, \"implements\") then\n    -- set on the class itself instead of parents\n    rawset(self, \"implements\", {})\n  end\n  self.implements[mixin] = true\n  for k, v in pairs(mixin) do\n    if self[k] == nil and type(v) == \"function\" then\n      self[k] = v\n    end\n  end\nend\n\n---\n---Instance of.\n---\n---Test whether an object is {class}, inherits {class} or implements mixin {class}.\n---\n---@generic T\n---@param class T `<T>`\n---@return boolean\nfunction Class:is(class)\n  local mt = getmetatable(self)\n  while mt do\n    if mt == class then\n      return true\n    end\n    if mt.implements and mt.implements[class] then\n      return true\n    end\n    mt = getmetatable(mt)\n  end\n  return false\nend\n\n---\n---Type safe cast.\n---\n---If instance [nvim_tree.Class:is()], cast to {class} and return it, otherwise nil.\n---\n---@generic T\n---@param class T `<T>`\n---@return T? `<T>`\nfunction Class:as(class)\n  return self:is(class) and self or nil\nend\n\n---\n---Constructs an instance: calls [nvim_tree.Class:new()] and returns the new instance.\n---\n---@param ... any constructor args\n---@return nvim_tree.Class\nfunction Class:__call(...)\n  local obj = setmetatable({}, self)\n  obj:new(...)\n  return obj\nend\n\n---\n---Utility method to bypass unused param warnings in abstract methods.\n---\n---@param ... any\nfunction Class:nop(...) --luacheck: ignore 212\nend\n\nreturn Class\n"
  },
  {
    "path": "lua/nvim-tree/commands.lua",
    "content": "local M = {}\n\n---@type nvim_tree.api.commands.Command[]\nlocal CMDS = {\n  {\n    name = \"NvimTreeOpen\",\n    opts = {\n      desc = \"nvim-tree: open\",\n      nargs = \"?\",\n      complete = \"dir\",\n    },\n    command = function(c)\n      require(\"nvim-tree.api\").tree.open({ path = c.args })\n    end,\n  },\n  {\n    name = \"NvimTreeClose\",\n    opts = {\n      desc = \"nvim-tree: close\",\n      bar = true,\n    },\n    command = function()\n      require(\"nvim-tree.api\").tree.close()\n    end,\n  },\n  {\n    name = \"NvimTreeToggle\",\n    opts = {\n      desc = \"nvim-tree: toggle\",\n      nargs = \"?\",\n      complete = \"dir\",\n    },\n    command = function(c)\n      require(\"nvim-tree.api\").tree.toggle({\n        find_file = false,\n        focus = true,\n        path = c.args,\n        update_root = false,\n      })\n    end,\n  },\n  {\n    name = \"NvimTreeFocus\",\n    opts = {\n      desc = \"nvim-tree: focus\",\n      bar = true,\n    },\n    command = function()\n      require(\"nvim-tree.api\").tree.open()\n    end,\n  },\n  {\n    name = \"NvimTreeRefresh\",\n    opts = {\n      desc = \"nvim-tree: refresh\",\n      bar = true,\n    },\n    command = function()\n      require(\"nvim-tree.api\").tree.reload()\n    end,\n  },\n  {\n    name = \"NvimTreeClipboard\",\n    opts = {\n      desc = \"nvim-tree: print clipboard\",\n      bar = true,\n    },\n    command = function()\n      require(\"nvim-tree.api\").fs.print_clipboard()\n    end,\n  },\n  {\n    name = \"NvimTreeFindFile\",\n    opts = {\n      desc = \"nvim-tree: find file\",\n      bang = true,\n      bar = true,\n    },\n    command = function(c)\n      require(\"nvim-tree.api\").tree.find_file({\n        open = true,\n        focus = true,\n        update_root = c.bang,\n      })\n    end,\n  },\n  {\n    name = \"NvimTreeFindFileToggle\",\n    opts = {\n      desc = \"nvim-tree: find file, toggle\",\n      bang = true,\n      nargs = \"?\",\n      complete = \"dir\",\n    },\n    command = function(c)\n      require(\"nvim-tree.api\").tree.toggle({\n        find_file = true,\n        focus = true,\n        path = c.args,\n        update_root = c.bang,\n      })\n    end,\n  },\n  {\n    name = \"NvimTreeResize\",\n    opts = {\n      desc = \"nvim-tree: resize\",\n      nargs = 1,\n      bar = true,\n    },\n    command = function(c)\n      local sign = c.args:sub(1, 1)\n      if sign == \"+\" or sign == \"-\" then\n        require(\"nvim-tree.api\").tree.resize({ relative = tonumber(c.args) })\n      else\n        require(\"nvim-tree.api\").tree.resize({ absolute = tonumber(c.args) })\n      end\n    end,\n  },\n  {\n    name = \"NvimTreeCollapse\",\n    opts = {\n      desc = \"nvim-tree: collapse\",\n      bar = true,\n    },\n    command = function()\n      require(\"nvim-tree.api\").tree.collapse_all({ keep_buffers = false })\n    end,\n  },\n  {\n    name = \"NvimTreeCollapseKeepBuffers\",\n    opts = {\n      desc = \"nvim-tree: collapse, keep directories open\",\n      bar = true,\n    },\n    command = function()\n      require(\"nvim-tree.api\").tree.collapse_all({ keep_buffers = true })\n    end,\n  },\n  {\n    name = \"NvimTreeHiTest\",\n    opts = {\n      desc = \"nvim-tree: highlight test\",\n    },\n    command = function()\n      require(\"nvim-tree.api\").appearance.hi_test()\n    end,\n  },\n}\n\n---@return nvim_tree.api.commands.Command[]\nfunction M.get()\n  return vim.deepcopy(CMDS)\nend\n\nfunction M.setup()\n  for _, cmd in ipairs(CMDS) do\n    local opts = vim.tbl_extend(\"force\", cmd.opts, { force = true })\n    vim.api.nvim_create_user_command(cmd.name, cmd.command, opts)\n  end\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/config.lua",
    "content": "--This will be required after api, before setup.\n--This file should have minimal requires that are cheap and have no dependencies or are already required.\n\nlocal notify = require(\"nvim-tree.notify\")\nlocal legacy = require(\"nvim-tree.legacy\")\nlocal utils = require(\"nvim-tree.utils\")\n\n-- short names like g are used rather than getters to keep code brief\n\nlocal M = {\n  ---@type nvim_tree.config immutable default config\n  d = {},\n\n  ---@type nvim_tree.config? global current config, nil until setup called\n  g = nil,\n\n  ---@type nvim_tree.config? raw user config, nil when no user config passed to setup\n  u = nil,\n}\n\nM.d = { -- config-default-start\n  on_attach = \"default\",\n  hijack_cursor = false,\n  auto_reload_on_write = true,\n  disable_netrw = false,\n  hijack_netrw = true,\n  hijack_unnamed_buffer_when_opening = false,\n  root_dirs = {},\n  prefer_startup_root = false,\n  sync_root_with_cwd = false,\n  reload_on_bufenter = false,\n  respect_buf_cwd = false,\n  select_prompts = false,\n  sort = {\n    sorter = \"name\",\n    folders_first = true,\n    files_first = false,\n  },\n  view = {\n    centralize_selection = false,\n    cursorline = true,\n    cursorlineopt = \"both\",\n    debounce_delay = 15,\n    side = \"left\",\n    preserve_window_proportions = false,\n    number = false,\n    relativenumber = false,\n    signcolumn = \"yes\",\n    width = 30,\n    float = {\n      enable = false,\n      quit_on_focus_loss = true,\n      open_win_config = {\n        relative = \"editor\",\n        border = \"rounded\",\n        width = 30,\n        height = 30,\n        row = 1,\n        col = 1,\n      },\n    },\n  },\n  renderer = {\n    add_trailing = false,\n    group_empty = false,\n    full_name = false,\n    root_folder_label = \":~:s?$?/..?\",\n    indent_width = 2,\n    special_files = { \"Cargo.toml\", \"Makefile\", \"README.md\", \"readme.md\" },\n    hidden_display = \"none\",\n    symlink_destination = true,\n    decorators = { \"Git\", \"Open\", \"Hidden\", \"Modified\", \"Bookmark\", \"Diagnostics\", \"Copied\", \"Cut\", },\n    highlight_git = \"none\",\n    highlight_diagnostics = \"none\",\n    highlight_opened_files = \"none\",\n    highlight_modified = \"none\",\n    highlight_hidden = \"none\",\n    highlight_bookmarks = \"none\",\n    highlight_clipboard = \"name\",\n    indent_markers = {\n      enable = false,\n      inline_arrows = true,\n      icons = {\n        corner = \"└\",\n        edge = \"│\",\n        item = \"│\",\n        bottom = \"─\",\n        none = \" \",\n      },\n    },\n    icons = {\n      web_devicons = {\n        file = {\n          enable = true,\n          color = true,\n        },\n        folder = {\n          enable = false,\n          color = true,\n        },\n      },\n      git_placement = \"before\",\n      modified_placement = \"after\",\n      hidden_placement = \"after\",\n      diagnostics_placement = \"signcolumn\",\n      bookmarks_placement = \"signcolumn\",\n      padding = {\n        icon = \" \",\n        folder_arrow = \" \",\n      },\n      symlink_arrow = \" ➛ \",\n      show = {\n        file = true,\n        folder = true,\n        folder_arrow = true,\n        git = true,\n        modified = true,\n        hidden = false,\n        diagnostics = true,\n        bookmarks = true,\n      },\n      glyphs = {\n        default = \"\",\n        symlink = \"\",\n        bookmark = \"󰆤\",\n        modified = \"●\",\n        hidden = \"󰜌\",\n        folder = {\n          arrow_closed = \"\",\n          arrow_open = \"\",\n          default = \"\",\n          open = \"\",\n          empty = \"\",\n          empty_open = \"\",\n          symlink = \"\",\n          symlink_open = \"\",\n        },\n        git = {\n          unstaged = \"✗\",\n          staged = \"✓\",\n          unmerged = \"\",\n          renamed = \"➜\",\n          untracked = \"★\",\n          deleted = \"\",\n          ignored = \"◌\",\n        },\n      },\n    },\n  },\n  hijack_directories = {\n    enable = true,\n    auto_open = true,\n  },\n  update_focused_file = {\n    enable = false,\n    update_root = {\n      enable = false,\n      ignore_list = {},\n    },\n    exclude = false,\n  },\n  system_open = {\n    cmd = \"\",\n    args = {},\n  },\n  git = {\n    enable = true,\n    show_on_dirs = true,\n    show_on_open_dirs = true,\n    disable_for_dirs = {},\n    timeout = 400,\n    cygwin_support = false,\n  },\n  diagnostics = {\n    enable = false,\n    show_on_dirs = false,\n    show_on_open_dirs = true,\n    debounce_delay = 500,\n    severity = {\n      min = vim.diagnostic.severity.HINT,\n      max = vim.diagnostic.severity.ERROR,\n    },\n    icons = {\n      hint = \"\",\n      info = \"\",\n      warning = \"\",\n      error = \"\",\n    },\n    diagnostic_opts = false,\n  },\n  modified = {\n    enable = false,\n    show_on_dirs = true,\n    show_on_open_dirs = true,\n  },\n  filters = {\n    enable = true,\n    git_ignored = true,\n    dotfiles = false,\n    git_clean = false,\n    no_buffer = false,\n    no_bookmark = false,\n    custom = {},\n    exclude = {},\n  },\n  live_filter = {\n    prefix = \"[FILTER]: \",\n    always_show_folders = true,\n  },\n  filesystem_watchers = {\n    enable = true,\n    debounce_delay = 50,\n    max_events = 0,\n    ignore_dirs = {\n      \"/.ccls-cache\",\n      \"/build\",\n      \"/node_modules\",\n      \"/target\",\n      \"/.zig-cache\",\n    },\n  },\n  actions = {\n    use_system_clipboard = true,\n    change_dir = {\n      enable = true,\n      global = false,\n      restrict_above_cwd = false,\n    },\n    expand_all = {\n      max_folder_discovery = 300,\n      exclude = {},\n    },\n    file_popup = {\n      open_win_config = {\n        col = 1,\n        row = 1,\n        relative = \"cursor\",\n        border = \"shadow\",\n        style = \"minimal\",\n      },\n    },\n    open_file = {\n      quit_on_open = false,\n      eject = true,\n      resize_window = true,\n      relative_path = true,\n      window_picker = {\n        enable = true,\n        picker = \"default\",\n        chars = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890\",\n        exclude = {\n          filetype = { \"notify\", \"packer\", \"qf\", \"diff\", \"fugitive\", \"fugitiveblame\" },\n          buftype = { \"nofile\", \"terminal\", \"help\" },\n        },\n      },\n    },\n    remove_file = {\n      close_window = true,\n    },\n  },\n  trash = {\n    cmd = \"gio trash\",\n  },\n  tab = {\n    sync = {\n      open = false,\n      close = false,\n      ignore = {},\n    },\n  },\n  notify = {\n    threshold = vim.log.levels.INFO,\n    absolute_path = true,\n  },\n  help = {\n    sort_by = \"key\",\n  },\n  ui = {\n    confirm = {\n      remove = true,\n      trash = true,\n      default_yes = false,\n    },\n  },\n  bookmarks = {\n    persist = false,\n  },\n  experimental = {\n  },\n  log = {\n    enable = false,\n    truncate = false,\n    types = {\n      all = false,\n      config = false,\n      copy_paste = false,\n      dev = false,\n      diagnostics = false,\n      git = false,\n      profile = false,\n      watcher = false,\n    },\n  },\n} -- config-default-end\n\n-- Immediately apply OS specific localisations to defaults\nif utils.is_macos or utils.is_windows then\n  M.d.trash.cmd = \"trash\"\nend\nif utils.is_windows then\n  M.d.filesystem_watchers.max_events = 1000\nend\n\nlocal FIELD_SKIP_VALIDATE = {\n  open_win_config = true,\n}\n\nlocal ACCEPTED_TYPES = {\n  on_attach = { \"function\", \"string\" },\n  sort = {\n    sorter = { \"function\", \"string\" },\n  },\n  view = {\n    width = {\n      \"string\",\n      \"function\",\n      \"number\",\n      \"table\",\n      min = { \"string\", \"function\", \"number\" },\n      max = { \"string\", \"function\", \"number\" },\n      lines_excluded = { \"table\" },\n      padding = { \"function\", \"number\" },\n    },\n  },\n  renderer = {\n    hidden_display = { \"function\", \"string\" },\n    group_empty = { \"boolean\", \"function\" },\n    root_folder_label = { \"function\", \"string\", \"boolean\" },\n  },\n  update_focused_file = {\n    exclude = { \"function\" },\n  },\n  git = {\n    disable_for_dirs = { \"function\" },\n  },\n  filters = {\n    custom = { \"function\" },\n  },\n  filesystem_watchers = {\n    ignore_dirs = { \"function\" },\n  },\n  actions = {\n    open_file = {\n      window_picker = {\n        picker = { \"function\", \"string\" },\n      },\n    },\n  },\n  bookmarks = {\n    persist = { \"boolean\", \"string\" },\n  },\n}\n\nlocal ACCEPTED_STRINGS = {\n  sort = {\n    sorter = { \"name\", \"case_sensitive\", \"modification_time\", \"extension\", \"suffix\", \"filetype\" },\n  },\n  view = {\n    side = { \"left\", \"right\" },\n    signcolumn = { \"yes\", \"no\", \"auto\" },\n  },\n  renderer = {\n    hidden_display = { \"none\", \"simple\", \"all\" },\n    highlight_git = { \"none\", \"icon\", \"name\", \"all\" },\n    highlight_opened_files = { \"none\", \"icon\", \"name\", \"all\" },\n    highlight_modified = { \"none\", \"icon\", \"name\", \"all\" },\n    highlight_hidden = { \"none\", \"icon\", \"name\", \"all\" },\n    highlight_bookmarks = { \"none\", \"icon\", \"name\", \"all\" },\n    highlight_diagnostics = { \"none\", \"icon\", \"name\", \"all\" },\n    highlight_clipboard = { \"none\", \"icon\", \"name\", \"all\" },\n    icons = {\n      git_placement = { \"before\", \"after\", \"signcolumn\", \"right_align\" },\n      modified_placement = { \"before\", \"after\", \"signcolumn\", \"right_align\" },\n      hidden_placement = { \"before\", \"after\", \"signcolumn\", \"right_align\" },\n      diagnostics_placement = { \"before\", \"after\", \"signcolumn\", \"right_align\" },\n      bookmarks_placement = { \"before\", \"after\", \"signcolumn\", \"right_align\" },\n    },\n  },\n  help = {\n    sort_by = { \"key\", \"desc\" },\n  },\n}\n\nlocal ACCEPTED_ENUMS = {\n  view = {\n    width = {\n      lines_excluded = { \"root\", },\n    },\n  },\n}\n\n---Validate types and values of the user supplied config.\n---Warns and removes invalid in place.\n---@param u nvim_tree.config\nlocal function validate_config(u)\n  local msg\n\n  ---@param user any\n  ---@param def any\n  ---@param strs table\n  ---@param types table\n  ---@param enums table\n  ---@param prefix string\n  local function validate(user, def, strs, types, enums, prefix)\n    -- if user's option is not a table there is nothing to do\n    if type(user) ~= \"table\" then\n      return\n    end\n\n    -- we have hit a leaf enum to validate against - it's an integer indexed table\n    local enum_value = type(enums) == \"table\" and next(enums) and type(next(enums)) == \"number\"\n\n    -- only compare tables with contents that are not integer indexed nor enums\n    if not enum_value and (type(def) ~= \"table\" or not next(def) or type(next(def)) == \"number\") then\n      -- unless the field can be a table (and is not a table in default config)\n      if vim.tbl_contains(types, \"table\") then\n        -- use a dummy default to allow all checks\n        def = {}\n      else\n        return\n      end\n    end\n\n    for k, v in pairs(user) do\n      if not FIELD_SKIP_VALIDATE[k] then\n        local invalid\n\n        if enum_value then\n          if not vim.tbl_contains(enums, v) then\n            invalid = string.format(\"Invalid value for field %s%s: Expected one of enum '%s', got '%s'\", prefix, k,\n              table.concat(enums, \"'|'\"), tostring(v))\n          end\n        else\n          if def[k] == nil and types[k] == nil then\n            -- option does not exist\n            invalid = string.format(\"Unknown option: %s%s\", prefix, k)\n          elseif type(v) ~= type(def[k]) then\n            local expected\n\n            if types[k] and #types[k] > 0 then\n              if not vim.tbl_contains(types[k], type(v)) then\n                expected = table.concat(types[k], \"|\")\n              end\n            else\n              expected = type(def[k])\n            end\n\n            if expected then\n              -- option is of the wrong type\n              invalid = string.format(\"Invalid option: %s%s. Expected %s, got %s\", prefix, k, expected, type(v))\n            end\n          elseif type(v) == \"string\" and strs[k] and not vim.tbl_contains(strs[k], v) then\n            -- option has type `string` but value is not accepted\n            invalid = string.format(\"Invalid value for field %s%s: '%s'\", prefix, k, v)\n          end\n        end\n\n        if invalid then\n          if msg then\n            msg = string.format(\"%s\\n%s\", msg, invalid)\n          else\n            msg = invalid\n          end\n          user[k] = nil\n        elseif not enum_value then\n          validate(v, def[k], strs[k] or {}, types[k] or {}, enums[k] or {}, prefix .. k .. \".\")\n        end\n      end\n    end\n  end\n\n  validate(u, M.d, ACCEPTED_STRINGS, ACCEPTED_TYPES, ACCEPTED_ENUMS, \"\")\n\n  if msg then\n    notify.warn(msg .. \"\\n\\nsee :help nvim-tree-config for available configuration options\")\n  end\nend\n\n---Validate user config and migrate legacy.\n---Merge with M.d and persist as M.g\n---When no user config M.g is set to M.d and M.u is set to nil\n---@param u? nvim_tree.config user supplied subset of config\nfunction M.setup(u)\n  if not u or type(u) ~= \"table\" then\n    if u then\n      notify.warn(string.format(\"invalid config type \\\"%s\\\" passed to setup, using defaults\", type(u)))\n    end\n    M.g = vim.deepcopy(M.d)\n    M.u = nil\n    return\n  end\n\n  -- retain user for reference\n  M.u = vim.deepcopy(u)\n\n  legacy.migrate_config(u)\n\n  validate_config(u)\n\n  -- set global to the validated and populated user config\n  M.g = vim.tbl_deep_extend(\"force\", M.d, u)\nend\n\n---Deep clone defaults\n---@return nvim_tree.config\nfunction M.d_clone()\n  return vim.deepcopy(M.d)\nend\n\n---Deep clone user\n---@return nvim_tree.config? nil when no config passed to setup\nfunction M.u_clone()\n  return vim.deepcopy(M.u)\nend\n\n---Deep clone global\n---@return nvim_tree.config? nil when setup not called\nfunction M.g_clone()\n  return vim.deepcopy(M.g)\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/core.lua",
    "content": "local events = require(\"nvim-tree.events\")\nlocal notify = require(\"nvim-tree.notify\")\nlocal view = require(\"nvim-tree.view\")\nlocal log = require(\"nvim-tree.log\")\n\nlocal M = {}\n\n---@type Explorer|nil\nlocal TreeExplorer = nil\nlocal first_init_done = false\n\n---@param foldername string\nfunction M.init(foldername)\n  local profile = log.profile_start(\"core init %s\", foldername)\n\n  if TreeExplorer then\n    TreeExplorer:destroy()\n  end\n\n  local err, path\n\n  if foldername then\n    path, err = vim.loop.fs_realpath(foldername)\n  else\n    path, err = vim.loop.cwd()\n  end\n  if path then\n    TreeExplorer = require(\"nvim-tree.explorer\")({ path = path })\n  else\n    notify.error(err)\n    TreeExplorer = nil\n  end\n\n  if not first_init_done then\n    events._dispatch_ready()\n    first_init_done = true\n  end\n  log.profile_end(profile)\nend\n\n---@return Explorer|nil\nfunction M.get_explorer()\n  return TreeExplorer\nend\n\nfunction M.reset_explorer()\n  TreeExplorer = nil\nend\n\n---@return string|nil\nfunction M.get_cwd()\n  return TreeExplorer and TreeExplorer.absolute_path\nend\n\n---@return integer\nfunction M.get_nodes_starting_line()\n  local offset = 1\n  if view.is_root_folder_visible(M.get_cwd()) then\n    offset = offset + 1\n  end\n  if TreeExplorer and TreeExplorer.live_filter.filter then\n    return offset + 1\n  end\n  return offset\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/diagnostics.lua",
    "content": "local core = require(\"nvim-tree.core\")\nlocal utils = require(\"nvim-tree.utils\")\nlocal view = require(\"nvim-tree.view\")\nlocal log = require(\"nvim-tree.log\")\n\nlocal DirectoryNode = require(\"nvim-tree.node.directory\")\n\nlocal M = {}\n\n---COC severity level strings to LSP severity levels\n---@enum COC_SEVERITY_LEVELS\nlocal COC_SEVERITY_LEVELS = {\n  Error = 1,\n  Warning = 2,\n  Information = 3,\n  Hint = 4,\n}\n\n---Absolute Node path to LSP severity level\n---@alias NodeSeverities table<string, vim.diagnostic.Severity>\n\n---@class DiagStatus\n---@field value lsp.DiagnosticSeverity|nil\n---@field cache_version integer\n\n--- The buffer-severity mappings derived during the last diagnostic list update.\n---@type NodeSeverities\nlocal NODE_SEVERITIES = {}\n\n---The cache version number of the buffer-severity mappings.\n---@type integer\nlocal NODE_SEVERITIES_VERSION = 0\n\n---@param path string\n---@return string\nlocal function uniformize_path(path)\n  return utils.canonical_path(path:gsub(\"\\\\\", \"/\"))\nend\n\n---Severity is within diagnostics.severity.min, diagnostics.severity.max\n---@param severity lsp.DiagnosticSeverity\n---@param config table\n---@return boolean\nlocal function is_severity_in_range(severity, config)\n  return config.max <= severity and severity <= config.min\nend\n\n---Handle any COC exceptions, preventing any propagation\n---@param err string\nlocal function handle_coc_exception(err)\n  log.line(\"diagnostics\", \"handle_coc_exception: %s\", vim.inspect(err))\n  local notify = true\n\n  -- avoid distractions on interrupts (CTRL-C)\n  if err:find(\"Vim:Interrupt\") or err:find(\"Keyboard interrupt\") then\n    notify = false\n  end\n\n  if notify then\n    require(\"nvim-tree.notify\").error(\"Diagnostics update from coc.nvim failed. \" .. vim.inspect(err))\n  end\nend\n\n---COC service initialized\n---@return boolean\nlocal function is_using_coc()\n  return vim.g.coc_service_initialized == 1\nend\n\n---Marshal severities from COC. Does nothing when COC service not started.\n---@return NodeSeverities\nlocal function from_coc()\n  if not is_using_coc() then\n    return {}\n  end\n\n  local ok, diagnostic_list = xpcall(function()\n    return vim.fn.CocAction(\"diagnosticList\")\n  end, handle_coc_exception)\n  if not ok or type(diagnostic_list) ~= \"table\" or vim.tbl_isempty(diagnostic_list) then\n    return {}\n  end\n\n  local buffer_severity = {}\n  for _, diagnostic in ipairs(diagnostic_list) do\n    local bufname = uniformize_path(diagnostic.file)\n    local coc_severity = COC_SEVERITY_LEVELS[diagnostic.severity]\n    local highest_severity = buffer_severity[bufname] or coc_severity\n    if is_severity_in_range(highest_severity, M.severity) then\n      buffer_severity[bufname] = math.min(highest_severity, coc_severity)\n    end\n  end\n\n  return buffer_severity\nend\n\n---Maybe retrieve severity level from the cache\n---@param node Node\n---@return DiagStatus\nlocal function from_cache(node)\n  local nodepath = uniformize_path(node.absolute_path)\n  local max_severity = nil\n  if not node:is(DirectoryNode) then\n    -- direct cache hit for files\n    max_severity = NODE_SEVERITIES[nodepath]\n  else\n    -- dirs should be searched in the list of cached buffer names by prefix\n    for bufname, severity in pairs(NODE_SEVERITIES) do\n      local node_contains_buf = vim.startswith(bufname, nodepath .. \"/\")\n      if node_contains_buf then\n        if not max_severity or severity < max_severity then\n          max_severity = severity\n        end\n      end\n    end\n  end\n  return { value = max_severity, cache_version = NODE_SEVERITIES_VERSION }\nend\n\n---Fired on DiagnosticChanged for a single buffer.\n---This will be called on set and reset of diagnostics.\n---On disabling LSP, a reset event will be sent for all buffers.\n---@param ev table standard event with data.diagnostics populated\nfunction M.update_lsp(ev)\n  if not M.enable or not ev or not ev.data or not ev.data.diagnostics then\n    return\n  end\n\n  local profile_event = log.profile_start(\"DiagnosticChanged event\")\n\n  local diagnostics = vim.diagnostic.get(ev.buf)\n\n  -- use the buffer from the event, as ev.data.diagnostics will be empty on resolved diagnostics\n  local bufname = uniformize_path(vim.api.nvim_buf_get_name(ev.buf))\n\n  ---@type vim.diagnostic.Severity?\n  local new_severity = nil\n\n  -- most severe (lowest) severity in user range\n  for _, diagnostic in ipairs(diagnostics) do\n    if diagnostic.severity >= M.severity.max and diagnostic.severity <= M.severity.min then\n      if not new_severity or diagnostic.severity < new_severity then\n        new_severity = diagnostic.severity\n      end\n    end\n  end\n\n  -- record delta and schedule a redraw\n  if new_severity ~= NODE_SEVERITIES[bufname] then\n    NODE_SEVERITIES[bufname] = new_severity\n    NODE_SEVERITIES_VERSION = NODE_SEVERITIES_VERSION + 1\n\n    utils.debounce(\"DiagnosticChanged redraw\", M.debounce_delay, function()\n      local profile_redraw = log.profile_start(\"DiagnosticChanged redraw\")\n\n      local explorer = core.get_explorer()\n      if explorer then\n        explorer.renderer:draw()\n      end\n\n      log.profile_end(profile_redraw)\n    end)\n  end\n\n  log.profile_end(profile_event)\nend\n\n---Fired on CocDiagnosticChanged events:\n---debounced retrieval, cache update, version increment and draw\nfunction M.update_coc()\n  if not M.enable then\n    return\n  end\n  utils.debounce(\"CocDiagnosticChanged update\", M.debounce_delay, function()\n    local profile = log.profile_start(\"CocDiagnosticChanged update\")\n    NODE_SEVERITIES = from_coc()\n    NODE_SEVERITIES_VERSION = NODE_SEVERITIES_VERSION + 1\n    if log.enabled(\"diagnostics\") then\n      for bufname, severity in pairs(NODE_SEVERITIES) do\n        log.line(\"diagnostics\", \"COC Indexing bufname '%s' with severity %d\", bufname, severity)\n      end\n    end\n    log.profile_end(profile)\n\n    local bufnr = view.get_bufnr()\n    local should_draw = bufnr and vim.api.nvim_buf_is_valid(bufnr) and vim.api.nvim_buf_is_loaded(bufnr)\n    if should_draw then\n      local explorer = core.get_explorer()\n      if explorer then\n        explorer.renderer:draw()\n      end\n    end\n  end)\nend\n\n---Maybe retrieve diagnostic status for a node.\n---Returns cached value when node's version matches.\n---@param node Node\n---@return DiagStatus|nil\nfunction M.get_diag_status(node)\n  if not M.enable then\n    return nil\n  end\n\n  -- dir but we shouldn't show on dirs at all\n  if node:is(DirectoryNode) and not M.show_on_dirs then\n    return nil\n  end\n\n  -- here, we do a lazy update of the diagnostic status carried by the node.\n  -- This is by design, as diagnostics and nodes live in completely separate\n  -- worlds, and this module is the link between the two\n  if not node.diag_status or node.diag_status.cache_version < NODE_SEVERITIES_VERSION then\n    node.diag_status = from_cache(node)\n  end\n\n  local dir = node:as(DirectoryNode)\n\n  -- file\n  if not dir then\n    return node.diag_status\n  end\n\n  -- dir is closed or we should show on open_dirs\n  if not dir.open or M.show_on_open_dirs then\n    return node.diag_status\n  end\n  return nil\nend\n\nfunction M.setup(opts)\n  M.enable = opts.diagnostics.enable\n  M.debounce_delay = opts.diagnostics.debounce_delay\n  M.severity = opts.diagnostics.diagnostic_opts and {\n    min = vim.diagnostic.severity.HINT,\n    max = vim.diagnostic.severity.ERROR\n  } or opts.diagnostics.severity\n\n  if M.enable then\n    log.line(\"diagnostics\", \"setup\")\n  end\n\n  M.show_on_dirs = opts.diagnostics.show_on_dirs\n  M.show_on_open_dirs = opts.diagnostics.show_on_open_dirs\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/enum.lua",
    "content": "local M = {}\n\n---Reason for filter in filter.lua\n---@enum FILTER_REASON\nM.FILTER_REASON = {\n  none = 0, -- It's not filtered\n  git = 1,\n  buf = 2,\n  dotfile = 4,\n  custom = 8,\n  bookmark = 16,\n}\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/events.lua",
    "content": "local notify = require(\"nvim-tree.notify\")\nlocal Event = require(\"nvim-tree._meta.api.events\").Event\n\nlocal M = {}\n\nlocal global_handlers = {}\n\n---@param event_name string\n---@return table\nlocal function get_handlers(event_name)\n  return global_handlers[event_name] or {}\nend\n\n---@param event_name string\n---@param handler function\nfunction M.subscribe(event_name, handler)\n  local handlers = get_handlers(event_name)\n  table.insert(handlers, handler)\n  global_handlers[event_name] = handlers\nend\n\n---@param event_name string\n---@param payload table|nil\nlocal function dispatch(event_name, payload)\n  for _, handler in pairs(get_handlers(event_name)) do\n    local success, error = pcall(handler, payload)\n    if not success then\n      notify.error(\"Handler for event \" .. event_name .. \" errored. \" .. vim.inspect(error))\n    end\n  end\nend\n\n--@private\nfunction M._dispatch_ready()\n  dispatch(Event.Ready)\nend\n\n--@private\nfunction M._dispatch_will_rename_node(old_name, new_name)\n  dispatch(Event.WillRenameNode, { old_name = old_name, new_name = new_name })\nend\n\n--@private\nfunction M._dispatch_node_renamed(old_name, new_name)\n  dispatch(Event.NodeRenamed, { old_name = old_name, new_name = new_name })\nend\n\n--@private\nfunction M._dispatch_will_remove_file(fname)\n  dispatch(Event.WillRemoveFile, { fname = fname })\nend\n\n--@private\nfunction M._dispatch_file_removed(fname)\n  dispatch(Event.FileRemoved, { fname = fname })\nend\n\n--@private\nfunction M._dispatch_will_create_file(fname)\n  dispatch(Event.WillCreateFile, { fname = fname })\nend\n\n--@private\nfunction M._dispatch_file_created(fname)\n  dispatch(Event.FileCreated, { fname = fname })\nend\n\n--@private\nfunction M._dispatch_folder_created(folder_name)\n  dispatch(Event.FolderCreated, { folder_name = folder_name })\nend\n\n--@private\nfunction M._dispatch_folder_removed(folder_name)\n  dispatch(Event.FolderRemoved, { folder_name = folder_name })\nend\n\n--@private\nfunction M._dispatch_on_tree_pre_open()\n  dispatch(Event.TreePreOpen, nil)\nend\n\n--@private\nfunction M._dispatch_on_tree_open()\n  dispatch(Event.TreeOpen, nil)\nend\n\n--@private\nfunction M._dispatch_on_tree_close()\n  dispatch(Event.TreeClose, nil)\nend\n\n--@private\nfunction M._dispatch_on_tree_resize(size)\n  dispatch(Event.Resize, size)\nend\n\n--@private\nfunction M._dispatch_tree_attached_post(buf)\n  dispatch(Event.TreeAttachedPost, buf)\nend\n\n--@private\nfunction M._dispatch_on_tree_rendered(bufnr, winnr)\n  dispatch(Event.TreeRendered, { bufnr = bufnr, winnr = winnr })\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/explorer/filters.lua",
    "content": "local utils = require(\"nvim-tree.utils\")\nlocal FILTER_REASON = require(\"nvim-tree.enum\").FILTER_REASON\n\nlocal Class = require(\"nvim-tree.classic\")\n\n---@alias FilterType \"custom\" | \"dotfiles\" | \"git_ignored\" | \"git_clean\" | \"no_buffer\" | \"no_bookmark\"\n\n---@class (exact) Filters: nvim_tree.Class\n---@field enabled boolean\n---@field state table<FilterType, boolean>\n---@field private explorer Explorer\n---@field private exclude_list string[] filters.exclude\n---@field private ignore_list table<string, boolean> filters.custom string table\n---@field private custom_function (fun(absolute_path: string): boolean)|nil filters.custom function\nlocal Filters = Class:extend()\n\n---@class Filters\n---@overload fun(args: FiltersArgs): Filters\n\n---@class (exact) FiltersArgs\n---@field explorer Explorer\n\n---@protected\n---@param args FiltersArgs\nfunction Filters:new(args)\n  self.explorer        = args.explorer\n  self.ignore_list     = {}\n  self.exclude_list    = self.explorer.opts.filters.exclude\n  self.custom_function = nil\n\n  self.enabled         = self.explorer.opts.filters.enable\n  self.state           = {\n    custom      = true,\n    dotfiles    = self.explorer.opts.filters.dotfiles,\n    git_ignored = self.explorer.opts.filters.git_ignored,\n    git_clean   = self.explorer.opts.filters.git_clean,\n    no_buffer   = self.explorer.opts.filters.no_buffer,\n    no_bookmark = self.explorer.opts.filters.no_bookmark,\n  }\n\n  local custom_filter  = self.explorer.opts.filters.custom\n  if type(custom_filter) == \"function\" then\n    self.custom_function = custom_filter\n  else\n    if custom_filter and #custom_filter > 0 then\n      for _, filter_name in pairs(custom_filter) do\n        self.ignore_list[filter_name] = true\n      end\n    end\n  end\nend\n\n---@private\n---@param path string\n---@return boolean\nfunction Filters:is_excluded(path)\n  for _, node in ipairs(self.exclude_list) do\n    if path:match(node) then\n      return true\n    end\n  end\n  return false\nend\n\n---Check if the given path is git clean/ignored\n---@private\n---@param path string Absolute path\n---@param project nvim_tree.git.Project from prepare\n---@return boolean\nfunction Filters:git(path, project)\n  if type(project) ~= \"table\" or type(project.files) ~= \"table\" or type(project.dirs) ~= \"table\" then\n    return false\n  end\n\n  -- default status to clean\n  local xy = project.files[path]\n  xy = xy or project.dirs.direct[path] and project.dirs.direct[path][1]\n  xy = xy or project.dirs.indirect[path] and project.dirs.indirect[path][1]\n\n  -- filter ignored; overrides clean as they are effectively dirty\n  if self.state.git_ignored and xy == \"!!\" then\n    return true\n  end\n\n  -- filter clean\n  if self.state.git_clean and not xy then\n    return true\n  end\n\n  return false\nend\n\n---Check if the given path has no listed buffer\n---@private\n---@param path string Absolute path\n---@param bufinfo table vim.fn.getbufinfo { buflisted = 1 }\n---@return boolean\nfunction Filters:buf(path, bufinfo)\n  if not self.state.no_buffer or type(bufinfo) ~= \"table\" then\n    return false\n  end\n\n  -- filter files with no open buffer and directories containing no open buffers\n  for _, b in ipairs(bufinfo) do\n    if b.name == path or b.name:find(path .. \"/\", 1, true) then\n      return false\n    end\n  end\n\n  return true\nend\n\n---@private\n---@param path string\n---@return boolean\nfunction Filters:dotfile(path)\n  return self.state.dotfiles and utils.path_basename(path):sub(1, 1) == \".\"\nend\n\n---Bookmark is present\n---@private\n---@param path string\n---@param path_type string|nil filetype of path\n---@param bookmarks table<string, Node> path to bookmarked Node\n---@return boolean\nfunction Filters:bookmark(path, path_type, bookmarks)\n  if not self.state.no_bookmark then\n    return false\n  end\n  -- if bookmark is empty, we should see a empty filetree\n  if next(bookmarks) == nil then\n    return true\n  end\n\n  local DirectoryNode = require(\"nvim-tree.node.directory\")\n  local mark_parent = utils.path_add_trailing(path)\n\n  for bookmark_path, bookmark_entry in pairs(bookmarks) do\n    if path == bookmark_path then\n      return false\n    end\n\n    if path_type == \"directory\" then\n      -- check if path is mark's parent\n      if vim.fn.stridx(bookmark_path, mark_parent) == 0 then\n        return false\n      end\n    end\n\n    ---@type DirectoryNode?\n    local dir = bookmark_entry:as(DirectoryNode)\n    if dir then\n      -- check if mark is path's parent\n      local path_parent = utils.path_add_trailing(bookmark_path)\n      if vim.fn.stridx(path, path_parent) == 0 then\n        return false\n      end\n    end\n  end\n\n  return true\nend\n\n---@private\n---@param path string\n---@return boolean\nfunction Filters:custom(path)\n  if not self.state.custom then\n    return false\n  end\n\n  local basename = utils.path_basename(path)\n\n  -- filter user's custom function\n  if self.custom_function and self.custom_function(path) then\n    return true\n  end\n\n  -- filter custom regexes\n  local relpath = utils.path_relative(path, vim.loop.cwd())\n  for pat, _ in pairs(self.ignore_list) do\n    if vim.fn.match(relpath, pat) ~= -1 or vim.fn.match(basename, pat) ~= -1 then\n      return true\n    end\n  end\n\n  local idx = path:match(\".+()%.[^.]+$\")\n  if idx then\n    if self.ignore_list[\"*\" .. string.sub(path, idx)] == true then\n      return true\n    end\n  end\n\n  return false\nend\n\n---Prepare arguments for should_filter. This is done prior to should_filter for efficiency reasons.\n---@param project nvim_tree.git.Project? optional results of git.load_projects(...)\n---@return table\n--- project: reference\n--- bufinfo: empty unless no_buffer set: vim.fn.getbufinfo { buflisted = 1 }\n--- bookmarks: absolute paths to boolean\nfunction Filters:prepare(project)\n  local status = {\n    project = project or {},\n    bufinfo = {},\n    bookmarks = {},\n  }\n\n  if self.state.no_buffer then\n    status.bufinfo = vim.fn.getbufinfo({ buflisted = 1 })\n  end\n\n  local explorer = require(\"nvim-tree.core\").get_explorer()\n  if explorer then\n    for _, node in ipairs(explorer.marks:list()) do\n      status.bookmarks[node.absolute_path] = node\n    end\n  end\n\n  return status\nend\n\n---Check if the given path should be filtered.\n---@param path string Absolute path\n---@param fs_stat uv.fs_stat.result|nil fs_stat of file\n---@param status table from prepare\n---@return boolean\nfunction Filters:should_filter(path, fs_stat, status)\n  if not self.enabled then\n    return false\n  end\n\n  -- exclusions override all filters\n  if self:is_excluded(path) then\n    return false\n  end\n\n  return self:git(path, status.project)\n    or self:buf(path, status.bufinfo)\n    or self:dotfile(path)\n    or self:custom(path)\n    or self:bookmark(path, fs_stat and fs_stat.type, status.bookmarks)\nend\n\n--- Check if the given path should be filtered, and provide the reason why it was\n---@param path string Absolute path\n---@param fs_stat uv.fs_stat.result|nil fs_stat of file\n---@param status table from prepare\n---@return FILTER_REASON\nfunction Filters:should_filter_as_reason(path, fs_stat, status)\n  if not self.enabled then\n    return FILTER_REASON.none\n  end\n\n  if self:is_excluded(path) then\n    return FILTER_REASON.none\n  end\n\n  if self:git(path, status.project) then\n    return FILTER_REASON.git\n  elseif self:buf(path, status.bufinfo) then\n    return FILTER_REASON.buf\n  elseif self:dotfile(path) then\n    return FILTER_REASON.dotfile\n  elseif self:custom(path) then\n    return FILTER_REASON.custom\n  elseif self:bookmark(path, fs_stat and fs_stat.type, status.bookmarks) then\n    return FILTER_REASON.bookmark\n  else\n    return FILTER_REASON.none\n  end\nend\n\n---Toggle a type and refresh\n---@param type FilterType? nil to disable all\nfunction Filters:toggle(type)\n  if not type or self.state[type] == nil then\n    self.enabled = not self.enabled\n  else\n    self.state[type] = not self.state[type]\n  end\n\n  local node = self.explorer:get_node_at_cursor()\n  self.explorer:reload_explorer()\n  if node then\n    self.explorer:focus_node_or_parent(node)\n  end\nend\n\nreturn Filters\n"
  },
  {
    "path": "lua/nvim-tree/explorer/init.lua",
    "content": "local appearance = require(\"nvim-tree.appearance\")\nlocal buffers = require(\"nvim-tree.buffers\")\nlocal core = require(\"nvim-tree.core\")\nlocal git = require(\"nvim-tree.git\")\nlocal log = require(\"nvim-tree.log\")\nlocal utils = require(\"nvim-tree.utils\")\nlocal view = require(\"nvim-tree.view\")\nlocal node_factory = require(\"nvim-tree.node.factory\")\n\nlocal DirectoryNode = require(\"nvim-tree.node.directory\")\nlocal RootNode = require(\"nvim-tree.node.root\")\nlocal Watcher = require(\"nvim-tree.watcher\")\n\nlocal Iterator = require(\"nvim-tree.iterators.node-iterator\")\nlocal NodeIterator = require(\"nvim-tree.iterators.node-iterator\")\n\nlocal Filters = require(\"nvim-tree.explorer.filters\")\nlocal Marks = require(\"nvim-tree.marks\")\nlocal LiveFilter = require(\"nvim-tree.explorer.live-filter\")\nlocal Sorter = require(\"nvim-tree.explorer.sorter\")\nlocal Clipboard = require(\"nvim-tree.actions.fs.clipboard\")\nlocal Renderer = require(\"nvim-tree.renderer\")\nlocal FileNode = require(\"nvim-tree.node.file\")\n\nlocal FILTER_REASON = require(\"nvim-tree.enum\").FILTER_REASON\nlocal find_file = require(\"nvim-tree.actions.finders.find-file\")\n\nlocal config\n\n---@class (exact) Explorer: RootNode\n---@field uid_explorer number vim.loop.hrtime() at construction time\n---@field opts table user options\n---@field augroup_id integer\n---@field current_tab integer\n---@field renderer Renderer\n---@field filters Filters\n---@field live_filter LiveFilter\n---@field sorters Sorter\n---@field marks Marks\n---@field clipboard Clipboard\nlocal Explorer = RootNode:extend()\n\n---@class Explorer\n---@overload fun(args: ExplorerArgs): Explorer\n\n---@class (exact) ExplorerArgs\n---@field path string\n\n---@protected\n---@param args ExplorerArgs\nfunction Explorer:new(args)\n  Explorer.super.new(self, {\n    explorer      = self,\n    absolute_path = args.path,\n    name          = \"..\",\n  })\n\n  self.uid_explorer = vim.loop.hrtime()\n  self.augroup_id   = vim.api.nvim_create_augroup(\"NvimTree_Explorer_\" .. self.uid_explorer, {})\n\n  self.open         = true\n  self.opts         = config\n\n\n  self.sorters     = Sorter({ explorer = self })\n  self.renderer    = Renderer({ explorer = self })\n  self.filters     = Filters({ explorer = self })\n  self.live_filter = LiveFilter({ explorer = self })\n  self.marks       = Marks({ explorer = self })\n  self.clipboard   = Clipboard({ explorer = self })\n\n  self.current_tab = vim.api.nvim_get_current_tabpage()\n\n  self:create_autocmds()\n\n  self:_load(self)\nend\n\nfunction Explorer:destroy()\n  log.line(\"dev\", \"Explorer:destroy\")\n\n  vim.api.nvim_del_augroup_by_id(self.augroup_id)\n\n  RootNode.destroy(self)\nend\n\nfunction Explorer:create_autocmds()\n  -- reset and draw (highlights) when colorscheme is changed\n  vim.api.nvim_create_autocmd(\"ColorScheme\", {\n    group = self.augroup_id,\n    callback = function()\n      appearance.setup()\n      view.reset_winhl()\n      self.renderer:draw()\n    end,\n  })\n\n  vim.api.nvim_create_autocmd(\"BufWritePost\", {\n    group = self.augroup_id,\n    callback = function()\n      if self.opts.auto_reload_on_write and not self.opts.filesystem_watchers.enable then\n        self:reload_explorer()\n      end\n    end,\n  })\n\n  vim.api.nvim_create_autocmd(\"BufReadPost\", {\n    group = self.augroup_id,\n    callback = function(data)\n      -- only handle normal files\n      if vim.bo[data.buf].buftype ~= \"\" then\n        return\n      end\n\n      if self.filters.state.no_buffer then\n        -- full reload is required to update the filter state\n        utils.debounce(\"Buf:filter_buffer_\" .. self.uid_explorer, self.opts.view.debounce_delay, function()\n          self:reload_explorer()\n        end)\n      elseif self.opts.renderer.highlight_opened_files ~= \"none\" then\n        -- draw to update opened highlight\n        self.renderer:draw()\n      end\n    end,\n  })\n\n  -- update opened file buffers\n  vim.api.nvim_create_autocmd(\"BufUnload\", {\n    group = self.augroup_id,\n    callback = function(data)\n      -- only handle normal files\n      if vim.bo[data.buf].buftype ~= \"\" then\n        return\n      end\n\n      if self.filters.state.no_buffer then\n        -- full reload is required to update the filter state\n        utils.debounce(\"Buf:filter_buffer_\" .. self.uid_explorer, self.opts.view.debounce_delay, function()\n          self:reload_explorer()\n        end)\n      elseif self.opts.renderer.highlight_opened_files ~= \"none\" then\n        -- draw to update opened highlight; must be delayed as the buffer is still loaded during BufUnload\n        vim.schedule(function()\n          self.renderer:draw()\n        end)\n      end\n    end,\n  })\n\n  vim.api.nvim_create_autocmd(\"BufEnter\", {\n    group = self.augroup_id,\n    pattern = \"NvimTree_*\",\n    callback = function()\n      if utils.is_nvim_tree_buf(0) then\n        if vim.fn.getcwd() ~= self.absolute_path or (self.opts.reload_on_bufenter and not self.opts.filesystem_watchers.enable) then\n          self:reload_explorer()\n        end\n      end\n    end,\n  })\n\n  vim.api.nvim_create_autocmd(\"User\", {\n    group = self.augroup_id,\n    pattern = { \"FugitiveChanged\", \"NeogitStatusRefreshed\" },\n    callback = function()\n      if not self.opts.filesystem_watchers.enable and self.opts.git.enable then\n        self:reload_git()\n      end\n    end,\n  })\n\n  if self.opts.hijack_cursor then\n    vim.api.nvim_create_autocmd(\"CursorMoved\", {\n      group = self.augroup_id,\n      pattern = \"NvimTree_*\",\n      callback = function()\n        if utils.is_nvim_tree_buf(0) then\n          self:place_cursor_on_node()\n        end\n      end,\n    })\n  end\n\n  if self.opts.modified.enable then\n    vim.api.nvim_create_autocmd({ \"BufModifiedSet\", \"BufWritePost\" }, {\n      group = self.augroup_id,\n      callback = function()\n        utils.debounce(\"Buf:modified_\" .. self.uid_explorer, self.opts.view.debounce_delay, function()\n          buffers.reload_modified()\n          self.renderer:draw()\n        end)\n      end,\n    })\n  end\nend\n\n---@param node DirectoryNode\nfunction Explorer:expand_dir_node(node)\n  self:_load(node)\nend\n\n---@param node DirectoryNode\n---@param project nvim_tree.git.Project?\n---@return Node[]?\nfunction Explorer:reload(node, project)\n  local cwd = node.link_to or node.absolute_path\n  local handle = vim.loop.fs_scandir(cwd)\n  if not handle then\n    return\n  end\n\n  local profile = log.profile_start(\"reload %s\", node.absolute_path)\n\n  local filter_status = self.filters:prepare(project)\n\n  if node.group_next then\n    node.nodes = { node.group_next }\n    node.group_next = nil\n  end\n\n  local remain_childs = {}\n\n  local node_ignored = node:is_git_ignored()\n  ---@type table<string, Node>\n  local nodes_by_path = utils.key_by(node.nodes, \"absolute_path\")\n\n  -- To reset we must 'zero' everything that we use\n  node.hidden_stats = vim.tbl_deep_extend(\"force\", node.hidden_stats or {}, {\n    git      = 0,\n    buf      = 0,\n    dotfile  = 0,\n    custom   = 0,\n    bookmark = 0,\n  })\n\n  while true do\n    local name, _ = vim.loop.fs_scandir_next(handle)\n    if not name then\n      break\n    end\n\n    local abs = utils.path_join({ cwd, name })\n\n    -- path incorrectly specified as an integer\n    local stat = vim.loop.fs_lstat(abs) ---@diagnostic disable-line param-type-mismatch\n\n    local filter_reason = self.filters:should_filter_as_reason(abs, stat, filter_status)\n    if filter_reason == FILTER_REASON.none then\n      remain_childs[abs] = true\n\n      -- Recreate node if type changes.\n      if nodes_by_path[abs] then\n        local n = nodes_by_path[abs]\n\n        if not stat or n.type ~= stat.type then\n          utils.array_remove(node.nodes, n)\n          n:destroy()\n          nodes_by_path[abs] = nil\n        end\n      end\n\n      if not nodes_by_path[abs] then\n        local new_child = node_factory.create({\n          explorer      = self,\n          parent        = node,\n          absolute_path = abs,\n          name          = name,\n          fs_stat       = stat\n        })\n        if new_child then\n          table.insert(node.nodes, new_child)\n          nodes_by_path[abs] = new_child\n        end\n      else\n        local n = nodes_by_path[abs]\n        if n then\n          n.executable = utils.is_executable(abs) or false\n          n.fs_stat = stat\n        end\n      end\n    else\n      for reason, value in pairs(FILTER_REASON) do\n        if filter_reason == value then\n          node.hidden_stats[reason] = node.hidden_stats[reason] + 1\n        end\n      end\n    end\n  end\n\n  node.nodes = vim.tbl_map(\n    self:update_git_statuses(nodes_by_path, node_ignored, project),\n    vim.tbl_filter(function(n)\n      if remain_childs[n.absolute_path] then\n        return remain_childs[n.absolute_path]\n      else\n        n:destroy()\n        return false\n      end\n    end, node.nodes)\n  )\n\n  local single_child = node:single_child_directory()\n  if config.renderer.group_empty and node.parent and single_child then\n    node.group_next = single_child\n    local ns = self:reload(single_child, project)\n    node.nodes = ns or {}\n    log.profile_end(profile)\n    return ns\n  end\n\n  self.sorters:sort(node.nodes)\n  self.live_filter:apply_filter(node)\n  log.profile_end(profile)\n  return node.nodes\nend\n\n---Refresh contents of all nodes to a path: actual directory and links.\n---Groups will be expanded if needed.\n---@param path string absolute path\nfunction Explorer:refresh_parent_nodes_for_path(path)\n  local profile = log.profile_start(\"refresh_parent_nodes_for_path %s\", path)\n\n  -- collect parent nodes from the top down\n  local parent_nodes = {}\n  NodeIterator.builder({ self })\n    :recursor(function(node)\n      return node.nodes\n    end)\n    :applier(function(node)\n      local abs_contains = node.absolute_path and path:find(node.absolute_path, 1, true) == 1\n      local link_contains = node.link_to and path:find(node.link_to, 1, true) == 1\n      if abs_contains or link_contains then\n        table.insert(parent_nodes, node)\n      end\n    end)\n    :iterate()\n\n  -- refresh in order; this will expand groups as needed\n  for _, node in ipairs(parent_nodes) do\n    local toplevel = git.get_toplevel(node.absolute_path)\n    local project = git.get_project(toplevel) or {}\n\n    self:reload(node, project)\n    git.update_parent_projects(node, project, toplevel)\n  end\n\n  log.profile_end(profile)\nend\n\n---@private\n---@param node DirectoryNode\nfunction Explorer:_load(node)\n  local cwd = node.link_to or node.absolute_path\n  local project = git.load_project(cwd)\n  self:explore(node, project, self)\nend\n\n---@private\n---@param nodes_by_path Node[]\n---@param node_ignored boolean\n---@param project nvim_tree.git.Project?\n---@return fun(node: Node): Node\nfunction Explorer:update_git_statuses(nodes_by_path, node_ignored, project)\n  return function(node)\n    if nodes_by_path[node.absolute_path] then\n      node:update_git_status(node_ignored, project)\n    end\n    return node\n  end\nend\n\n---@private\n---@param handle uv.uv_fs_t\n---@param cwd string\n---@param node DirectoryNode\n---@param project nvim_tree.git.Project\n---@param parent Explorer\nfunction Explorer:populate_children(handle, cwd, node, project, parent)\n  local node_ignored = node:is_git_ignored()\n  local nodes_by_path = utils.bool_record(node.nodes, \"absolute_path\")\n\n  local filter_status = parent.filters:prepare(project)\n\n  node.hidden_stats = vim.tbl_deep_extend(\"force\", node.hidden_stats or {}, {\n    git      = 0,\n    buf      = 0,\n    dotfile  = 0,\n    custom   = 0,\n    bookmark = 0,\n  })\n\n  while true do\n    local name, _ = vim.loop.fs_scandir_next(handle)\n    if not name then\n      break\n    end\n\n    local abs = utils.path_join({ cwd, name })\n\n    if Watcher.is_fs_event_capable(abs) then\n      local profile = log.profile_start(\"populate_children %s\", abs)\n\n      -- path incorrectly specified as an integer\n      local stat = vim.loop.fs_lstat(abs) ---@diagnostic disable-line param-type-mismatch\n\n      local filter_reason = parent.filters:should_filter_as_reason(abs, stat, filter_status)\n      if filter_reason == FILTER_REASON.none and not nodes_by_path[abs] then\n        local child = node_factory.create({\n          explorer      = self,\n          parent        = node,\n          absolute_path = abs,\n          name          = name,\n          fs_stat       = stat\n        })\n        if child then\n          table.insert(node.nodes, child)\n          nodes_by_path[child.absolute_path] = true\n          child:update_git_status(node_ignored, project)\n        end\n      elseif node.hidden_stats then\n        for reason, value in pairs(FILTER_REASON) do\n          if filter_reason == value and type(node.hidden_stats[reason]) == \"number\" then\n            node.hidden_stats[reason] = node.hidden_stats[reason] + 1\n          end\n        end\n      end\n\n      log.profile_end(profile)\n    end\n  end\nend\n\n---@private\n---@param node DirectoryNode\n---@param project nvim_tree.git.Project\n---@param parent Explorer\n---@return Node[]|nil\nfunction Explorer:explore(node, project, parent)\n  local cwd = node.link_to or node.absolute_path\n  local handle = vim.loop.fs_scandir(cwd)\n  if not handle then\n    return\n  end\n\n  local profile = log.profile_start(\"explore %s\", node.absolute_path)\n\n  self:populate_children(handle, cwd, node, project, parent)\n\n  local is_root = not node.parent\n  local single_child = node:single_child_directory()\n  if config.renderer.group_empty and not is_root and single_child then\n    local child_cwd = single_child.link_to or single_child.absolute_path\n    local child_project = git.load_project(child_cwd)\n    node.group_next = single_child\n    local ns = self:explore(single_child, child_project, parent)\n    node.nodes = ns or {}\n\n    log.profile_end(profile)\n    return ns\n  end\n\n  parent.sorters:sort(node.nodes)\n  parent.live_filter:apply_filter(node)\n\n  log.profile_end(profile)\n  return node.nodes\nend\n\n---@private\n---@param projects nvim_tree.git.Project[]\nfunction Explorer:refresh_nodes(projects)\n  Iterator.builder({ self })\n    :applier(function(n)\n      local dir = n:as(DirectoryNode)\n      if dir then\n        local toplevel = git.get_toplevel(dir.cwd or dir.link_to or dir.absolute_path)\n        self:reload(dir, projects[toplevel] or {})\n      end\n    end)\n    :recursor(function(n)\n      return n.group_next and { n.group_next } or (n.open and n.nodes)\n    end)\n    :iterate()\nend\n\nlocal event_running = false\nfunction Explorer:reload_explorer()\n  if event_running or vim.v.exiting ~= vim.NIL then\n    return\n  end\n  event_running = true\n\n  local projects = git.reload_all_projects()\n  self:refresh_nodes(projects)\n  if view.is_visible() then\n    self.renderer:draw()\n  end\n  event_running = false\nend\n\nfunction Explorer:reload_git()\n  if not git.config.git.enable or event_running then\n    return\n  end\n  event_running = true\n\n  local projects = git.reload_all_projects()\n  git.reload_node_status(self, projects)\n  self.renderer:draw()\n  event_running = false\nend\n\n---Cursor position as per vim.api.nvim_win_get_cursor\n---nil on no explorer or invalid view win\n---@return integer[]|nil\nfunction Explorer:get_cursor_position()\n  local winnr = view.get_winnr()\n  if not winnr or not vim.api.nvim_win_is_valid(winnr) then\n    return\n  end\n\n  return vim.api.nvim_win_get_cursor(winnr)\nend\n\n---@return Node|nil\nfunction Explorer:get_node_at_cursor()\n  local cursor = self:get_cursor_position()\n  if not cursor then\n    return\n  end\n\n  if cursor[1] == 1 and view.is_root_folder_visible(self.absolute_path) then\n    return self\n  end\n\n  return self:get_nodes_by_line(core.get_nodes_starting_line())[cursor[1]]\nend\n\nfunction Explorer:place_cursor_on_node()\n  local ok, search = pcall(vim.fn.searchcount)\n  if ok and search and search.exact_match == 1 then\n    return\n  end\n\n  local node = self:get_node_at_cursor()\n  if not node or node.name == \"..\" then\n    return\n  end\n  node = node:get_parent_of_group() or node\n\n  local line = vim.api.nvim_get_current_line()\n  local cursor = vim.api.nvim_win_get_cursor(0)\n  local idx = vim.fn.stridx(line, node.name)\n\n  if idx >= 0 then\n    vim.api.nvim_win_set_cursor(0, { cursor[1], idx })\n  end\nend\n\n-- Find the line number of a node.\n---@param node Node?\n---@return integer -1 not found\nfunction Explorer:find_node_line(node)\n  if not node then\n    return -1\n  end\n\n  local first_node_line = core.get_nodes_starting_line()\n  local nodes_by_line = self:get_nodes_by_line(first_node_line)\n  local iter_start, iter_end = first_node_line, #nodes_by_line\n\n  for line = iter_start, iter_end, 1 do\n    if nodes_by_line[line] == node then\n      return line\n    end\n  end\n\n  return -1\nend\n\n-- get the node in the tree state depending on the absolute path of the node\n-- (grouped or hidden too)\n---@param path string\n---@return Node|nil\n---@return number|nil\nfunction Explorer:get_node_from_path(path)\n  if self.absolute_path == path then\n    return self\n  end\n\n  return Iterator.builder(self.nodes)\n    :hidden()\n    :matcher(function(node)\n      return node.absolute_path == path or node.link_to == path\n    end)\n    :recursor(function(node)\n      if node.group_next then\n        return { node.group_next }\n      end\n      if node.nodes then\n        return node.nodes\n      end\n    end)\n    :iterate()\nend\n\n---Focus node passed as parameter if visible, otherwise focus first visible parent.\n---If none of the parents is visible focus root.\n---If node is nil do nothing.\n---@param node Node? node to focus\nfunction Explorer:focus_node_or_parent(node)\n  while node do\n    local found_node, i = self:find_node(function(node_)\n      return node_.absolute_path == node.absolute_path\n    end)\n\n    if found_node or node.parent == nil then\n      view.set_cursor({ i + 1, 1 })\n      break\n    end\n\n    node = node.parent\n  end\nend\n\n--- Get the node and index of the node from the tree that matches the predicate.\n--- The explored nodes are those displayed on the view.\n---@param fn fun(node: Node): boolean\n---@return table|nil\n---@return number\nfunction Explorer:find_node(fn)\n  local node, i = Iterator.builder(self.nodes)\n    :matcher(fn)\n    :recursor(function(node)\n      return node.group_next and { node.group_next } or (node.open and #node.nodes > 0 and node.nodes)\n    end)\n    :iterate()\n  i = view.is_root_folder_visible() and i or i - 1\n  if node and node.explorer.live_filter.filter then\n    i = i + 1\n  end\n  return node, i\nend\n\n---Get all nodes in a line range (inclusive), for visual selection operations.\n---@param start_line integer\n---@param end_line integer\n---@return Node[]\nfunction Explorer:get_nodes_in_range(start_line, end_line)\n  local nodes_by_line = self:get_nodes_by_line(core.get_nodes_starting_line())\n  local nodes = {}\n  for line = start_line, end_line do\n    local node = nodes_by_line[line]\n    if node and node.absolute_path then\n      table.insert(nodes, node)\n    end\n  end\n  return nodes\nend\n\n--- Return visible nodes indexed by line\n---@param line_start number\n---@return table\nfunction Explorer:get_nodes_by_line(line_start)\n  local nodes_by_line = {}\n  local line = line_start\n\n  Iterator.builder(self.nodes)\n    :applier(function(node)\n      if node.group_next then\n        return\n      end\n      nodes_by_line[line] = node\n      line = line + 1\n    end)\n    :recursor(function(node)\n      return node.group_next and { node.group_next } or (node.open and #node.nodes > 0 and node.nodes)\n    end)\n    :iterate()\n\n  return nodes_by_line\nend\n\n---@param node Node\nfunction Explorer:dir_up(node)\n  if not node or node.name == \"..\" then\n    self:change_dir(\"..\")\n  else\n    local cwd = self.absolute_path\n    if cwd == nil then\n      return\n    end\n\n    local newdir = vim.fn.fnamemodify(utils.path_remove_trailing(cwd), \":h\")\n    self:change_dir(newdir)\n    find_file.fn(node.absolute_path)\n  end\nend\n\n---Api.tree.get_nodes\n---@return nvim_tree.api.Node\nfunction Explorer:get_nodes()\n  return self:clone()\nend\n\n---Expand the directory node or the root\n---@param node Node\n---@param expand_opts? nvim_tree.api.node.expand.Opts\nfunction Explorer:expand_all(node, expand_opts)\n  if node then\n    node:expand(expand_opts)\n  else\n    self:expand(expand_opts)\n  end\nend\n\n---Expand the directory node or parent node\n---@param node? Node\n---@param expand_opts? nvim_tree.api.node.expand.Opts\nfunction Explorer:expand_node(node, expand_opts)\n  if not node then\n    return\n  end\n\n  node:expand(expand_opts)\nend\n\n---@private\n---@param new_tabpage integer\n---@return boolean\nfunction Explorer:is_window_event(new_tabpage)\n  local is_event_scope_window = vim.v.event.scope == \"window\" or vim.v.event.changed_window or false\n  return is_event_scope_window and new_tabpage == self.current_tab\nend\n\n---@private\n---@param name string\n---@return string|nil\nfunction Explorer:clean_input_cwd(name)\n  name = vim.fn.fnameescape(name)\n  local cwd = self.absolute_path\n  if cwd == nil then\n    return\n  end\n  local root_parent_cwd = vim.fn.fnamemodify(utils.path_remove_trailing(cwd), \":h\")\n  if name == \"..\" and root_parent_cwd then\n    return vim.fn.expand(root_parent_cwd)\n  else\n    return vim.fn.expand(name)\n  end\nend\n\n---@private\n---@param foldername string\n---@return boolean\nfunction Explorer:prevent_cwd_change(foldername)\n  local is_same_cwd = foldername == self.absolute_path\n  local is_restricted_above = config.actions.change_dir.restrict_above_cwd and foldername < vim.fn.getcwd(-1, -1)\n  return is_same_cwd or is_restricted_above\nend\n\n---@private\n---@return boolean\nfunction Explorer:should_change_dir()\n  return config.actions.change_dir.enable and vim.tbl_isempty(vim.v.event)\nend\n\n---@private\n---@param global boolean\n---@param path string\nfunction Explorer:cd(global, path)\n  vim.cmd((global and \"cd \" or \"lcd \") .. vim.fn.fnameescape(path))\nend\n\n---@private\n---@param foldername string\n---@param should_open_view boolean|nil\n---@param should_init boolean|nil\nfunction Explorer:force_dirchange(foldername, should_open_view, should_init)\n  local profile = log.profile_start(\"change dir %s\", foldername)\n\n  local valid_dir = vim.fn.isdirectory(foldername) == 1 -- prevent problems on non existing dirs\n  if valid_dir then\n    if self:should_change_dir() then\n      self:cd(config.actions.change_dir.global, foldername)\n    end\n\n    if should_init ~= false then\n      core.init(foldername)\n    end\n  end\n\n  if should_open_view then\n    require(\"nvim-tree.lib\").open()\n  else\n    -- TODO #2255\n    -- The call to core.init destroyed this Explorer instance hence we need to fetch the new instance.\n    -- Problem is described at https://github.com/nvim-tree/nvim-tree.lua/pull/3233#issuecomment-3704402527\n    local explorer = core.get_explorer()\n    if explorer then\n      explorer.renderer:draw()\n    end\n  end\n\n  log.profile_end(profile)\nend\n\n---@param input_cwd string\n---@param with_open boolean|nil\nfunction Explorer:change_dir(input_cwd, with_open)\n  local new_tabpage = vim.api.nvim_get_current_tabpage()\n  if self:is_window_event(new_tabpage) then\n    return\n  end\n\n  local foldername = self:clean_input_cwd(input_cwd)\n  if foldername == nil or self:prevent_cwd_change(foldername) then\n    return\n  end\n\n  self.current_tab = new_tabpage\n  self:force_dirchange(foldername, with_open)\nend\n\nfunction Explorer:change_dir_to_node(node)\n  if node.name == \"..\" or node:is(RootNode) then\n    self:change_dir(\"..\")\n  elseif node:is(FileNode) and node.parent ~= nil then\n    self:change_dir(node.parent:last_group_node().absolute_path)\n  else\n    node = node:as(DirectoryNode)\n    if node then\n      self:change_dir(node:last_group_node().absolute_path)\n    end\n  end\nend\n\nfunction Explorer:setup(opts)\n  config = opts\nend\n\nreturn Explorer\n"
  },
  {
    "path": "lua/nvim-tree/explorer/live-filter.lua",
    "content": "local view = require(\"nvim-tree.view\")\nlocal utils = require(\"nvim-tree.utils\")\n\nlocal Class = require(\"nvim-tree.classic\")\nlocal Iterator = require(\"nvim-tree.iterators.node-iterator\")\nlocal DirectoryNode = require(\"nvim-tree.node.directory\")\n\n---@class (exact) LiveFilter: nvim_tree.Class\n---@field explorer Explorer\n---@field prefix string\n---@field always_show_folders boolean\n---@field filter string\nlocal LiveFilter = Class:extend()\n\n---@class LiveFilter\n---@overload fun(args: LiveFilterArgs): LiveFilter\n\n---@class (exact) LiveFilterArgs\n---@field explorer Explorer\n\n---@protected\n---@param args LiveFilterArgs\nfunction LiveFilter:new(args)\n  self.explorer = args.explorer\n  self.prefix = self.explorer.opts.live_filter.prefix\n  self.always_show_folders = self.explorer.opts.live_filter.always_show_folders\n  self.filter = nil\nend\n\n---@param node_ Node?\nlocal function reset_filter(self, node_)\n  node_ = node_ or self.explorer\n\n  if node_ == nil then\n    return\n  end\n\n  local dir_ = node_:as(DirectoryNode)\n  if dir_ then\n    dir_.hidden_stats = vim.tbl_deep_extend(\"force\", dir_.hidden_stats or {}, { live_filter = 0, })\n  end\n\n  Iterator.builder(node_.nodes)\n    :hidden()\n    :applier(function(node)\n      node.hidden = false\n      local dir = node:as(DirectoryNode)\n      if dir then\n        dir.hidden_stats = vim.tbl_deep_extend(\"force\", dir.hidden_stats or {}, { live_filter = 0, })\n      end\n    end)\n    :iterate()\nend\n\nlocal overlay_bufnr = 0\nlocal overlay_winnr = 0\n\nlocal function remove_overlay(self)\n  if view.View.float.enable and view.View.float.quit_on_focus_loss then\n    -- return to normal nvim-tree float behaviour when filter window is closed\n    vim.api.nvim_create_autocmd(\"WinLeave\", {\n      pattern = \"NvimTree_*\",\n      group = vim.api.nvim_create_augroup(\"NvimTree\", { clear = false }),\n      callback = function()\n        if utils.is_nvim_tree_buf(0) then\n          view.close()\n        end\n      end,\n    })\n  end\n\n  vim.api.nvim_win_close(overlay_winnr, true)\n  vim.api.nvim_buf_delete(overlay_bufnr, { force = true })\n  overlay_bufnr = 0\n  overlay_winnr = 0\n\n  if self.filter == \"\" then\n    self:clear_filter()\n  end\nend\n\n---@param node Node\n---@return boolean\nlocal function matches(self, node)\n  if not self.explorer.filters.enabled then\n    return true\n  end\n\n  local path = node.absolute_path\n  local name = vim.fn.fnamemodify(path, \":t\")\n  return vim.regex(self.filter):match_str(name) ~= nil\nend\n\n---@param node_ DirectoryNode?\nfunction LiveFilter:apply_filter(node_)\n  if not self.filter or self.filter == \"\" then\n    reset_filter(self, node_)\n    return\n  end\n\n  -- this iterator cannot yet be refactored with the Iterator module\n  -- since the node mapper is based on its children\n  local function iterate(node)\n    local filtered_nodes = 0\n    local nodes = node.group_next and { node.group_next } or node.nodes\n\n    node.hidden_stats = vim.tbl_deep_extend(\"force\", node.hidden_stats or {}, {\n      live_filter = 0,\n    })\n\n    if nodes then\n      for _, n in pairs(nodes) do\n        iterate(n)\n        if n.hidden then\n          filtered_nodes = filtered_nodes + 1\n        end\n      end\n    end\n\n    node.hidden_stats.live_filter = filtered_nodes\n\n    local has_nodes = nodes and (self.always_show_folders or #nodes > filtered_nodes)\n    local ok, is_match = pcall(matches, self, node)\n    node.hidden = not (has_nodes or (ok and is_match))\n  end\n\n  iterate(node_ or self.explorer)\nend\n\nlocal function record_char(self)\n  vim.schedule(function()\n    self.filter = vim.api.nvim_buf_get_lines(overlay_bufnr, 0, -1, false)[1]\n    self:apply_filter()\n    self.explorer.renderer:draw()\n  end)\nend\n\nlocal function configure_buffer_overlay(self)\n  overlay_bufnr = vim.api.nvim_create_buf(false, true)\n\n  vim.api.nvim_buf_attach(overlay_bufnr, true, {\n    on_lines = function()\n      return record_char(self)\n    end,\n  })\n\n  vim.api.nvim_create_autocmd(\"InsertLeave\", {\n    callback = function()\n      return remove_overlay(self)\n    end,\n    once = true,\n  })\n\n  vim.api.nvim_buf_set_keymap(overlay_bufnr, \"i\", \"<CR>\", \"<cmd>stopinsert<CR>\", {})\nend\n\n---@return integer\nlocal function calculate_overlay_win_width(self)\n  local wininfo = vim.fn.getwininfo(view.get_winnr())[1]\n\n  if wininfo then\n    return wininfo.width - wininfo.textoff - #self.prefix\n  end\n\n  return 20\nend\n\nlocal function create_overlay(self)\n  if view.View.float.enable then\n    -- don't close nvim-tree float when focus is changed to filter window\n    vim.api.nvim_clear_autocmds({\n      event   = \"WinLeave\",\n      pattern = \"NvimTree_*\",\n      group   = vim.api.nvim_create_augroup(\"NvimTree\", { clear = false }),\n    })\n  end\n\n  configure_buffer_overlay(self)\n  overlay_winnr = vim.api.nvim_open_win(overlay_bufnr, true, {\n    col      = 1,\n    row      = 0,\n    relative = \"cursor\",\n    width    = calculate_overlay_win_width(self),\n    height   = 1,\n    border   = \"none\",\n    style    = \"minimal\",\n  })\n\n  if vim.fn.has(\"nvim-0.10\") == 1 then\n    vim.api.nvim_set_option_value(\"modifiable\", true,             { buf = overlay_bufnr })\n    vim.api.nvim_set_option_value(\"filetype\",   \"NvimTreeFilter\", { buf = overlay_bufnr })\n  else\n    vim.api.nvim_buf_set_option(overlay_bufnr, \"modifiable\", true) ---@diagnostic disable-line: deprecated\n    vim.api.nvim_buf_set_option(overlay_bufnr, \"filetype\",   \"NvimTreeFilter\") ---@diagnostic disable-line: deprecated\n  end\n\n  vim.api.nvim_buf_set_lines(overlay_bufnr, 0, -1, false, { self.filter })\n  vim.cmd(\"startinsert\")\n  vim.api.nvim_win_set_cursor(overlay_winnr, { 1, #self.filter + 1 })\nend\n\nfunction LiveFilter:start_filtering()\n  view.View.live_filter.prev_focused_node = self.explorer:get_node_at_cursor()\n  self.filter = self.filter or \"\"\n\n  self.explorer.renderer:draw()\n  local row = require(\"nvim-tree.core\").get_nodes_starting_line() - 1\n  local col = #self.prefix > 0 and #self.prefix - 1 or 1\n  view.set_cursor({ row, col })\n  -- needs scheduling to let the cursor move before initializing the window\n  vim.schedule(function()\n    return create_overlay(self)\n  end)\nend\n\nfunction LiveFilter:clear_filter()\n  local node = self.explorer:get_node_at_cursor()\n  local last_node = view.View.live_filter.prev_focused_node\n\n  self.filter = nil\n  reset_filter(self)\n  self.explorer.renderer:draw()\n\n  if node then\n    self.explorer:focus_node_or_parent(node)\n  elseif last_node then\n    self.explorer:focus_node_or_parent(last_node)\n  end\nend\n\nreturn LiveFilter\n"
  },
  {
    "path": "lua/nvim-tree/explorer/sorter.lua",
    "content": "local Class = require(\"nvim-tree.classic\")\nlocal DirectoryNode = require(\"nvim-tree.node.directory\")\n\n---@alias SorterType \"name\" | \"case_sensitive\" | \"modification_time\" | \"extension\" | \"suffix\" | \"filetype\"\n---@alias SorterComparator fun(self: Sorter, a: Node, b: Node): boolean?\n\n---@alias SorterUser fun(nodes: Node[]): SorterType?\n\n---@class (exact) Sorter: nvim_tree.Class\n---@field private explorer Explorer\nlocal Sorter = Class:extend()\n\n---@class Sorter\n---@overload fun(args: SorterArgs): Sorter\n\n---@class (exact) SorterArgs\n---@field explorer Explorer\n\n---@protected\n---@param args SorterArgs\nfunction Sorter:new(args)\n  self.explorer = args.explorer\nend\n\n---Create a shallow copy of a portion of a list.\n---@param t table\n---@param first integer First index, inclusive\n---@param last integer Last index, inclusive\n---@return table\nlocal function tbl_slice(t, first, last)\n  local slice = {}\n  for i = first, last or #t, 1 do\n    table.insert(slice, t[i])\n  end\n\n  return slice\nend\n\n---Evaluate folders_first and sort.files_first returning nil when no order is necessary\n---@private\n---@type SorterComparator\nfunction Sorter:folders_or_files_first(a, b)\n  if not (self.explorer.opts.sort.folders_first or self.explorer.opts.sort.files_first) then\n    return nil\n  end\n\n  if not a:is(DirectoryNode) and b:is(DirectoryNode) then\n    -- file <> folder\n    return self.explorer.opts.sort.files_first\n  elseif a:is(DirectoryNode) and not b:is(DirectoryNode) then\n    -- folder <> file\n    return not self.explorer.opts.sort.files_first\n  end\n\n  return nil\nend\n\n---@private\n---@param t Node[]\n---@param first number\n---@param mid number\n---@param last number\n---@param comparator SorterComparator\nfunction Sorter:merge(t, first, mid, last, comparator)\n  local n1 = mid - first + 1\n  local n2 = last - mid\n  local ls = tbl_slice(t, first, mid)\n  local rs = tbl_slice(t, mid + 1, last)\n  local i = 1\n  local j = 1\n  local k = first\n\n  while i <= n1 and j <= n2 do\n    if comparator(self, ls[i], rs[j]) then\n      t[k] = ls[i]\n      i = i + 1\n    else\n      t[k] = rs[j]\n      j = j + 1\n    end\n    k = k + 1\n  end\n\n  while i <= n1 do\n    t[k] = ls[i]\n    i = i + 1\n    k = k + 1\n  end\n\n  while j <= n2 do\n    t[k] = rs[j]\n    j = j + 1\n    k = k + 1\n  end\nend\n\n---@private\n---@param t Node[]\n---@param first number\n---@param last number\n---@param comparator SorterComparator\nfunction Sorter:split_merge(t, first, last, comparator)\n  if (last - first) < 1 then\n    return\n  end\n\n  local mid = math.floor((first + last) / 2)\n\n  self:split_merge(t, first,   mid,  comparator)\n  self:split_merge(t, mid + 1, last, comparator)\n  self:merge(t, first, mid, last, comparator)\nend\n\n---Perform a merge sort using sorter option.\n---@param t Node[]\nfunction Sorter:sort(t)\n  if self[self.explorer.opts.sort.sorter] then\n    self:split_merge(t, 1, #t, self[self.explorer.opts.sort.sorter])\n  elseif type(self.explorer.opts.sort.sorter) == \"function\" then\n    local t_user = {}\n    local origin_index = {}\n\n    for _, n in ipairs(t) do\n      table.insert(t_user, {\n        absolute_path = n.absolute_path,\n        executable    = n.executable,\n        extension     = n.extension,\n        filetype      = vim.filetype.match({ filename = n.name }),\n        link_to       = n.link_to,\n        name          = n.name,\n        type          = n.type,\n      })\n      table.insert(origin_index, n)\n    end\n\n    -- user may return a SorterType\n    local ret = self.explorer.opts.sort.sorter(t_user)\n    if self[ret] then\n      self:split_merge(t, 1, #t, self[ret])\n      return\n    end\n\n    -- do merge sort for prevent memory exceed\n    local user_index = {}\n    for i, v in ipairs(t_user) do\n      if type(v.absolute_path) == \"string\" and user_index[v.absolute_path] == nil then\n        user_index[v.absolute_path] = i\n      end\n    end\n\n    -- if missing value found, then using origin_index\n    local mini_comparator = function(_, a, b)\n      local a_index = user_index[a.absolute_path] or origin_index[a.absolute_path]\n      local b_index = user_index[b.absolute_path] or origin_index[b.absolute_path]\n\n      if type(a_index) == \"number\" and type(b_index) == \"number\" then\n        return a_index <= b_index\n      end\n      return (a_index or 0) <= (b_index or 0)\n    end\n\n    self:split_merge(t, 1, #t, mini_comparator) -- sort by user order\n  end\nend\n\n---@private\n---@param a Node\n---@param b Node\n---@param ignore_case boolean\n---@return boolean\nfunction Sorter:name_case(a, b, ignore_case)\n  if not (a and b) then\n    return true\n  end\n\n  local early_return = self:folders_or_files_first(a, b)\n  if early_return ~= nil then\n    return early_return\n  end\n\n  if ignore_case then\n    return a.name:lower() <= b.name:lower()\n  else\n    return a.name <= b.name\n  end\nend\n\n---@private\n---@type SorterComparator\nfunction Sorter:case_sensitive(a, b)\n  return self:name_case(a, b, false)\nend\n\n---@private\n---@type SorterComparator\nfunction Sorter:name(a, b)\n  return self:name_case(a, b, true)\nend\n\n---@private\n---@type SorterComparator\nfunction Sorter:modification_time(a, b)\n  if not (a and b) then\n    return true\n  end\n\n  local early_return = self:folders_or_files_first(a, b)\n  if early_return ~= nil then\n    return early_return\n  end\n\n  local last_modified_a = 0\n  local last_modified_b = 0\n\n  if a.fs_stat ~= nil then\n    last_modified_a = a.fs_stat.mtime.sec\n  end\n\n  if b.fs_stat ~= nil then\n    last_modified_b = b.fs_stat.mtime.sec\n  end\n\n  return last_modified_b <= last_modified_a\nend\n\n---@private\n---@type SorterComparator\nfunction Sorter:suffix(a, b)\n  if not (a and b) then\n    return true\n  end\n\n  -- directories go first\n  local early_return = self:folders_or_files_first(a, b)\n  if early_return ~= nil then\n    return early_return\n  elseif a.nodes and b.nodes then\n    return self:name(a, b)\n  end\n\n  -- dotfiles go second\n  if a.name:sub(1, 1) == \".\" and b.name:sub(1, 1) ~= \".\" then\n    return true\n  elseif a.name:sub(1, 1) ~= \".\" and b.name:sub(1, 1) == \".\" then\n    return false\n  elseif a.name:sub(1, 1) == \".\" and b.name:sub(1, 1) == \".\" then\n    return self:name(a, b)\n  end\n\n  -- unsuffixed go third\n  local a_suffix_ndx = a.name:find(\"%.%w+$\")\n  local b_suffix_ndx = b.name:find(\"%.%w+$\")\n\n  if not a_suffix_ndx and b_suffix_ndx then\n    return true\n  elseif a_suffix_ndx and not b_suffix_ndx then\n    return false\n  elseif not (a_suffix_ndx and b_suffix_ndx) then\n    return self:name(a, b)\n  end\n\n  -- finally, compare by suffixes\n  local a_suffix = a.name:sub(a_suffix_ndx)\n  local b_suffix = b.name:sub(b_suffix_ndx)\n\n  if a_suffix and not b_suffix then\n    return true\n  elseif not a_suffix and b_suffix then\n    return false\n  elseif a_suffix:lower() == b_suffix:lower() then\n    return self:name(a, b)\n  end\n\n  return a_suffix:lower() < b_suffix:lower()\nend\n\n---@private\n---@type SorterComparator\nfunction Sorter:extension(a, b)\n  if not (a and b) then\n    return true\n  end\n\n  local early_return = self:folders_or_files_first(a, b)\n  if early_return ~= nil then\n    return early_return\n  end\n\n  if a.extension and not b.extension then\n    return true\n  elseif not a.extension and b.extension then\n    return false\n  end\n\n  local a_ext = (a.extension or \"\"):lower()\n  local b_ext = (b.extension or \"\"):lower()\n  if a_ext == b_ext then\n    return self:name(a, b)\n  end\n\n  return a_ext < b_ext\nend\n\n---@private\n---@type SorterComparator\nfunction Sorter:filetype(a, b)\n  local a_ft = vim.filetype.match({ filename = a.name })\n  local b_ft = vim.filetype.match({ filename = b.name })\n\n  -- directories first\n  local early_return = self:folders_or_files_first(a, b)\n  if early_return ~= nil then\n    return early_return\n  end\n\n  -- one is nil, the other wins\n  if a_ft and not b_ft then\n    return true\n  elseif not a_ft and b_ft then\n    return false\n  end\n\n  -- same filetype or both nil, sort by name\n  if a_ft == b_ft then\n    return self:name(a, b)\n  end\n\n  return a_ft < b_ft\nend\n\nreturn Sorter\n"
  },
  {
    "path": "lua/nvim-tree/explorer/watch.lua",
    "content": "local log = require(\"nvim-tree.log\")\nlocal git = require(\"nvim-tree.git\")\nlocal utils = require(\"nvim-tree.utils\")\nlocal notify = require(\"nvim-tree.notify\")\nlocal Watcher = require(\"nvim-tree.watcher\").Watcher\n\nlocal M = {\n  config = {},\n  uid = 0,\n}\n\n---@param path string\n---@return boolean\nlocal function is_git(path)\n  -- If $GIT_DIR is set, consider its value to be equivalent to '.git'.\n  -- Expand $GIT_DIR (and `path`) to a full path (see :help filename-modifiers), since\n  -- it's possible to set it to a relative path. We want to make our best\n  -- effort to expand that to a valid absolute path.\n  if vim.fn.fnamemodify(path, \":p\") == vim.fn.fnamemodify(vim.env.GIT_DIR, \":p\") then\n    return true\n  elseif vim.fn.fnamemodify(path, \":t\") == \".git\" then\n    return true\n  else\n    return false\n  end\nend\n\nlocal IGNORED_PATHS = {\n  -- disable watchers on kernel filesystems\n  -- which have a lot of unwanted events\n  \"/sys\",\n  \"/proc\",\n  \"/dev\",\n}\n\n---@param path string\n---@return boolean\nlocal function is_folder_ignored(path)\n  for _, folder in ipairs(IGNORED_PATHS) do\n    if vim.startswith(path, folder) then\n      return true\n    end\n  end\n\n  if type(M.config.filesystem_watchers.ignore_dirs) == \"table\" then\n    for _, ignore_dir in ipairs(M.config.filesystem_watchers.ignore_dirs) do\n      if utils.is_windows then\n        ignore_dir = ignore_dir:gsub(\"/\", \"\\\\\\\\\") or ignore_dir\n      end\n\n      if vim.fn.match(path, ignore_dir) ~= -1 then\n        return true\n      end\n    end\n  elseif type(M.config.filesystem_watchers.ignore_dirs) == \"function\" then\n    return M.config.filesystem_watchers.ignore_dirs(path)\n  end\n\n  return false\nend\n\n---@param node DirectoryNode\n---@return Watcher|nil\nfunction M.create_watcher(node)\n  if not M.config.filesystem_watchers.enable or type(node) ~= \"table\" then\n    return nil\n  end\n\n  local path = node.link_to or node.absolute_path\n  if is_git(path) or is_folder_ignored(path) then\n    return nil\n  end\n\n  ---@param watcher Watcher\n  local function callback(watcher)\n    log.line(\"watcher\", \"node event scheduled refresh %s\", watcher.data.context)\n\n    -- event is awaiting debouncing and handling\n    watcher.data.outstanding_events = watcher.data.outstanding_events + 1\n\n    -- disable watcher when outstanding exceeds max\n    if M.config.filesystem_watchers.max_events > 0 and watcher.data.outstanding_events > M.config.filesystem_watchers.max_events then\n      notify.error(string.format(\n        \"Observed %d consecutive file system events with interval < %dms, exceeding filesystem_watchers.max_events=%s. Disabling watcher for directory '%s'. Consider adding this directory to filesystem_watchers.ignore_dirs\",\n        watcher.data.outstanding_events,\n        M.config.filesystem_watchers.debounce_delay,\n        M.config.filesystem_watchers.max_events,\n        node.absolute_path\n      ))\n      node:destroy_watcher()\n    end\n\n    utils.debounce(watcher.data.context, M.config.filesystem_watchers.debounce_delay, function()\n      if watcher.destroyed then\n        return\n      end\n\n      -- event has been handled\n      watcher.data.outstanding_events = 0\n\n      if node.link_to then\n        log.line(\"watcher\", \"node event executing refresh '%s' -> '%s'\", node.link_to, node.absolute_path)\n      else\n        log.line(\"watcher\", \"node event executing refresh '%s'\", node.absolute_path)\n      end\n      git.refresh_dir(node)\n    end)\n  end\n\n  M.uid = M.uid + 1\n  return Watcher:create({\n    path = path,\n    callback = callback,\n    data = {\n      context = \"explorer:watch:\" .. path .. \":\" .. M.uid,\n      outstanding_events = 0, -- unprocessed events that have not been debounced\n    }\n  })\nend\n\nfunction M.setup(opts)\n  M.config.filesystem_watchers = opts.filesystem_watchers\n  M.uid = 0\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/git/init.lua",
    "content": "local log = require(\"nvim-tree.log\")\nlocal utils = require(\"nvim-tree.utils\")\nlocal git_utils = require(\"nvim-tree.git.utils\")\n\nlocal GitRunner = require(\"nvim-tree.git.runner\")\nlocal Watcher = require(\"nvim-tree.watcher\").Watcher\nlocal Iterator = require(\"nvim-tree.iterators.node-iterator\")\nlocal DirectoryNode = require(\"nvim-tree.node.directory\")\n\n-- Git short-format status\n---@alias nvim_tree.git.PathXY table<string, nvim_tree.git.XY>\n\n-- Git short-format statuses\n---@alias nvim_tree.git.PathXYs table<string, nvim_tree.git.XY[]>\n\n---Git state for an entire repo\n---@class (exact) nvim_tree.git.Project\n---@field files nvim_tree.git.ProjectFiles?\n---@field dirs nvim_tree.git.ProjectDirs?\n---@field watcher Watcher?\n\n---@alias nvim_tree.git.ProjectFiles nvim_tree.git.PathXY\n---@alias nvim_tree.git.ProjectDirs table<\"direct\" | \"indirect\", nvim_tree.git.PathXYs>\n\nlocal M = {\n  config = {},\n\n  ---all projects keyed by toplevel\n  ---@type table<string, nvim_tree.git.Project>\n  _projects_by_toplevel = {},\n\n  ---index of paths inside toplevels, false when not inside a project\n  ---@type table<string, string|false>\n  _toplevels_by_path = {},\n\n  -- git dirs by toplevel\n  ---@type table<string, string>\n  _git_dirs_by_toplevel = {},\n}\n\n-- Files under .git that should result in a reload when changed.\n-- Utilities (like watchman) can also write to this directory (often) and aren't useful for us.\nlocal WATCHED_FILES = {\n  \"FETCH_HEAD\", -- remote ref\n  \"HEAD\",       -- local ref\n  \"HEAD.lock\",  -- HEAD will not always be updated e.g. revert\n  \"config\",     -- user config\n  \"index\",      -- staging area\n}\n\n---@param toplevel string|nil\n---@param path string|nil\n---@param project nvim_tree.git.Project\n---@param project_files nvim_tree.git.ProjectFiles?\nlocal function reload_git_project(toplevel, path, project, project_files)\n  if path then\n    for p in pairs(project.files) do\n      if p:find(path, 1, true) == 1 then\n        project.files[p] = nil\n      end\n    end\n    project.files = vim.tbl_deep_extend(\"force\", project.files, project_files)\n  else\n    project.files = project_files or {}\n  end\n\n  project.dirs = git_utils.project_files_to_project_dirs(project.files, toplevel)\nend\n\n--- Is this path in a known ignored directory?\n---@param path string\n---@param project nvim_tree.git.Project\n---@return boolean\nlocal function path_ignored_in_project(path, project)\n  if not path or not project then\n    return false\n  end\n\n  if project.files then\n    for p, xy in pairs(project.files) do\n      if xy == \"!!\" and vim.startswith(path, p) then\n        return true\n      end\n    end\n  end\n  return false\nend\n\n---@return nvim_tree.git.Project[] maybe empty\nfunction M.reload_all_projects()\n  if not M.config.git.enable then\n    return {}\n  end\n\n  for toplevel in pairs(M._projects_by_toplevel) do\n    M.reload_project(toplevel)\n  end\n\n  return M._projects_by_toplevel\nend\n\n--- Reload one project. Does nothing when no project or path is ignored\n---@param toplevel string?\n---@param path string? optional path to update only\n---@param callback function?\nfunction M.reload_project(toplevel, path, callback)\n  local project = M._projects_by_toplevel[toplevel] --[[@as nvim_tree.git.Project]]\n\n  if not toplevel or not project or not M.config.git.enable then\n    if callback then\n      callback()\n    end\n    return\n  end\n\n  if path and (path:find(toplevel, 1, true) ~= 1 or path_ignored_in_project(path, project)) then\n    if callback then\n      callback()\n    end\n    return\n  end\n\n  ---@type GitRunnerArgs\n  local args = {\n    toplevel       = toplevel,\n    path           = path,\n    list_untracked = git_utils.should_show_untracked(toplevel),\n    list_ignored   = true,\n    timeout        = M.config.git.timeout,\n  }\n\n  if callback then\n    ---@param path_xy nvim_tree.git.PathXY\n    args.callback = function(path_xy)\n      reload_git_project(toplevel, path, project, path_xy)\n      callback()\n    end\n    GitRunner:run(args)\n  else\n    -- TODO #1974 use callback once async/await is available\n    reload_git_project(toplevel, path, project, GitRunner:run(args))\n  end\nend\n\n--- Retrieve a known project\n---@param toplevel string?\n---@return nvim_tree.git.Project? project\nfunction M.get_project(toplevel)\n  return M._projects_by_toplevel[toplevel]\nend\n\n--- Retrieve the toplevel for a path. nil on:\n---  git disabled\n---  not part of a project\n---  not a directory\n---  path in git.disable_for_dirs\n---@param path string absolute\n---@return string|nil\nfunction M.get_toplevel(path)\n  if not path then\n    return nil\n  end\n\n  if not M.config.git.enable then\n    return nil\n  end\n\n  local tl = M._toplevels_by_path[path]\n  if tl then\n    return tl\n  elseif tl == false then\n    return nil\n  end\n\n  local stat, _ = vim.loop.fs_stat(path)\n  if not stat or stat.type ~= \"directory\" then\n    return nil\n  end\n\n  -- short-circuit any known ignored paths\n  for root, project in pairs(M._projects_by_toplevel) do\n    if project and path_ignored_in_project(path, project) then\n      M._toplevels_by_path[path] = root\n      return root\n    end\n  end\n\n  -- attempt to fetch toplevel, cache if untracked\n  local toplevel, git_dir = git_utils.get_toplevel(path)\n  if not toplevel or not git_dir then\n    M._toplevels_by_path[path] = false\n    return nil\n  end\n  local toplevel_norm = vim.fn.fnamemodify(toplevel, \":p\")\n\n  -- ignore disabled paths\n  if type(M.config.git.disable_for_dirs) == \"table\" then\n    for _, disabled_for_dir in ipairs(M.config.git.disable_for_dirs) do\n      local disabled_norm = vim.fn.fnamemodify(disabled_for_dir, \":p\")\n      if toplevel_norm == disabled_norm then\n        return nil\n      end\n    end\n  elseif type(M.config.git.disable_for_dirs) == \"function\" then\n    if M.config.git.disable_for_dirs(toplevel_norm) then\n      return nil\n    end\n  end\n\n  M._toplevels_by_path[path] = toplevel\n\n  M._git_dirs_by_toplevel[toplevel] = git_dir\n\n  toplevel = M._toplevels_by_path[path]\n  if toplevel == false then\n    return nil\n  else\n    return toplevel\n  end\nend\n\nlocal function reload_tree_at(toplevel)\n  if not M.config.git.enable or not toplevel then\n    return nil\n  end\n\n  log.line(\"watcher\", \"git event executing '%s'\", toplevel)\n\n  local explorer = require(\"nvim-tree.core\").get_explorer()\n  if not explorer then\n    return nil\n  end\n\n  local root_node = explorer:get_node_from_path(toplevel)\n  if not root_node then\n    return\n  end\n\n  M.reload_project(toplevel, nil, function()\n    local project = M.get_project(toplevel)\n\n    Iterator.builder(root_node.nodes)\n      :hidden()\n      :applier(function(node)\n        local parent_ignored = node.parent and node.parent:is_git_ignored() or false\n        node:update_git_status(parent_ignored, project)\n      end)\n      :recursor(function(node)\n        return node.nodes and #node.nodes > 0 and node.nodes\n      end)\n      :iterate()\n\n    explorer.renderer:draw()\n  end)\nend\n\n--- Load the project status for a path. Does nothing when no toplevel for path.\n--- Only fetches project status when unknown, otherwise returns existing.\n---@param path string absolute\n---@return nvim_tree.git.Project maybe empty\nfunction M.load_project(path)\n  if not M.config.git.enable then\n    return {}\n  end\n\n  local toplevel = M.get_toplevel(path)\n  if not toplevel then\n    M._toplevels_by_path[path] = false\n    return {}\n  end\n\n  local project = M._projects_by_toplevel[toplevel]\n  if project then\n    return project\n  end\n\n  local path_xys = GitRunner:run({\n    toplevel       = toplevel,\n    list_untracked = git_utils.should_show_untracked(toplevel),\n    list_ignored   = true,\n    timeout        = M.config.git.timeout,\n  })\n\n  local watcher = nil\n  if M.config.filesystem_watchers.enable then\n    log.line(\"watcher\", \"git start\")\n\n    ---@param w Watcher\n    local callback = function(w)\n      log.line(\"watcher\", \"git event scheduled '%s'\", w.data.toplevel)\n      utils.debounce(\"git:watcher:\" .. w.data.toplevel, M.config.filesystem_watchers.debounce_delay, function()\n        if w.destroyed then\n          return\n        end\n        reload_tree_at(w.data.toplevel)\n      end)\n    end\n\n    local git_dir = vim.env.GIT_DIR or M._git_dirs_by_toplevel[toplevel] or utils.path_join({ toplevel, \".git\" })\n    watcher = Watcher:create({\n      path     = git_dir,\n      files    = WATCHED_FILES,\n      callback = callback,\n      data     = {\n        toplevel = toplevel,\n      }\n    })\n  end\n\n  if path_xys then\n    M._projects_by_toplevel[toplevel] = {\n      files   = path_xys,\n      dirs    = git_utils.project_files_to_project_dirs(path_xys, toplevel),\n      watcher = watcher,\n    }\n    return M._projects_by_toplevel[toplevel]\n  else\n    M._toplevels_by_path[path] = false\n    return {}\n  end\nend\n\n---@param dir DirectoryNode\n---@param project nvim_tree.git.Project?\n---@param root string?\nfunction M.update_parent_projects(dir, project, root)\n  while project and dir do\n    -- step up to the containing project\n    if dir.absolute_path == root then\n      -- stop at the top of the tree\n      if not dir.parent then\n        break\n      end\n\n      root = M.get_toplevel(dir.parent.absolute_path)\n\n      -- stop when no more projects\n      if not root then\n        break\n      end\n\n      -- update the containing project\n      project = M.get_project(root)\n      M.reload_project(root, dir.absolute_path, nil)\n    end\n\n    -- update status\n    dir:update_git_status(dir.parent and dir.parent:is_git_ignored() or false, project)\n\n    -- maybe parent\n    dir = dir.parent\n  end\nend\n\n---Refresh contents and git status for a single directory\n---@param dir DirectoryNode\nfunction M.refresh_dir(dir)\n  local node = dir:get_parent_of_group() or dir\n  local toplevel = M.get_toplevel(dir.absolute_path)\n\n  M.reload_project(toplevel, dir.absolute_path, function()\n    local project = M.get_project(toplevel) or {}\n\n    dir.explorer:reload(node, project)\n\n    M.update_parent_projects(dir, project, toplevel)\n\n    dir.explorer.renderer:draw()\n  end)\nend\n\n---@param dir DirectoryNode?\n---@param projects nvim_tree.git.Project[]\nfunction M.reload_node_status(dir, projects)\n  dir = dir and dir:as(DirectoryNode)\n  if not dir or #dir.nodes == 0 then\n    return\n  end\n\n  local toplevel = M.get_toplevel(dir.absolute_path)\n  local project = projects[toplevel] or {}\n  for _, node in ipairs(dir.nodes) do\n    node:update_git_status(dir:is_git_ignored(), project)\n    M.reload_node_status(node:as(DirectoryNode), projects)\n  end\nend\n\nfunction M.purge_state()\n  log.line(\"git\", \"purge_state\")\n\n  for _, project in pairs(M._projects_by_toplevel) do\n    if project.watcher then\n      project.watcher:destroy()\n    end\n  end\n\n  M._projects_by_toplevel = {}\n  M._toplevels_by_path = {}\n  M._git_dirs_by_toplevel = {}\nend\n\n--- Disable git integration permanently\nfunction M.disable_git_integration()\n  log.line(\"git\", \"disabling git integration\")\n  M.purge_state()\n  M.config.git.enable = false\nend\n\nfunction M.setup(opts)\n  M.config.git = opts.git\n  M.config.filesystem_watchers = opts.filesystem_watchers\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/git/runner.lua",
    "content": "local log = require(\"nvim-tree.log\")\nlocal utils = require(\"nvim-tree.utils\")\nlocal notify = require(\"nvim-tree.notify\")\n\nlocal Class = require(\"nvim-tree.classic\")\n\n---@class (exact) GitRunner: nvim_tree.Class\n---@field private toplevel string absolute path\n---@field private path string? absolute path\n---@field private list_untracked boolean\n---@field private list_ignored boolean\n---@field private timeout integer\n---@field private callback fun(path_xy: nvim_tree.git.PathXY)?\n---@field private path_xy nvim_tree.git.PathXY\n---@field private rc integer? -- -1 indicates timeout\nlocal GitRunner = Class:extend()\n\n---@class GitRunner\n---@overload fun(args: GitRunnerArgs): GitRunner\n\n---@class (exact) GitRunnerArgs\n---@field toplevel string absolute path\n---@field path string? absolute path\n---@field list_untracked boolean\n---@field list_ignored boolean\n---@field timeout integer\n---@field callback fun(path_xy: nvim_tree.git.PathXY)?\n\nlocal timeouts = 0\nlocal MAX_TIMEOUTS = 5\n\n---@protected\n---@param args GitRunnerArgs\nfunction GitRunner:new(args)\n  self.toplevel       = args.toplevel\n  self.path           = args.path\n  self.list_untracked = args.list_untracked\n  self.list_ignored   = args.list_ignored\n  self.timeout        = args.timeout\n  self.callback       = args.callback\n\n  self.path_xy        = {}\n  self.rc             = nil\nend\n\n---@private\n---@param status string\n---@param path string|nil\nfunction GitRunner:parse_status_output(status, path)\n  if not path then\n    return\n  end\n\n  -- replacing slashes if on windows\n  if vim.fn.has(\"win32\") == 1 then\n    path = path:gsub(\"/\", \"\\\\\")\n  end\n  if #status > 0 and #path > 0 then\n    self.path_xy[utils.path_remove_trailing(utils.path_join({ self.toplevel, path }))] = status\n  end\nend\n\n---@private\n---@param prev_output string\n---@param incoming string\n---@return string\nfunction GitRunner:handle_incoming_data(prev_output, incoming)\n  if incoming and utils.str_find(incoming, \"\\n\") then\n    local prev = prev_output .. incoming\n    local i = 1\n    local skip_next_line = false\n    for line in prev:gmatch(\"[^\\n]*\\n\") do\n      if skip_next_line then\n        skip_next_line = false\n      else\n        local status = line:sub(1, 2)\n        local path = line:sub(4, -2)\n        if utils.str_find(status, \"R\") then\n          -- skip next line if it is a rename entry\n          skip_next_line = true\n        end\n        self:parse_status_output(status, path)\n      end\n      i = i + #line\n    end\n\n    return prev:sub(i, -1)\n  end\n\n  if incoming then\n    return prev_output .. incoming\n  end\n\n  for line in prev_output:gmatch(\"[^\\n]*\\n\") do\n    self:parse_status_output(line)\n  end\n\n  return \"\"\nend\n\n---@private\n---@param stdout_handle uv.uv_pipe_t\n---@param stderr_handle uv.uv_pipe_t\n---@return uv.spawn.options\nfunction GitRunner:get_spawn_options(stdout_handle, stderr_handle)\n  local untracked = self.list_untracked and \"-u\" or nil\n  local ignored = (self.list_untracked and self.list_ignored) and \"--ignored=matching\" or \"--ignored=no\"\n  return {\n    args  = { \"--no-optional-locks\", \"status\", \"--porcelain=v1\", \"-z\", ignored, untracked, self.path },\n    cwd   = self.toplevel,\n    stdio = { nil, stdout_handle, stderr_handle },\n  }\nend\n\n---@private\n---@param output string\nfunction GitRunner:log_raw_output(output)\n  if log.enabled(\"git\") and output and type(output) == \"string\" then\n    log.raw(\"git\", \"%s\", output)\n    log.line(\"git\", \"done\")\n  end\nend\n\n---@private\n---@param callback function|nil\nfunction GitRunner:run_git_job(callback)\n  local handle, pid\n  local stdout = vim.loop.new_pipe(false)\n  local stderr = vim.loop.new_pipe(false)\n  local timer = vim.loop.new_timer()\n\n  if stdout == nil or stderr == nil or timer == nil then\n    return\n  end\n\n  local function on_finish(rc)\n    self.rc = rc or 0\n    if timer:is_closing() or stdout:is_closing() or stderr:is_closing() or (handle and handle:is_closing()) then\n      if callback then\n        callback()\n      end\n      return\n    end\n    timer:stop()\n    timer:close()\n    stdout:read_stop()\n    stderr:read_stop()\n    stdout:close()\n    stderr:close()\n\n    -- don't close the handle when killing as it will leave a zombie\n    if rc == -1 then\n      pcall(vim.loop.kill, pid, \"sigkill\")\n    elseif handle then\n      handle:close()\n    end\n\n    if callback then\n      callback()\n    end\n  end\n\n  local spawn_options = self:get_spawn_options(stdout, stderr)\n  log.line(\"git\", \"running job with timeout %dms\", self.timeout)\n  log.line(\"git\", \"git %s\",                        table.concat(utils.array_remove_nils(spawn_options.args), \" \"))\n\n  handle, pid = vim.loop.spawn(\n    \"git\",\n    spawn_options,\n    vim.schedule_wrap(function(rc)\n      on_finish(rc)\n    end)\n  )\n\n  timer:start(\n    self.timeout,\n    0,\n    vim.schedule_wrap(function()\n      on_finish(-1)\n    end)\n  )\n\n  local output_leftover = \"\"\n  local function manage_stdout(err, data)\n    if err then\n      return\n    end\n    if data then\n      data = data:gsub(\"%z\", \"\\n\")\n    end\n    self:log_raw_output(data)\n    output_leftover = self:handle_incoming_data(output_leftover, data)\n  end\n\n  local function manage_stderr(_, data)\n    self:log_raw_output(data)\n  end\n\n  vim.loop.read_start(stdout, vim.schedule_wrap(manage_stdout))\n  vim.loop.read_start(stderr, vim.schedule_wrap(manage_stderr))\nend\n\n---@private\nfunction GitRunner:wait()\n  local function is_done()\n    return self.rc ~= nil\n  end\n\n  while not vim.wait(30, is_done) do\n  end\nend\n\n---@private\nfunction GitRunner:finalise()\n  if self.rc == -1 then\n    log.line(\"git\", \"job timed out  %s %s\", self.toplevel, self.path)\n    timeouts = timeouts + 1\n    if timeouts == MAX_TIMEOUTS then\n      notify.warn(string.format(\"%d git jobs have timed out after git.timeout %dms, disabling git integration.\", timeouts,\n        self.timeout))\n      require(\"nvim-tree.git\").disable_git_integration()\n    end\n  elseif self.rc ~= 0 then\n    log.line(\"git\", \"job fail rc %d %s %s\", self.rc, self.toplevel, self.path)\n  else\n    log.line(\"git\", \"job success    %s %s\", self.toplevel, self.path)\n  end\nend\n\n---Return nil when callback present\n---@private\n---@return nvim_tree.git.PathXY?\nfunction GitRunner:execute()\n  local async = self.callback ~= nil\n  local profile = log.profile_start(\"git %s job %s %s\", async and \"async\" or \"sync\", self.toplevel, self.path)\n\n  if async and self.callback then\n    -- async, always call back\n    self:run_git_job(function()\n      log.profile_end(profile)\n\n      self:finalise()\n\n      self.callback(self.path_xy)\n    end)\n  else\n    -- sync, maybe call back\n    self:run_git_job()\n    self:wait()\n\n    log.profile_end(profile)\n\n    self:finalise()\n\n    if self.callback then\n      self.callback(self.path_xy)\n    else\n      return self.path_xy\n    end\n  end\nend\n\n---Static method to run a git process, which will be killed if it takes more than timeout\n---Return nil when callback present\n---@param args GitRunnerArgs\n---@return nvim_tree.git.PathXY?\nfunction GitRunner:run(args)\n  local runner = GitRunner(args)\n\n  return runner:execute()\nend\n\nreturn GitRunner\n"
  },
  {
    "path": "lua/nvim-tree/git/utils.lua",
    "content": "local log = require(\"nvim-tree.log\")\nlocal utils = require(\"nvim-tree.utils\")\n\nlocal M = {\n  use_cygpath = false,\n}\n\n--- Execute system command\n---@param cmd string[]\n---@return string stdout\n---@return integer exit code\nlocal function system(cmd)\n  if vim.fn.has(\"nvim-0.10\") == 1 then\n    local obj = vim.system(cmd):wait(M.opts.git.timeout)\n    return obj.stdout or \"\", obj.code\n  else\n    return vim.fn.system(cmd), vim.v.shell_error\n  end\nend\n\n--- Retrieve the git toplevel directory\n---@param cwd string path\n---@return string|nil toplevel absolute path\n---@return string|nil git_dir absolute path\nfunction M.get_toplevel(cwd)\n  local profile = log.profile_start(\"git toplevel git_dir %s\", cwd)\n\n  -- both paths are absolute\n  local cmd = { \"git\", \"-C\", cwd, \"rev-parse\", \"--show-toplevel\", \"--absolute-git-dir\" }\n  log.line(\"git\", \"%s\", table.concat(cmd, \" \"))\n\n  local out, exitCode = system(cmd)\n\n  log.raw(\"git\", out)\n  log.profile_end(profile)\n\n  if exitCode ~= 0 or not out or #out == 0 or out:match(\"fatal\") then\n    return nil, nil\n  end\n\n  local toplevel, git_dir = out:match(\"([^\\n]+)\\n+([^\\n]+)\")\n  if not toplevel then\n    return nil, nil\n  end\n  if not git_dir then\n    git_dir = utils.path_join({ toplevel, \".git\" })\n  end\n\n  -- git always returns path with forward slashes\n  if vim.fn.has(\"win32\") == 1 then\n    -- msys2 git support\n    -- cygpath calls must in array format to avoid shell compatibility issues\n    if M.use_cygpath then\n      toplevel = vim.fn.system({ \"cygpath\", \"-w\", toplevel })\n      if vim.v.shell_error ~= 0 then\n        return nil, nil\n      end\n      -- remove trailing newline(\\n) character added by vim.fn.system\n      toplevel = toplevel:gsub(\"\\n\", \"\")\n      git_dir = vim.fn.system({ \"cygpath\", \"-w\", git_dir })\n      if vim.v.shell_error ~= 0 then\n        return nil, nil\n      end\n      -- remove trailing newline(\\n) character added by vim.fn.system\n      git_dir = git_dir:gsub(\"\\n\", \"\")\n    end\n    toplevel = toplevel:gsub(\"/\", \"\\\\\")\n    git_dir = git_dir:gsub(\"/\", \"\\\\\")\n  end\n\n  return toplevel, git_dir\nend\n\n---@type table<string, boolean>\nlocal untracked = {}\n\n---@param cwd string\n---@return boolean\nfunction M.should_show_untracked(cwd)\n  if untracked[cwd] ~= nil then\n    return untracked[cwd]\n  end\n\n  local profile = log.profile_start(\"git untracked %s\", cwd)\n\n  local cmd = { \"git\", \"-C\", cwd, \"config\", \"status.showUntrackedFiles\" }\n  log.line(\"git\", table.concat(cmd, \" \"))\n\n  local has_untracked = system(cmd)\n\n  log.raw(\"git\", has_untracked)\n  log.profile_end(profile)\n\n  untracked[cwd] = vim.trim(has_untracked) ~= \"no\"\n  return untracked[cwd]\nend\n\n---@param t table<string|integer, boolean>?\n---@param k string|integer\n---@return table\nlocal function nil_insert(t, k)\n  t = t or {}\n  t[k] = true\n  return t\nend\n\n---@param project_files nvim_tree.git.ProjectFiles\n---@param cwd string|nil\n---@return nvim_tree.git.ProjectDirs\nfunction M.project_files_to_project_dirs(project_files, cwd)\n  ---@type nvim_tree.git.ProjectDirs\n  local project_dirs = {}\n\n  project_dirs.direct = {}\n  for p, s in pairs(project_files) do\n    if s ~= \"!!\" then\n      local modified = vim.fn.fnamemodify(p, \":h\")\n      project_dirs.direct[modified] = nil_insert(project_dirs.direct[modified], s)\n    end\n  end\n\n  project_dirs.indirect = {}\n  for dirname, statuses in pairs(project_dirs.direct) do\n    for s, _ in pairs(statuses) do\n      local modified = dirname\n      while modified ~= cwd and modified ~= \"/\" do\n        modified = vim.fn.fnamemodify(modified, \":h\")\n        project_dirs.indirect[modified] = nil_insert(project_dirs.indirect[modified], s)\n      end\n    end\n  end\n\n  for _, d in pairs(project_dirs) do\n    for dirname, statuses in pairs(d) do\n      local new_statuses = {}\n      for s, _ in pairs(statuses) do\n        table.insert(new_statuses, s)\n      end\n      d[dirname] = new_statuses\n    end\n  end\n\n  return project_dirs\nend\n\n---Git file status for an absolute path\n---@param parent_ignored boolean\n---@param project nvim_tree.git.Project?\n---@param path string\n---@param path_fallback string? alternative file path when no other file status\n---@return nvim_tree.git.Status\nfunction M.git_status_file(parent_ignored, project, path, path_fallback)\n  ---@type nvim_tree.git.Status\n  local ns\n\n  if parent_ignored then\n    ns = {\n      file = \"!!\"\n    }\n  elseif project and project.files then\n    ns = {\n      file = project.files[path] or project.files[path_fallback]\n    }\n  else\n    ns = {}\n  end\n\n  return ns\nend\n\n---Git file and directory status for an absolute path\n---@param parent_ignored boolean\n---@param project nvim_tree.git.Project?\n---@param path string\n---@param path_fallback string? alternative file path when no other file status\n---@return nvim_tree.git.Status?\nfunction M.git_status_dir(parent_ignored, project, path, path_fallback)\n  ---@type nvim_tree.git.Status?\n  local ns\n\n  if parent_ignored then\n    ns = {\n      file = \"!!\"\n    }\n  elseif project then\n    ns = {\n      file = project.files and (project.files[path] or project.files[path_fallback]),\n      dir  = project.dirs and {\n        direct   = project.dirs.direct and project.dirs.direct[path],\n        indirect = project.dirs.indirect and project.dirs.indirect[path],\n      },\n    }\n  end\n\n  return ns\nend\n\nfunction M.setup(opts)\n  if opts.git.cygwin_support then\n    M.use_cygpath = vim.fn.executable(\"cygpath\") == 1\n  end\n  M.opts = opts\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/help.lua",
    "content": "local keymap = require(\"nvim-tree.keymap\")\nlocal api = {} -- circular dependency\n\nlocal PAT_MOUSE = \"^<.*Mouse\"\nlocal PAT_CTRL = \"^<C%-\"\nlocal PAT_SPECIAL = \"^<.+\"\n\nlocal WIN_HL = table.concat({\n  \"NormalFloat:NvimTreeNormalFloat\",\n  \"WinSeparator:NvimTreeWinSeparator\",\n  \"CursorLine:NvimTreeCursorLine\",\n}, \",\")\n\nlocal namespace_help_id = vim.api.nvim_create_namespace(\"NvimTreeHelp\")\n\nlocal M = {\n  config = {},\n\n  -- one and only buf/win\n  bufnr = nil,\n  winnr = nil,\n}\n\n--- Shorten and normalise a vim command lhs\n---@param lhs string\n---@return string\nlocal function tidy_lhs(lhs)\n  -- nvim_buf_get_keymap replaces leading \"<\" with \"<lt>\" e.g. \"<lt>CTRL-v>\"\n  lhs = lhs:gsub(\"^<lt>\", \"<\")\n\n  -- shorten ctrls\n  if lhs:lower():match(\"^<ctrl%-\") then\n    lhs = lhs:lower():gsub(\"^<ctrl%-\", \"<C%-\")\n  end\n\n  -- uppercase ctrls\n  if lhs:lower():match(\"^<c%-\") then\n    lhs = lhs:upper()\n  end\n\n  -- space is not escaped\n  lhs = lhs:gsub(\" \", \"<Space>\")\n\n  return lhs\nend\n\n--- Remove prefix 'nvim-tree: '\n--- Hardcoded to keep on_attach_default simple\n---@param desc string\n---@return string\nlocal function tidy_desc(desc)\n  return desc and desc:gsub(\"^nvim%-tree: \", \"\") or \"\"\nend\n\n--- sort vim command lhs roughly as per :help index\n---@param a string\n---@param b string\n---@return boolean\nlocal function sort_lhs(a, b)\n  -- mouse first\n  if a:match(PAT_MOUSE) and not b:match(PAT_MOUSE) then\n    return true\n  elseif not a:match(PAT_MOUSE) and b:match(PAT_MOUSE) then\n    return false\n  end\n\n  -- ctrl next\n  if a:match(PAT_CTRL) and not b:match(PAT_CTRL) then\n    return true\n  elseif not a:match(PAT_CTRL) and b:match(PAT_CTRL) then\n    return false\n  end\n\n  -- special next\n  if a:match(PAT_SPECIAL) and not b:match(PAT_SPECIAL) then\n    return true\n  elseif not a:match(PAT_SPECIAL) and b:match(PAT_SPECIAL) then\n    return false\n  end\n\n  -- remainder alpha\n  return a:gsub(\"[^a-zA-Z]\", \"\") < b:gsub(\"[^a-zA-Z]\", \"\")\nend\n\n--- Compute all lines for the buffer\n---@param map table keymap.get_keymap\n---@return string[] lines of text\n---@return HighlightRangeArgs[] hl_range_args for lines\n---@return number maximum length of text\nlocal function compute(map)\n  local head_lhs = \"nvim-tree mappings\"\n  local head_rhs1 = \"exit: q\"\n  local head_rhs2 = string.format(\"sort by %s: s\", M.config.sort_by == \"key\" and \"description\" or \"keymap\")\n\n  -- merge modes for duplicate lhs+desc entries e.g. \"n\" + \"x\" -> \"nx\"\n  local merged = {}\n  local mappings = {}\n  for _, m in ipairs(map) do\n    local lhs = tidy_lhs(m.lhs)\n    local desc = tidy_desc(m.desc)\n    local key = lhs .. \"\\0\" .. desc\n    if merged[key] then\n      merged[key].mode = merged[key].mode .. m.mode\n    else\n      local entry = { lhs = lhs, desc = desc, mode = m.mode or \"n\" }\n      merged[key] = entry\n      table.insert(mappings, entry)\n    end\n  end\n\n  -- sorter function for mappings\n  local sort_fn\n\n  if M.config.sort_by == \"desc\" then\n    sort_fn = function(a, b)\n      return a.desc:lower() < b.desc:lower()\n    end\n  else\n    -- by default sort roughly by lhs\n    sort_fn = function(a, b)\n      return sort_lhs(a.lhs, b.lhs)\n    end\n  end\n\n  table.sort(mappings, sort_fn)\n\n  -- sort mode characters for deterministic display e.g. \"nx\" not \"xn\"\n  for _, entry in ipairs(mappings) do\n    local chars = {}\n    for c in entry.mode:gmatch(\".\") do\n      table.insert(chars, c)\n    end\n    table.sort(chars)\n    entry.mode = table.concat(chars)\n  end\n\n  -- longest lhs, mode and description\n  local max_lhs = 0\n  local max_mode = 0\n  local max_desc = 0\n  for _, l in ipairs(mappings) do\n    max_lhs = math.max(#l.lhs, max_lhs)\n    max_mode = math.max(#l.mode, max_mode)\n    max_desc = math.max(#l.desc, max_desc)\n  end\n\n  -- increase desc if lines are shorter than the header\n  max_desc = math.max(max_desc, #head_lhs + #head_rhs1 - max_lhs - max_mode)\n\n  -- header text, not padded\n  local lines = {\n    head_lhs .. string.rep(\" \", max_lhs + max_mode + max_desc - #head_lhs - #head_rhs1 + 3) .. head_rhs1,\n    string.rep(\" \", max_lhs + max_mode + max_desc - #head_rhs2 + 3) .. head_rhs2,\n  }\n  local width = #lines[1]\n\n  -- header highlight, assume one character keys\n  local hl_range_args = {\n    { higroup = \"NvimTreeFolderName\", start = { 0, 0, },         finish = { 0, #head_lhs, }, },\n    { higroup = \"NvimTreeFolderName\", start = { 0, width - 1, }, finish = { 0, width, }, },\n    { higroup = \"NvimTreeFolderName\", start = { 1, width - 1, }, finish = { 1, width, }, },\n  }\n\n  -- mappings, left padded 1\n  local fmt = string.format(\" %%-%ds %%-%ds %%-%ds\", max_lhs, max_mode, max_desc)\n  for i, l in ipairs(mappings) do\n    -- format in left aligned columns\n    local line = string.format(fmt, l.lhs, l.mode, l.desc)\n    table.insert(lines, line)\n    width = math.max(#line, width)\n\n    -- highlight lhs\n    table.insert(hl_range_args, { higroup = \"NvimTreeFolderName\", start = { i + 1, 1, }, finish = { i + 1, #l.lhs + 1, }, })\n  end\n\n  return lines, hl_range_args, width\nend\n\n--- close the window and delete the buffer, if they exist\nlocal function close()\n  if M.winnr then\n    vim.api.nvim_win_close(M.winnr, true)\n    M.winnr = nil\n  end\n  if M.bufnr then\n    vim.api.nvim_buf_delete(M.bufnr, { force = true })\n    M.bufnr = nil\n  end\nend\n\n--- open a new window and buffer\nlocal function open()\n  -- close existing, shouldn't be necessary\n  close()\n\n  -- fetch all mappings\n  local map = keymap.get_keymap()\n\n  -- text and highlight\n  local lines, hl_range_args, width = compute(map)\n\n  -- create the buffer\n  M.bufnr = vim.api.nvim_create_buf(false, true)\n\n  -- populate it\n  vim.api.nvim_buf_set_lines(M.bufnr, 0, -1, false, lines)\n\n  if vim.fn.has(\"nvim-0.10\") == 1 then\n    vim.api.nvim_set_option_value(\"modifiable\", false, { buf = M.bufnr })\n  else\n    vim.api.nvim_buf_set_option(M.bufnr, \"modifiable\", false) ---@diagnostic disable-line: deprecated\n  end\n\n  -- highlight it\n  for _, args in ipairs(hl_range_args) do\n    if vim.fn.has(\"nvim-0.11\") == 1 and vim.hl and vim.hl.range then\n      vim.hl.range(M.bufnr, namespace_help_id, args.higroup, args.start, args.finish, {})\n    else\n      vim.api.nvim_buf_add_highlight(M.bufnr, -1, args.higroup, args.start[1], args.start[2], args.finish[2]) ---@diagnostic disable-line: deprecated\n    end\n  end\n\n  -- open a very restricted window\n  M.winnr = vim.api.nvim_open_win(M.bufnr, true, {\n    relative = \"editor\",\n    border = \"single\",\n    width = width,\n    height = #lines,\n    row = 1,\n    col = 0,\n    style = \"minimal\",\n    noautocmd = true,\n  })\n\n  -- style it a bit like the tree\n  vim.wo[M.winnr].winhl = WIN_HL\n  vim.wo[M.winnr].cursorline = M.config.cursorline\n\n  local function toggle_sort()\n    M.config.sort_by = (M.config.sort_by == \"desc\") and \"key\" or \"desc\"\n    open()\n  end\n\n  -- hardcoded\n  local help_keymaps = {\n    q = { fn = close, desc = \"nvim-tree: exit help\" },\n    [\"<Esc>\"] = { fn = close, desc = \"nvim-tree: exit help\" }, -- hidden\n    s = { fn = toggle_sort, desc = \"nvim-tree: toggle sorting method\" },\n  }\n\n  -- api help binding closes\n  for _, m in ipairs(map) do\n    if m.callback == api.tree.toggle_help then\n      help_keymaps[m.lhs] = { fn = close, desc = \"nvim-tree: exit help\" }\n    end\n  end\n\n  for k, v in pairs(help_keymaps) do\n    vim.keymap.set(\"n\", k, v.fn, {\n      desc = v.desc,\n      buffer = M.bufnr,\n      noremap = true,\n      silent = true,\n      nowait = true,\n    })\n  end\n\n  -- close window and delete buffer on leave\n  vim.api.nvim_create_autocmd({ \"BufLeave\", \"WinLeave\" }, {\n    buffer = M.bufnr,\n    once = true,\n    callback = close,\n  })\nend\n\nfunction M.toggle()\n  if M.winnr or M.bufnr then\n    close()\n  else\n    open()\n  end\nend\n\nfunction M.setup(opts)\n  M.config.cursorline = opts.view.cursorline\n  M.config.sort_by = opts.help.sort_by\n\n  api = require(\"nvim-tree.api\")\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/iterators/node-iterator.lua",
    "content": "---@class NodeIterator\nlocal NodeIterator = {}\nNodeIterator.__index = NodeIterator\n\n---@param nodes Node[]\n---@return NodeIterator\nfunction NodeIterator.builder(nodes)\n  return setmetatable({\n    nodes = nodes,\n    _filter_hidden = function(node)\n      return not node.hidden\n    end,\n    _apply_fn_on_node = function(_) end,\n    _match = function(_) end,\n    _recurse_with = function(node)\n      return node.nodes\n    end,\n  }, NodeIterator)\nend\n\n---@return NodeIterator\nfunction NodeIterator:hidden()\n  self._filter_hidden = function(_)\n    return true\n  end\n  return self\nend\n\n---@param f fun(node: Node): boolean\n---@return NodeIterator\nfunction NodeIterator:matcher(f)\n  self._match = f\n  return self\nend\n\n---@param f fun(node: Node, i: number)\n---@return NodeIterator\nfunction NodeIterator:applier(f)\n  self._apply_fn_on_node = f\n  return self\nend\n\n---@param f fun(node: Node): any\n---@return NodeIterator\nfunction NodeIterator:recursor(f)\n  self._recurse_with = f\n  return self\nend\n\n---@return Node|nil\n---@return number|nil\nfunction NodeIterator:iterate()\n  local iteration_count = 0\n  local function iter(nodes)\n    for _, node in ipairs(nodes) do\n      if self._filter_hidden(node) then\n        if not node.group_next then\n          iteration_count = iteration_count + 1\n        end\n        if self._match(node) then\n          return node, iteration_count\n        end\n        self._apply_fn_on_node(node, iteration_count)\n        local children = self._recurse_with(node)\n        if children then\n          local n = iter(children)\n          if n then\n            return n, iteration_count\n          end\n        end\n      end\n    end\n    return nil, 0\n  end\n\n  return iter(self.nodes)\nend\n\nreturn NodeIterator\n"
  },
  {
    "path": "lua/nvim-tree/keymap.lua",
    "content": "local M = {}\n\n--- Apply mappings to a scratch buffer and return buffer local mappings\n---@param fn fun(bufnr: integer) on_attach or on_attach_default\n---@return table as per vim.api.nvim_buf_get_keymap\nlocal function generate_keymap(fn)\n  -- create an unlisted scratch buffer\n  local scratch_bufnr = vim.api.nvim_create_buf(false, true)\n\n  -- apply mappings\n  fn(scratch_bufnr)\n\n  -- retrieve all\n  local keymap = vim.api.nvim_buf_get_keymap(scratch_bufnr, \"\")\n\n  -- delete the scratch buffer\n  vim.api.nvim_buf_delete(scratch_bufnr, { force = true })\n\n  return keymap\nend\n\n---@return table\nfunction M.get_keymap()\n  return generate_keymap(M.on_attach)\nend\n\n---@return table\nfunction M.get_keymap_default()\n  return generate_keymap(M.on_attach_default)\nend\n\n---@param bufnr integer\nfunction M.on_attach_default(bufnr)\n  local api = require(\"nvim-tree.api\")\n\n  local function opts(desc)\n    return {\n      desc = \"nvim-tree: \" .. desc,\n      buffer = bufnr,\n      noremap = true,\n      silent = true,\n      nowait = true,\n    }\n  end\n\n  -- BEGIN_ON_ATTACH_DEFAULT\n  vim.keymap.set(\"n\",          \"<C-]>\",          api.tree.change_root_to_node,       opts(\"CD\"))\n  vim.keymap.set(\"n\",          \"<C-e>\",          api.node.open.replace_tree_buffer,  opts(\"Open: In Place\"))\n  vim.keymap.set(\"n\",          \"<C-k>\",          api.node.show_info_popup,           opts(\"Info\"))\n  vim.keymap.set(\"n\",          \"<C-r>\",          api.fs.rename_sub,                  opts(\"Rename: Omit Filename\"))\n  vim.keymap.set(\"n\",          \"<C-t>\",          api.node.open.tab,                  opts(\"Open: New Tab\"))\n  vim.keymap.set(\"n\",          \"<C-v>\",          api.node.open.vertical,             opts(\"Open: Vertical Split\"))\n  vim.keymap.set(\"n\",          \"<C-x>\",          api.node.open.horizontal,           opts(\"Open: Horizontal Split\"))\n  vim.keymap.set(\"n\",          \"<BS>\",           api.node.navigate.parent_close,     opts(\"Close Directory\"))\n  vim.keymap.set(\"n\",          \"<CR>\",           api.node.open.edit,                 opts(\"Open\"))\n  vim.keymap.set({ \"n\", \"x\" }, \"<Del>\",          api.fs.remove,                      opts(\"Delete\"))\n  vim.keymap.set(\"n\",          \"<Tab>\",          api.node.open.preview,              opts(\"Open Preview\"))\n  vim.keymap.set(\"n\",          \">\",              api.node.navigate.sibling.next,     opts(\"Next Sibling\"))\n  vim.keymap.set(\"n\",          \"<\",              api.node.navigate.sibling.prev,     opts(\"Previous Sibling\"))\n  vim.keymap.set(\"n\",          \".\",              api.node.run.cmd,                   opts(\"Run Command\"))\n  vim.keymap.set(\"n\",          \"-\",              api.tree.change_root_to_parent,     opts(\"Up\"))\n  vim.keymap.set(\"n\",          \"a\",              api.fs.create,                      opts(\"Create File Or Directory\"))\n  vim.keymap.set(\"n\",          \"bd\",             api.marks.bulk.delete,              opts(\"Delete Bookmarked\"))\n  vim.keymap.set(\"n\",          \"bt\",             api.marks.bulk.trash,               opts(\"Trash Bookmarked\"))\n  vim.keymap.set(\"n\",          \"bmv\",            api.marks.bulk.move,                opts(\"Move Bookmarked\"))\n  vim.keymap.set(\"n\",          \"B\",              api.filter.no_buffer.toggle,        opts(\"Toggle Filter: No Buffer\"))\n  vim.keymap.set({ \"n\", \"x\" }, \"c\",              api.fs.copy.node,                   opts(\"Copy\"))\n  vim.keymap.set(\"n\",          \"C\",              api.filter.git.clean.toggle,        opts(\"Toggle Filter: Git Clean\"))\n  vim.keymap.set(\"n\",          \"[c\",             api.node.navigate.git.prev,         opts(\"Prev Git\"))\n  vim.keymap.set(\"n\",          \"]c\",             api.node.navigate.git.next,         opts(\"Next Git\"))\n  vim.keymap.set({ \"n\", \"x\" }, \"d\",              api.fs.remove,                      opts(\"Delete\"))\n  vim.keymap.set({ \"n\", \"x\" }, \"D\",              api.fs.trash,                       opts(\"Trash\"))\n  vim.keymap.set(\"n\",          \"E\",              api.tree.expand_all,                opts(\"Expand All\"))\n  vim.keymap.set(\"n\",          \"e\",              api.fs.rename_basename,             opts(\"Rename: Basename\"))\n  vim.keymap.set(\"n\",          \"]e\",             api.node.navigate.diagnostics.next, opts(\"Next Diagnostic\"))\n  vim.keymap.set(\"n\",          \"[e\",             api.node.navigate.diagnostics.prev, opts(\"Prev Diagnostic\"))\n  vim.keymap.set(\"n\",          \"F\",              api.filter.live.clear,              opts(\"Live Filter: Clear\"))\n  vim.keymap.set(\"n\",          \"f\",              api.filter.live.start,              opts(\"Live Filter: Start\"))\n  vim.keymap.set(\"n\",          \"g?\",             api.tree.toggle_help,               opts(\"Help\"))\n  vim.keymap.set(\"n\",          \"gy\",             api.fs.copy.absolute_path,          opts(\"Copy Absolute Path\"))\n  vim.keymap.set(\"n\",          \"ge\",             api.fs.copy.basename,               opts(\"Copy Basename\"))\n  vim.keymap.set(\"n\",          \"H\",              api.filter.dotfiles.toggle,         opts(\"Toggle Filter: Dotfiles\"))\n  vim.keymap.set(\"n\",          \"I\",              api.filter.git.ignored.toggle,      opts(\"Toggle Filter: Git Ignored\"))\n  vim.keymap.set(\"n\",          \"J\",              api.node.navigate.sibling.last,     opts(\"Last Sibling\"))\n  vim.keymap.set(\"n\",          \"K\",              api.node.navigate.sibling.first,    opts(\"First Sibling\"))\n  vim.keymap.set(\"n\",          \"L\",              api.node.open.toggle_group_empty,   opts(\"Toggle Group Empty\"))\n  vim.keymap.set(\"n\",          \"M\",              api.filter.no_bookmark.toggle,      opts(\"Toggle Filter: No Bookmark\"))\n  vim.keymap.set({ \"n\", \"x\" }, \"m\",              api.marks.toggle,                   opts(\"Toggle Bookmark\"))\n  vim.keymap.set(\"n\",          \"o\",              api.node.open.edit,                 opts(\"Open\"))\n  vim.keymap.set(\"n\",          \"O\",              api.node.open.no_window_picker,     opts(\"Open: No Window Picker\"))\n  vim.keymap.set(\"n\",          \"p\",              api.fs.paste,                       opts(\"Paste\"))\n  vim.keymap.set(\"n\",          \"P\",              api.node.navigate.parent,           opts(\"Parent Directory\"))\n  vim.keymap.set(\"n\",          \"q\",              api.tree.close,                     opts(\"Close\"))\n  vim.keymap.set(\"n\",          \"r\",              api.fs.rename,                      opts(\"Rename\"))\n  vim.keymap.set(\"n\",          \"R\",              api.tree.reload,                    opts(\"Refresh\"))\n  vim.keymap.set(\"n\",          \"s\",              api.node.run.system,                opts(\"Run System\"))\n  vim.keymap.set(\"n\",          \"S\",              api.tree.search_node,               opts(\"Search\"))\n  vim.keymap.set(\"n\",          \"u\",              api.fs.rename_full,                 opts(\"Rename: Full Path\"))\n  vim.keymap.set(\"n\",          \"U\",              api.filter.custom.toggle,           opts(\"Toggle Filter: Custom\"))\n  vim.keymap.set(\"n\",          \"W\",              api.tree.collapse_all,              opts(\"Collapse All\"))\n  vim.keymap.set({ \"n\", \"x\" }, \"x\",              api.fs.cut,                         opts(\"Cut\"))\n  vim.keymap.set(\"n\",          \"y\",              api.fs.copy.filename,               opts(\"Copy Name\"))\n  vim.keymap.set(\"n\",          \"Y\",              api.fs.copy.relative_path,          opts(\"Copy Relative Path\"))\n  vim.keymap.set(\"n\",          \"<2-LeftMouse>\",  api.node.open.edit,                 opts(\"Open\"))\n  vim.keymap.set(\"n\",          \"<2-RightMouse>\", api.tree.change_root_to_node,       opts(\"CD\"))\n  -- END_ON_ATTACH_DEFAULT\nend\n\nfunction M.setup(opts)\n  if type(opts.on_attach) ~= \"function\" then\n    M.on_attach = M.on_attach_default\n  else\n    M.on_attach = opts.on_attach\n  end\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/legacy.lua",
    "content": "local notify = require(\"nvim-tree.notify\")\n\nlocal M = {}\n\n--- Create empty sub-tables if not present\n---@param tbl table to create empty inside of\n---@param path string dot separated string of sub-tables\n---@return table deepest sub-table\nlocal function create(tbl, path)\n  local t = tbl\n  for s in string.gmatch(path, \"([^%.]+)%.*\") do\n    if t[s] == nil then\n      t[s] = {}\n    end\n    t = t[s]\n  end\n\n  return t\nend\n\n--- Move a value from src to dst if value is nil on dst.\n--- Remove value from src\n---@param src table to copy from\n---@param src_path string dot separated string of sub-tables\n---@param src_pos string value pos\n---@param dst table to copy to\n---@param dst_path string dot separated string of sub-tables, created when missing\n---@param dst_pos string value pos\n---@param remove boolean\nlocal function move(src, src_path, src_pos, dst, dst_path, dst_pos, remove)\n  for pos in string.gmatch(src_path, \"([^%.]+)%.*\") do\n    if src[pos] and type(src[pos]) == \"table\" then\n      src = src[pos]\n    else\n      return\n    end\n  end\n  local src_val = src[src_pos]\n  if src_val == nil then\n    return\n  end\n\n  dst = create(dst, dst_path)\n  if dst[dst_pos] == nil then\n    dst[dst_pos] = src_val\n  end\n\n  if remove then\n    src[src_pos] = nil\n  end\nend\n\n-- silently move, please add to help nvim-tree-legacy-config\n---@param u nvim_tree.config user supplied subset of config\nlocal function refactored_config(u)\n  -- 2022/06/20\n  move(u, \"update_focused_file\", \"update_cwd\", u, \"update_focused_file\", \"update_root\",        true)\n  move(u, \"\",                    \"update_cwd\", u, \"\",                    \"sync_root_with_cwd\", true)\n\n  -- 2022/11/07\n  move(u, \"\", \"open_on_tab\",              u, \"tab.sync\", \"open\",   false)\n  move(u, \"\", \"open_on_tab\",              u, \"tab.sync\", \"close\",  true)\n  move(u, \"\", \"ignore_buf_on_tab_change\", u, \"tab.sync\", \"ignore\", true)\n\n  -- 2022/11/22\n  move(u, \"renderer\", \"root_folder_modifier\", u, \"renderer\", \"root_folder_label\", true)\n\n  -- 2023/01/01\n  move(u, \"update_focused_file\", \"debounce_delay\", u, \"view\", \"debounce_delay\", true)\n\n  -- 2023/01/08\n  move(u, \"trash\", \"require_confirm\", u, \"ui.confirm\", \"trash\", true)\n\n  -- 2023/01/15\n  if type(u.view) == \"table\" and u.view.adaptive_size ~= nil then\n    if u.view.adaptive_size and type(u.view.width) ~= \"table\" then\n      local width = u.view.width --[[@as nvim_tree.config.view.width.spec]]\n      u.view.width = {\n        min = width,\n      }\n    end\n    u.view[\"adaptive_size\"] = nil\n  end\n\n  -- 2023/07/15\n  move(u, \"\", \"sort_by\", u, \"sort\", \"sorter\", true)\n\n  -- 2023/07/16\n  move(u, \"git\", \"ignore\", u, \"filters\", \"git_ignored\", true)\n\n  -- 2023/08/26\n  move(u, \"renderer.icons\", \"webdev_colors\", u, \"renderer.icons.web_devicons.file\", \"color\", true)\n\n  -- 2023/10/08\n  if type(u.renderer) == \"table\" and type(u.renderer.highlight_diagnostics) == \"boolean\" then\n    u.renderer.highlight_diagnostics = u.renderer.highlight_diagnostics and \"name\" or \"none\"\n  end\n\n  -- 2023/10/21\n  if type(u.renderer) == \"table\" and type(u.renderer.highlight_git) == \"boolean\" then\n    u.renderer.highlight_git = u.renderer.highlight_git and \"name\" or \"none\"\n  end\n\n  -- 2024/02/15\n  if type(u.update_focused_file) == \"table\" then\n    if type(u.update_focused_file.update_root) ~= \"table\" then\n      u.update_focused_file.update_root = { enable = u.update_focused_file.update_root == true }\n    end\n  end\n  move(u, \"update_focused_file\", \"ignore_list\", u, \"update_focused_file.update_root\", \"ignore_list\", true)\n\n  -- 2025/04/30\n  if u.renderer and u.renderer.icons and type(u.renderer.icons.padding) == \"string\" then\n    local icons_padding = u.renderer.icons.padding --[[@as string]]\n    u.renderer.icons.padding = {}\n    u.renderer.icons.padding.icon = icons_padding\n  end\nend\n\n---@param u nvim_tree.config user supplied subset of config\nlocal function deprecated_config(u)\n  if type(u.view) == \"table\" and u.view.hide_root_folder then\n    notify.info(\"view.hide_root_folder is deprecated, please set renderer.root_folder_label = false\")\n  end\nend\n\n---@param u nvim_tree.config user supplied subset of config\nlocal function removed_config(u)\n  if u.auto_close then\n    notify.warn(\"auto close feature has been removed: https://github.com/nvim-tree/nvim-tree.lua/wiki/Auto-Close\")\n    u[\"auto_close\"] = nil\n  end\n\n  if u.focus_empty_on_setup then\n    notify.warn(\"focus_empty_on_setup has been removed: https://github.com/nvim-tree/nvim-tree.lua/wiki/Open-At-Startup\")\n    u[\"focus_empty_on_setup\"] = nil\n  end\n\n  if u.create_in_closed_folder then\n    notify.warn(\n      \"create_in_closed_folder has been removed and is now the default behaviour. You may use api.fs.create to add a file under your desired node.\")\n  end\n  u[\"create_in_closed_folder\"] = nil\nend\n\n---Migrate legacy config in place.\n---Refactored are silently migrated. Deprecated and removed result in a warning.\n---@param u nvim_tree.config user supplied subset of config\nfunction M.migrate_config(u)\n  -- silently move\n  refactored_config(u)\n\n  -- warn\n  deprecated_config(u)\n\n  -- warn and delete\n  removed_config(u)\nend\n\n---Silently create new api entries pointing legacy functions to current\n---@param api table not properly typed to prevent LSP from referencing implementations\nfunction M.map_api(api)\n  api.config = api.config or {}\n  api.config.mappings = api.config.mappings or {}\n  api.config.mappings.get_keymap = api.map.keymap.current\n  api.config.mappings.get_keymap_default = api.map.keymap.default\n  api.config.mappings.default_on_attach = api.map.on_attach.default\n\n  api.live_filter = api.live_filter or {}\n  api.live_filter.start = api.filter.live.start\n  api.live_filter.clear = api.filter.live.clear\n\n  api.tree = api.tree or {}\n  api.tree.toggle_enable_filters = api.filter.toggle\n  api.tree.toggle_gitignore_filter = api.filter.git.ignored.toggle\n  api.tree.toggle_git_clean_filter = api.filter.git.clean.toggle\n  api.tree.toggle_no_buffer_filter = api.filter.no_buffer.toggle\n  api.tree.toggle_custom_filter = api.filter.custom.toggle\n  api.tree.toggle_hidden_filter = api.filter.dotfiles.toggle\n  api.tree.toggle_no_bookmark_filter = api.filter.no_bookmark.toggle\n\n  api.diagnostics = api.diagnostics or {}\n  api.diagnostics.hi_test = api.appearance.hi_test\n\n  api.decorator.UserDecorator = api.Decorator\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/lib.lua",
    "content": "local view = require(\"nvim-tree.view\")\nlocal core = require(\"nvim-tree.core\")\nlocal notify = require(\"nvim-tree.notify\")\n\n---@class LibOpenOpts\n---@field path string|nil path\n---@field current_window boolean|nil default false\n---@field winid number|nil\n\nlocal M = {\n  target_winid = nil,\n}\n\nfunction M.set_target_win()\n  local id = vim.api.nvim_get_current_win()\n  local tree_id = view.get_winnr()\n  if tree_id and id == tree_id then\n    M.target_winid = 0\n    return\n  end\n\n  M.target_winid = id\nend\n\n---@param cwd string\nlocal function handle_buf_cwd(cwd)\n  local explorer = core.get_explorer()\n  if M.respect_buf_cwd and cwd ~= core.get_cwd() and explorer then\n    explorer:change_dir(cwd)\n  end\nend\n\nlocal function open_view_and_draw()\n  local cwd = vim.fn.getcwd()\n  view.open()\n  handle_buf_cwd(cwd)\n\n  local explorer = core.get_explorer()\n  if explorer then\n    explorer.renderer:draw()\n  end\nend\n\nlocal function should_hijack_current_buf()\n  local bufnr = vim.api.nvim_get_current_buf()\n  local bufname = vim.api.nvim_buf_get_name(bufnr)\n\n  local bufmodified, ft\n  if vim.fn.has(\"nvim-0.10\") == 1 then\n    bufmodified = vim.api.nvim_get_option_value(\"modified\", { buf = bufnr })\n    ft = vim.api.nvim_get_option_value(\"ft\", { buf = bufnr })\n  else\n    bufmodified = vim.api.nvim_buf_get_option(bufnr, \"modified\") ---@diagnostic disable-line: deprecated\n    ft = vim.api.nvim_buf_get_option(bufnr, \"ft\") ---@diagnostic disable-line: deprecated\n  end\n\n  local should_hijack_unnamed = M.hijack_unnamed_buffer_when_opening and bufname == \"\" and not bufmodified and ft == \"\"\n  local should_hijack_dir = bufname ~= \"\" and vim.fn.isdirectory(bufname) == 1 and M.hijack_directories.enable\n\n  return should_hijack_dir or should_hijack_unnamed\nend\n\n---@param prompt_input string\n---@param prompt_select string\n---@param items_short string[]\n---@param items_long string[]\n---@param kind string|nil\n---@param callback fun(item_short: string|nil)\nfunction M.prompt(prompt_input, prompt_select, items_short, items_long, kind, callback)\n  local function format_item(short)\n    for i, s in ipairs(items_short) do\n      if short == s then\n        return items_long[i]\n      end\n    end\n    return \"\"\n  end\n\n  if M.select_prompts then\n    vim.ui.select(items_short, { prompt = prompt_select, kind = kind, format_item = format_item }, function(item_short)\n      callback(item_short)\n    end)\n  else\n    vim.ui.input({ prompt = prompt_input, default = items_short[1] or \"\" }, function(item_short)\n      if item_short then\n        callback(string.lower(item_short and item_short:sub(1, 1)) or nil)\n      end\n    end)\n  end\nend\n\n---Open the tree, initialising as needed. Maybe hijack the current buffer.\n---@param opts LibOpenOpts|nil\nfunction M.open(opts)\n  opts = opts or {}\n\n  M.set_target_win()\n  if not core.get_explorer() or opts.path then\n    if opts.path then\n      core.init(opts.path)\n    else\n      local cwd, err = vim.loop.cwd()\n      if not cwd then\n        notify.error(string.format(\"current working directory unavailable: %s\", err))\n        return\n      end\n      core.init(cwd)\n    end\n  end\n\n  local explorer = core.get_explorer()\n\n  if should_hijack_current_buf() then\n    view.close_this_tab_only()\n    view.open_in_win()\n    if explorer then\n      explorer.renderer:draw()\n    end\n  elseif opts.winid then\n    view.open_in_win({ hijack_current_buf = false, resize = false, winid = opts.winid })\n    if explorer then\n      explorer.renderer:draw()\n    end\n  elseif opts.current_window then\n    view.open_in_win({ hijack_current_buf = false, resize = false })\n    if explorer then\n      explorer.renderer:draw()\n    end\n  else\n    open_view_and_draw()\n  end\n  view.restore_tab_state()\nend\n\nfunction M.setup(opts)\n  M.hijack_unnamed_buffer_when_opening = opts.hijack_unnamed_buffer_when_opening\n  M.hijack_directories = opts.hijack_directories\n  M.respect_buf_cwd = opts.respect_buf_cwd\n  M.select_prompts = opts.select_prompts\n  M.group_empty = opts.renderer.group_empty\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/log.lua",
    "content": "---@alias LogTypes \"all\" | \"config\" | \"copy_paste\" | \"dev\" | \"diagnostics\" | \"git\" | \"profile\" | \"watcher\"\n\n---@type table<LogTypes, boolean>\nlocal types = {}\n\n---@type string\nlocal file_path\n\nlocal M = {}\n\n--- Write to log file\n---@param typ string as per log.types config\n---@param fmt string for string.format\n---@param ... any arguments for string.format\nfunction M.raw(typ, fmt, ...)\n  if not M.enabled(typ) then\n    return\n  end\n\n  local line = string.format(fmt, ...)\n  local file = io.open(file_path, \"a\")\n  if file then\n    io.output(file)\n    io.write(line)\n    io.close(file)\n  end\nend\n\n--- Write to a new file\n---@param typ LogTypes as per log.types config\n---@param path string absolute path\n---@param fmt string for string.format\n---@param ... any arguments for string.format\nfunction M.file(typ, path, fmt, ...)\n  if not M.enabled(typ) then\n    return\n  end\n\n  local line = string.format(fmt, ...)\n  local file = io.open(path, \"w\")\n  if file then\n    io.output(file)\n    io.write(line)\n    io.close(file)\n  end\nend\n\n---@class Profile\n---@field start number nanos\n---@field tag string\n\n--- Write profile start to log file\n--- START is prefixed\n---@param fmt string for string.format\n---@param ... any arguments for string.format\n---@return Profile to pass to profile_end\nfunction M.profile_start(fmt, ...)\n  local profile = {}\n  if M.enabled(\"profile\") then\n    profile.start = vim.loop.hrtime()\n    profile.tag = string.format((fmt or \"???\"), ...)\n    M.line(\"profile\", \"START %s\", profile.tag)\n  end\n  return profile\nend\n\n--- Write profile end to log file\n--- END is prefixed and duration in seconds is suffixed\n---@param profile Profile returned from profile_start\nfunction M.profile_end(profile)\n  if M.enabled(\"profile\") and type(profile) == \"table\" then\n    local millis = profile.start and math.modf((vim.loop.hrtime() - profile.start) / 1000000) or -1\n    M.line(\"profile\", \"END   %s %dms\", profile.tag or \"\", millis)\n  end\nend\n\n--- Write to log file\n--- time and typ are prefixed and a trailing newline is added\n---@param typ LogTypes as per log.types config\n---@param fmt string for string.format\n---@param ... any arguments for string.format\nfunction M.line(typ, fmt, ...)\n  if M.enabled(typ) then\n    M.raw(typ, string.format(\"[%s] [%s] %s\\n\", os.date(\"%Y-%m-%d %H:%M:%S\"), typ, (fmt or \"???\")), ...)\n  end\nend\n\nlocal inspect_opts = {}\n\n---@param opts table\nfunction M.set_inspect_opts(opts)\n  inspect_opts = opts\nend\n\n--- Write to log file the inspection of a node\n---@param typ LogTypes as per log.types config\n---@param node Node node to be inspected\n---@param fmt string for string.format\n---@param ... any arguments for string.format\nfunction M.node(typ, node, fmt, ...)\n  if M.enabled(typ) then\n    M.raw(typ, string.format(\"[%s] [%s] %s\\n%s\\n\", os.date(\"%Y-%m-%d %H:%M:%S\"), typ, (fmt or \"???\"), vim.inspect(node, inspect_opts)), ...)\n  end\nend\n\n--- Logging is enabled for typ or all\n---@param typ LogTypes as per log.types config\n---@return boolean\nfunction M.enabled(typ)\n  return file_path ~= nil and (types[typ] or types.all)\nend\n\nfunction M.setup(opts)\n  if opts.log and opts.log.enable and opts.log.types then\n    types = opts.log.types\n    file_path = string.format(\"%s/nvim-tree.log\", vim.fn.stdpath(\"log\"), os.date(\"%H:%M:%S\"), vim.env.USER)\n    if opts.log.truncate then\n      os.remove(file_path)\n    end\n    require(\"nvim-tree.notify\").debug(\"nvim-tree.lua logging to \" .. file_path)\n  end\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/marks/init.lua",
    "content": "local Iterator = require(\"nvim-tree.iterators.node-iterator\")\nlocal core = require(\"nvim-tree.core\")\nlocal lib = require(\"nvim-tree.lib\")\nlocal notify = require(\"nvim-tree.notify\")\nlocal open_file = require(\"nvim-tree.actions.node.open-file\")\nlocal remove_file = require(\"nvim-tree.actions.fs.remove-file\")\nlocal rename_file = require(\"nvim-tree.actions.fs.rename-file\")\nlocal trash = require(\"nvim-tree.actions.fs.trash\")\nlocal utils = require(\"nvim-tree.utils\")\n\nlocal Class = require(\"nvim-tree.classic\")\nlocal DirectoryNode = require(\"nvim-tree.node.directory\")\nlocal Node = require(\"nvim-tree.node\")\n\nlocal function get_save_path(opts)\n  if type(opts.bookmarks.persist) == \"string\" then\n    return opts.bookmarks.persist\n  else\n    return vim.fn.stdpath(\"data\") .. \"/nvim-tree-bookmarks.json\"\n  end\nend\n\nlocal function save_bookmarks(marks, opts)\n  if not opts.bookmarks.persist then\n    return\n  end\n\n  local storepath = get_save_path(opts)\n  local file, errmsg = io.open(storepath, \"w\")\n  if file then\n    local data = {}\n    for path, _ in pairs(marks) do\n      table.insert(data, path)\n    end\n    file:write(vim.json.encode(data))\n    file:close()\n  else\n    notify.warn(string.format(\"Invalid bookmarks.persist, disabling persistence: %s\", errmsg))\n    opts.bookmarks.persist = false\n  end\nend\n\nlocal function load_bookmarks(opts)\n  local storepath = get_save_path(opts)\n  local file = io.open(storepath, \"r\")\n  if file then\n    local content = file:read(\"*all\")\n    file:close()\n    if content and content ~= \"\" then\n      local data = vim.json.decode(content)\n      local marks = {}\n      for _, path in ipairs(data) do\n        -- Store as boolean initially; will be lazily resolved to node on first access\n        marks[path] = true\n      end\n      return marks\n    end\n  end\n  return {}\nend\n\n---@class (exact) Marks: nvim_tree.Class\n---@field private explorer Explorer\n---@field private marks table<string, Node> by absolute path\nlocal Marks = Class:extend()\n\n---@class Marks\n---@overload fun(args: MarksArgs): Marks\n\n---@class (exact) MarksArgs\n---@field explorer Explorer\n\n---@protected\n---@param args MarksArgs\nfunction Marks:new(args)\n  self.explorer = args.explorer\n  self.marks = {}\n  if self.explorer.opts.bookmarks.persist then\n    local ok, loaded_marks = pcall(load_bookmarks, self.explorer.opts)\n    if ok then\n      self.marks = loaded_marks\n    else\n      notify.warn(string.format(\"Failed to load bookmarks: %s\", loaded_marks))\n    end\n  end\nend\n\n---Clear all marks and reload if watchers disabled\n---@private\nfunction Marks:clear_reload()\n  self:clear()\n  if not self.explorer.opts.filesystem_watchers.enable then\n    self.explorer:reload_explorer()\n  end\nend\n\n---Clear all marks and redraw\n---@public\nfunction Marks:clear()\n  self.marks = {}\n  self.explorer.renderer:draw()\nend\n\n---@private\n---@param node Node\nfunction Marks:toggle_one(node)\n  if node.absolute_path == nil then\n    return\n  end\n\n  if self:get(node) then\n    self.marks[node.absolute_path] = nil\n  else\n    self.marks[node.absolute_path] = node\n  end\nend\n\n---@public\n---@param node_or_nodes Node|Node[]\nfunction Marks:toggle(node_or_nodes)\n  if type(node_or_nodes) == \"table\" and node_or_nodes.is and node_or_nodes:is(Node) then\n    self:toggle_one(node_or_nodes)\n  else\n    for _, node in ipairs(node_or_nodes) do\n      self:toggle_one(node)\n    end\n  end\n\n  if self.explorer.opts.bookmarks.persist then\n    local ok, err = pcall(save_bookmarks, self.marks, self.explorer.opts)\n    if not ok then\n      notify.warn(string.format(\"Failed to save bookmarks: %s\", err))\n    end\n  end\n  self.explorer.renderer:draw()\nend\n\n---Return node if marked\n---@public\n---@param node Node\n---@return Node|nil\nfunction Marks:get(node)\n  if not node or not node.absolute_path then\n    return nil\n  end\n  local mark = self.marks[node.absolute_path]\n  if mark == true then\n    -- Lazy resolve: try to find node in explorer tree\n    local resolved_node = self.explorer:get_node_from_path(node.absolute_path)\n    if resolved_node then\n      -- Cache the resolved node\n      self.marks[node.absolute_path] = resolved_node\n      return resolved_node\n    end\n    return nil\n  end\n  return mark\nend\n\n---List marked nodes\n---@public\n---@return Node[]\nfunction Marks:list()\n  local list = {}\n  for path, mark in pairs(self.marks) do\n    local node\n    if mark == true then\n      -- Lazy resolve: try to find node in explorer tree\n      node = self.explorer:get_node_from_path(path)\n      if node then\n        -- Cache the resolved node for future access\n        self.marks[path] = node\n      end\n      -- If node not found (file deleted/moved), skip it silently\n    else\n      -- Already a node object\n      node = mark\n    end\n    if node then\n      table.insert(list, node)\n    end\n  end\n  return list\nend\n\n---Delete marked; each removal will be optionally notified\n---@public\nfunction Marks:bulk_delete()\n  if not next(self.marks) then\n    notify.warn(\"No bookmarks to delete.\")\n    return\n  end\n\n  local function execute()\n    for _, node in ipairs(self:list()) do\n      remove_file.remove(node)\n    end\n    self:clear_reload()\n  end\n\n  if self.explorer.opts.ui.confirm.remove then\n    local prompt_select = \"Remove bookmarked ?\"\n    local prompt_input = prompt_select .. \" y/N: \"\n    lib.prompt(prompt_input, prompt_select, { \"\", \"y\" }, { \"No\", \"Yes\" }, \"nvimtree_bulk_delete\", function(item_short)\n      utils.clear_prompt()\n      if item_short == \"y\" then\n        execute()\n      end\n    end)\n  else\n    execute()\n  end\nend\n\n---Trash marked; each removal will be optionally notified\n---@public\nfunction Marks:bulk_trash()\n  if not next(self.marks) then\n    notify.warn(\"No bookmarks to trash.\")\n    return\n  end\n\n  local function execute()\n    for _, node in ipairs(self:list()) do\n      trash.remove(node)\n    end\n    self:clear_reload()\n  end\n\n  if self.explorer.opts.ui.confirm.trash then\n    local prompt_select = \"Trash bookmarked ?\"\n    local prompt_input = prompt_select .. \" y/N: \"\n    lib.prompt(prompt_input, prompt_select, { \"\", \"y\" }, { \"No\", \"Yes\" }, \"nvimtree_bulk_trash\", function(item_short)\n      utils.clear_prompt()\n      if item_short == \"y\" then\n        execute()\n      end\n    end)\n  else\n    execute()\n  end\nend\n\n---Move marked\n---@public\nfunction Marks:bulk_move()\n  if not next(self.marks) then\n    notify.warn(\"No bookmarks to move.\")\n    return\n  end\n\n  local node_at_cursor = self.explorer:get_node_at_cursor()\n  local default_path = core.get_cwd()\n\n  if node_at_cursor and node_at_cursor:is(DirectoryNode) then\n    default_path = node_at_cursor.absolute_path\n  elseif node_at_cursor and node_at_cursor.parent then\n    default_path = node_at_cursor.parent.absolute_path\n  end\n\n  local input_opts = {\n    prompt = \"Move to: \",\n    default = default_path,\n    completion = \"dir\",\n  }\n\n  vim.ui.input(input_opts, function(location)\n    utils.clear_prompt()\n    if not location or location == \"\" then\n      return\n    end\n    if vim.fn.filewritable(location) ~= 2 then\n      notify.warn(location .. \" is not writable, cannot move.\")\n      return\n    end\n\n    for _, node in ipairs(self:list()) do\n      local head = vim.fn.fnamemodify(node.absolute_path, \":t\")\n      local to = utils.path_join({ location, head })\n      rename_file.rename(node, to)\n    end\n\n    self:clear_reload()\n  end)\nend\n\n---Focus nearest marked node in direction.\n---@private\n---@param up boolean\nfunction Marks:navigate(up)\n  local node = self.explorer:get_node_at_cursor()\n  if not node then\n    return\n  end\n\n  local first, prev, next, last = nil, nil, nil, nil\n  local found = false\n\n  Iterator.builder(self.explorer.nodes)\n    :recursor(function(n)\n      local dir = n:as(DirectoryNode)\n      return dir and dir.open and dir.nodes\n    end)\n    :applier(function(n)\n      if n.absolute_path == node.absolute_path then\n        found = true\n        return\n      end\n\n      if not self:get(n) then\n        return\n      end\n\n      last = n\n      first = first or n\n\n      if found and not next then\n        next = n\n      end\n\n      if not found then\n        prev = n\n      end\n    end)\n    :iterate()\n\n  if not found then\n    return\n  end\n\n  if up then\n    self.explorer:focus_node_or_parent(prev or last)\n  else\n    self.explorer:focus_node_or_parent(next or first)\n  end\nend\n\n---@public\nfunction Marks:navigate_prev()\n  self:navigate(true)\nend\n\n---@public\nfunction Marks:navigate_next()\n  self:navigate(false)\nend\n\n---Prompts for selection of a marked node, sorted by absolute paths.\n---A folder will be focused, a file will be opened.\n---@public\nfunction Marks:navigate_select()\n  local list = vim.tbl_map(function(n)\n    return n.absolute_path\n  end, self:list())\n\n  table.sort(list)\n\n  vim.ui.select(list, {\n    prompt = \"Select a file to open or a folder to focus\",\n  }, function(choice)\n    if not choice or choice == \"\" then\n      return\n    end\n    local mark = self.marks[choice]\n    local node\n    if mark == true then\n      -- Lazy resolve\n      node = self.explorer:get_node_from_path(choice)\n      if node then\n        self.marks[choice] = node\n      end\n    else\n      node = mark\n    end\n    if node and not node:is(DirectoryNode) and not utils.get_win_buf_from_path(node.absolute_path) then\n      open_file.fn(\"edit\", node.absolute_path)\n    elseif node then\n      self.explorer:focus_node_or_parent(node)\n    end\n  end)\nend\n\nreturn Marks\n"
  },
  {
    "path": "lua/nvim-tree/node/directory-link.lua",
    "content": "local git_utils = require(\"nvim-tree.git.utils\")\nlocal utils = require(\"nvim-tree.utils\")\n\nlocal DirectoryNode = require(\"nvim-tree.node.directory\")\nlocal LinkNode = require(\"nvim-tree.node.link\")\n\n---@class (exact) DirectoryLinkNode: DirectoryNode, LinkNode\nlocal DirectoryLinkNode = DirectoryNode:extend()\nDirectoryLinkNode:implement(LinkNode)\n\n---@class DirectoryLinkNode\n---@overload fun(args: LinkNodeArgs): DirectoryLinkNode\n\n---@protected\n---@param args LinkNodeArgs\nfunction DirectoryLinkNode:new(args)\n  LinkNode.new(self, args)\n\n  -- create DirectoryNode with watcher on link_to\n  local absolute_path = args.absolute_path\n  args.absolute_path = args.link_to\n  DirectoryLinkNode.super.new(self, args)\n\n  self.type          = \"link\"\n\n  -- reset absolute path to the link itself\n  self.absolute_path = absolute_path\nend\n\nfunction DirectoryLinkNode:destroy()\n  DirectoryNode.destroy(self)\nend\n\n---Update the directory git_status of link target and the file status of the link itself\n---@param parent_ignored boolean\n---@param project nvim_tree.git.Project?\nfunction DirectoryLinkNode:update_git_status(parent_ignored, project)\n  self.git_status = git_utils.git_status_dir(parent_ignored, project, self.link_to, self.absolute_path)\nend\n\n---@return nvim_tree.api.highlighted_string name\nfunction DirectoryLinkNode:highlighted_icon()\n  if not self.explorer.opts.renderer.icons.show.folder then\n    return self:highlighted_icon_empty()\n  end\n\n  local str, hl\n\n  if self.open then\n    str = self.explorer.opts.renderer.icons.glyphs.folder.symlink_open\n    hl  = \"NvimTreeOpenedFolderIcon\"\n  else\n    str = self.explorer.opts.renderer.icons.glyphs.folder.symlink\n    hl  = \"NvimTreeClosedFolderIcon\"\n  end\n\n  return { str = str, hl = { hl } }\nend\n\n---Maybe override name with arrow\n---@return nvim_tree.api.highlighted_string name\nfunction DirectoryLinkNode:highlighted_name()\n  local name = DirectoryNode.highlighted_name(self)\n\n  if self.explorer.opts.renderer.symlink_destination then\n    local link_to = utils.path_relative(self.link_to, self.explorer.absolute_path)\n    if self.explorer.opts.renderer.add_trailing then\n      link_to = utils.path_add_trailing(link_to)\n    end\n\n    name.str = string.format(\"%s%s%s\", name.str, self.explorer.opts.renderer.icons.symlink_arrow, link_to)\n    name.hl  = { \"NvimTreeSymlinkFolderName\" }\n  end\n\n  return name\nend\n\n---Create a sanitized partial copy of a node, populating children recursively.\n---@param api_nodes table<number, nvim_tree.api.Node>? optional map of uids to api node to populate\n---@return nvim_tree.api.DirectoryLinkNode cloned\nfunction DirectoryLinkNode:clone(api_nodes)\n  local clone = DirectoryNode.clone(self, api_nodes) --[[@as nvim_tree.api.DirectoryLinkNode]]\n\n  clone.link_to = self.link_to\n  clone.fs_stat_target = self.fs_stat_target\n\n  return clone\nend\n\nreturn DirectoryLinkNode\n"
  },
  {
    "path": "lua/nvim-tree/node/directory.lua",
    "content": "local git_utils = require(\"nvim-tree.git.utils\")\nlocal icons = require(\"nvim-tree.renderer.components.devicons\")\nlocal notify = require(\"nvim-tree.notify\")\nlocal Iterator = require(\"nvim-tree.iterators.node-iterator\")\n\nlocal Node = require(\"nvim-tree.node\")\n\n---@class (exact) DirectoryNode: Node\n---@field has_children boolean\n---@field group_next DirectoryNode? -- If node is grouped, this points to the next child dir/link node\n---@field nodes Node[]\n---@field open boolean\n---@field hidden_stats table? -- Each field of this table is a key for source and value for count\n---@field private watcher Watcher?\nlocal DirectoryNode = Node:extend()\n\n---@class DirectoryNode\n---@overload fun(args: NodeArgs): DirectoryNode\n\n---@protected\n---@param args NodeArgs\nfunction DirectoryNode:new(args)\n  DirectoryNode.super.new(self, args)\n\n  local handle       = vim.loop.fs_scandir(args.absolute_path)\n  local has_children = handle and vim.loop.fs_scandir_next(handle) ~= nil or false\n\n  self.type          = \"directory\"\n\n  self.has_children  = has_children\n  self.group_next    = nil\n  self.nodes         = {}\n  self.open          = false\n  self.hidden_stats  = nil\n\n  self.watcher       = require(\"nvim-tree.explorer.watch\").create_watcher(self)\nend\n\nfunction DirectoryNode:destroy()\n  self:destroy_watcher()\n\n  if self.nodes then\n    for _, node in pairs(self.nodes) do\n      node:destroy()\n    end\n  end\n\n  Node.destroy(self)\nend\n\n---Halt and remove the watcher for this node\nfunction DirectoryNode:destroy_watcher()\n  if self.watcher then\n    self.watcher:destroy()\n    self.watcher = nil\n  end\nend\n\n---Update the git_status of the directory\n---@param parent_ignored boolean\n---@param project nvim_tree.git.Project?\nfunction DirectoryNode:update_git_status(parent_ignored, project)\n  self.git_status = git_utils.git_status_dir(parent_ignored, project, self.absolute_path, nil)\nend\n\n---@return nvim_tree.git.XY[]?\nfunction DirectoryNode:get_git_xy()\n  if not self.git_status or not self.explorer.opts.git.show_on_dirs then\n    return nil\n  end\n\n  local xys = {}\n  if not self:last_group_node().open or self.explorer.opts.git.show_on_open_dirs then\n    -- dir is closed or we should show on open_dirs\n    if self.git_status.file ~= nil then\n      table.insert(xys, self.git_status.file)\n    end\n    if self.git_status.dir ~= nil then\n      if self.git_status.dir.direct ~= nil then\n        for _, s in pairs(self.git_status.dir.direct) do\n          table.insert(xys, s)\n        end\n      end\n      if self.git_status.dir.indirect ~= nil then\n        for _, s in pairs(self.git_status.dir.indirect) do\n          table.insert(xys, s)\n        end\n      end\n    end\n  else\n    -- dir is open and we shouldn't show on open_dirs\n    if self.git_status.file ~= nil then\n      table.insert(xys, self.git_status.file)\n    end\n    if self.git_status.dir ~= nil and self.git_status.dir.direct ~= nil then\n      local deleted = {\n        [\" D\"] = true,\n        [\"D \"] = true,\n        [\"RD\"] = true,\n        [\"DD\"] = true,\n      }\n      for _, s in pairs(self.git_status.dir.direct) do\n        if deleted[s] then\n          table.insert(xys, s)\n        end\n      end\n    end\n  end\n  if #xys == 0 then\n    return nil\n  else\n    return xys\n  end\nend\n\n-- If node is grouped, return the last node in the group. Otherwise, return the given node.\n---@return DirectoryNode\nfunction DirectoryNode:last_group_node()\n  return self.group_next and self.group_next:last_group_node() or self\nend\n\n---Return the one and only one child directory\n---@return DirectoryNode?\nfunction DirectoryNode:single_child_directory()\n  if #self.nodes == 1 then\n    return self.nodes[1]:as(DirectoryNode)\n  end\nend\n\n---@private\n-- Toggle group empty folders\nfunction DirectoryNode:toggle_group_folders()\n  local is_grouped = self.group_next ~= nil\n\n  if is_grouped then\n    self:ungroup_empty_folders()\n  else\n    self:group_empty_folders()\n  end\nend\n\n---Group empty folders\n-- Recursively group nodes\n---@private\n---@return Node[]\nfunction DirectoryNode:group_empty_folders()\n  local single_child = self:single_child_directory()\n  if self.explorer.opts.renderer.group_empty and self.parent and single_child then\n    self.group_next = single_child\n    local ns = single_child:group_empty_folders()\n    self.nodes = ns or {}\n    return ns\n  end\n  return self.nodes\nend\n\n---Ungroup empty folders\n-- If a node is grouped, ungroup it: put node.group_next to the node.nodes and set node.group_next to nil\n---@private\nfunction DirectoryNode:ungroup_empty_folders()\n  if self.group_next then\n    self.group_next:ungroup_empty_folders()\n    self.nodes = { self.group_next }\n    self.group_next = nil\n  end\nend\n\n---@param toggle_group boolean?\nfunction DirectoryNode:expand_or_collapse(toggle_group)\n  toggle_group = toggle_group or false\n  if self.has_children then\n    self.has_children = false\n  end\n\n  if #self.nodes == 0 then\n    self.explorer:expand_dir_node(self)\n  end\n\n  local head_node = self:get_parent_of_group() or self\n  if toggle_group then\n    head_node:toggle_group_folders()\n  end\n\n  local open = self:last_group_node().open\n  local next_open\n  if toggle_group then\n    next_open = open\n  else\n    next_open = not open\n  end\n\n  local node = head_node\n  while node do\n    node.open = next_open\n    node = node.group_next\n  end\n\n  self.explorer.renderer:draw()\nend\n\n---@return nvim_tree.api.highlighted_string icon\nfunction DirectoryNode:highlighted_icon()\n  if not self.explorer.opts.renderer.icons.show.folder then\n    return self:highlighted_icon_empty()\n  end\n\n  local str, hl\n\n  -- devicon if enabled and available\n  if self.explorer.opts.renderer.icons.web_devicons.folder.enable then\n    str, hl = icons.get_icon(self.name)\n    if not self.explorer.opts.renderer.icons.web_devicons.folder.color then\n      hl = nil\n    end\n  end\n\n  -- default icon from opts\n  if not str then\n    if #self.nodes ~= 0 or self.has_children then\n      if self.open then\n        str = self.explorer.opts.renderer.icons.glyphs.folder.open\n      else\n        str = self.explorer.opts.renderer.icons.glyphs.folder.default\n      end\n    else\n      if self.open then\n        str = self.explorer.opts.renderer.icons.glyphs.folder.empty_open\n      else\n        str = self.explorer.opts.renderer.icons.glyphs.folder.empty\n      end\n    end\n  end\n\n  -- default hl\n  if not hl then\n    if self.open then\n      hl = \"NvimTreeOpenedFolderIcon\"\n    else\n      hl = \"NvimTreeClosedFolderIcon\"\n    end\n  end\n\n  return { str = str, hl = { hl } }\nend\n\n---@return nvim_tree.api.highlighted_string icon\nfunction DirectoryNode:highlighted_name()\n  local str, hl\n\n  local name = self.name\n  local next = self.group_next\n  while next do\n    name = string.format(\"%s/%s\", name, next.name)\n    next = next.group_next\n  end\n\n  if self.group_next and type(self.explorer.opts.renderer.group_empty) == \"function\" then\n    local new_name = self.explorer.opts.renderer.group_empty(name)\n    if type(new_name) == \"string\" then\n      name = new_name\n    else\n      notify.warn(string.format(\"Invalid return type for field renderer.group_empty. Expected string, got %s\", type(new_name)))\n    end\n  end\n  str = string.format(\"%s%s\", name, self.explorer.opts.renderer.add_trailing and \"/\" or \"\")\n\n  hl = \"NvimTreeFolderName\"\n  if vim.tbl_contains(self.explorer.opts.renderer.special_files, self.absolute_path) or vim.tbl_contains(self.explorer.opts.renderer.special_files, self.name) then\n    hl = \"NvimTreeSpecialFolderName\"\n  elseif self.open then\n    hl = \"NvimTreeOpenedFolderName\"\n  elseif #self.nodes == 0 and not self.has_children then\n    hl = \"NvimTreeEmptyFolderName\"\n  end\n\n  return { str = str, hl = { hl } }\nend\n\n---Create a sanitized partial copy of a node, populating children recursively.\n---@param api_nodes table<number, nvim_tree.api.Node>? optional map of uids to api node to populate\n---@return nvim_tree.api.DirectoryNode cloned\nfunction DirectoryNode:clone(api_nodes)\n  local clone        = Node.clone(self, api_nodes) --[[@as nvim_tree.api.DirectoryNode]]\n\n  clone.has_children = self.has_children\n  clone.nodes        = {}\n  clone.open         = self.open\n\n  local clone_child\n  for _, child in ipairs(self.nodes) do\n    clone_child = child:clone(api_nodes)\n    clone_child.parent = clone\n    table.insert(clone.nodes, clone_child)\n  end\n\n  return clone\nend\n\n---@private\n---@param should_descend fun(expansion_count: integer, node: Node): boolean\n---@return fun(expansion_count: integer, node: Node): boolean\nfunction DirectoryNode:limit_folder_discovery(should_descend)\n  local MAX_FOLDER_DISCOVERY = self.explorer.opts.actions.expand_all.max_folder_discovery\n  return function(expansion_count, node)\n    local should_halt = expansion_count >= MAX_FOLDER_DISCOVERY\n    if should_halt then\n      notify.warn(\"expansion iteration was halted after \" .. MAX_FOLDER_DISCOVERY .. \" discovered folders\")\n      return false\n    end\n\n    return should_descend(expansion_count, node)\n  end\nend\n\n---@param expansion_count integer\n---@param should_descend fun(expansion_count: integer, node: Node): boolean\n---@return boolean\nfunction DirectoryNode:should_expand(expansion_count, should_descend)\n  if not self.open and should_descend(expansion_count, self) then\n    if #self.nodes == 0 then\n      self.explorer:expand_dir_node(self) -- populate node.group_next\n    end\n\n    if self.group_next then\n      local expand_next = self.group_next:should_expand(expansion_count, should_descend)\n      if expand_next then\n        self.open = true\n      end\n      return expand_next\n    else\n      return true\n    end\n  end\n  return false\nend\n\n---@param list string[]\n---@return table\nlocal function to_lookup_table(list)\n  local table = {}\n  for _, element in ipairs(list) do\n    table[element] = true\n  end\n\n  return table\nend\n\n---@param _ integer\n---@param node Node\n---@return boolean\nlocal function descend_until_empty(_, node)\n  local EXCLUDE = to_lookup_table(node.explorer.opts.actions.expand_all.exclude)\n  local should_exclude = EXCLUDE[node.name]\n  return not should_exclude\nend\n\n---@param expand_opts? nvim_tree.api.node.expand.Opts\nfunction DirectoryNode:expand(expand_opts)\n  local expansion_count = 0\n\n  local should_descend = self:limit_folder_discovery((expand_opts and expand_opts.expand_until) or descend_until_empty)\n\n\n  if self.parent and self.nodes and not self.open then\n    expansion_count = expansion_count + 1\n    self:expand_dir_node()\n  end\n\n  Iterator.builder(self.nodes)\n    :hidden()\n    :applier(function(node)\n      if node:should_expand(expansion_count, should_descend) then\n        expansion_count = expansion_count + 1\n        node:expand_dir_node()\n      end\n    end)\n    :recursor(function(node)\n      if not should_descend(expansion_count, node) then\n        return nil\n      end\n\n      if node.group_next then\n        return { node.group_next }\n      end\n\n      if node.open and node.nodes then\n        return node.nodes\n      end\n\n      return nil\n    end)\n    :iterate()\n\n  self.explorer.renderer:draw()\nend\n\nfunction DirectoryNode:expand_dir_node()\n  local node = self:last_group_node()\n  node.open = true\n  if #node.nodes == 0 then\n    self.explorer:expand_dir_node(node)\n  end\nend\n\nreturn DirectoryNode\n"
  },
  {
    "path": "lua/nvim-tree/node/factory.lua",
    "content": "local DirectoryLinkNode = require(\"nvim-tree.node.directory-link\")\nlocal DirectoryNode = require(\"nvim-tree.node.directory\")\nlocal FileLinkNode = require(\"nvim-tree.node.file-link\")\nlocal FileNode = require(\"nvim-tree.node.file\")\nlocal Watcher = require(\"nvim-tree.watcher\")\n\nlocal M = {}\n\n---Factory function to create the appropriate Node\n---nil on invalid stat or invalid link target stat\n---@param args NodeArgs\n---@return Node?\nfunction M.create(args)\n  if not args.fs_stat then\n    return nil\n  end\n\n  if args.fs_stat.type == \"directory\" then\n    -- directory must be readable and enumerable\n    if vim.loop.fs_access(args.absolute_path, \"R\") and Watcher.is_fs_event_capable(args.absolute_path) then\n      return DirectoryNode(args)\n    end\n  elseif args.fs_stat.type == \"file\" then\n    return FileNode(args)\n  elseif args.fs_stat.type == \"link\" then\n    -- link target path and stat must resolve\n    local link_to = vim.loop.fs_realpath(args.absolute_path)\n    local link_to_stat = link_to and vim.loop.fs_stat(link_to)\n    if not link_to or not link_to_stat then\n      return\n    end\n\n    ---@cast args LinkNodeArgs\n    args.link_to        = link_to\n    args.fs_stat_target = link_to_stat\n\n    -- choose directory or file\n    if link_to_stat.type == \"directory\" then\n      return DirectoryLinkNode(args)\n    else\n      return FileLinkNode(args)\n    end\n  end\n\n  return nil\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/node/file-link.lua",
    "content": "local git_utils = require(\"nvim-tree.git.utils\")\nlocal utils = require(\"nvim-tree.utils\")\n\nlocal FileNode = require(\"nvim-tree.node.file\")\nlocal LinkNode = require(\"nvim-tree.node.link\")\n\n---@class (exact) FileLinkNode: FileNode, LinkNode\nlocal FileLinkNode = FileNode:extend()\nFileLinkNode:implement(LinkNode)\n\n---@class FileLinkNode\n---@overload fun(args: LinkNodeArgs): FileLinkNode\n\n---@protected\n---@param args LinkNodeArgs\nfunction FileLinkNode:new(args)\n  LinkNode.new(self, args)\n  FileLinkNode.super.new(self, args)\n\n  self.type = \"link\"\nend\n\nfunction FileLinkNode:destroy()\n  FileNode.destroy(self)\nend\n\n---Update the git_status of the target otherwise the link itself\n---@param parent_ignored boolean\n---@param project nvim_tree.git.Project?\nfunction FileLinkNode:update_git_status(parent_ignored, project)\n  self.git_status = git_utils.git_status_file(parent_ignored, project, self.link_to, self.absolute_path)\nend\n\n---@return nvim_tree.api.highlighted_string icon\nfunction FileLinkNode:highlighted_icon()\n  if not self.explorer.opts.renderer.icons.show.file then\n    return self:highlighted_icon_empty()\n  end\n\n  local str, hl\n\n  -- default icon from opts\n  str = self.explorer.opts.renderer.icons.glyphs.symlink\n  hl = \"NvimTreeSymlinkIcon\"\n\n  return { str = str, hl = { hl } }\nend\n\n---@return nvim_tree.api.highlighted_string name\nfunction FileLinkNode:highlighted_name()\n  local str = self.name\n  if self.explorer.opts.renderer.symlink_destination then\n    local link_to = utils.path_relative(self.link_to, self.explorer.absolute_path)\n    str = string.format(\"%s%s%s\", str, self.explorer.opts.renderer.icons.symlink_arrow, link_to)\n  end\n\n  return { str = str, hl = { \"NvimTreeSymlink\" } }\nend\n\n---Create a sanitized partial copy of a node\n---@param api_nodes table<number, nvim_tree.api.Node>? optional map of uids to api node to populate\n---@return nvim_tree.api.FileLinkNode cloned\nfunction FileLinkNode:clone(api_nodes)\n  local clone = FileNode.clone(self, api_nodes) --[[@as nvim_tree.api.FileLinkNode]]\n\n  clone.link_to = self.link_to\n  clone.fs_stat_target = self.fs_stat_target\n\n  return clone\nend\n\nreturn FileLinkNode\n"
  },
  {
    "path": "lua/nvim-tree/node/file.lua",
    "content": "local git_utils = require(\"nvim-tree.git.utils\")\nlocal icons = require(\"nvim-tree.renderer.components.devicons\")\nlocal utils = require(\"nvim-tree.utils\")\n\nlocal Node = require(\"nvim-tree.node\")\n\nlocal PICTURE_MAP = {\n  jpg = true,\n  jpeg = true,\n  png = true,\n  gif = true,\n  webp = true,\n  jxl = true,\n}\n\n---@class (exact) FileNode: Node\n---@field extension string\nlocal FileNode = Node:extend()\n\n---@class FileNode\n---@overload fun(args: NodeArgs): FileNode\n\n---@protected\n---@param args NodeArgs\nfunction FileNode:new(args)\n  FileNode.super.new(self, args)\n\n  self.type       = \"file\"\n  self.extension  = string.match(args.name, \".?[^.]+%.(.*)\") or \"\"\n  self.executable = utils.is_executable(args.absolute_path)\nend\n\nfunction FileNode:destroy()\n  Node.destroy(self)\nend\n\n---Update the GitStatus of the file\n---@param parent_ignored boolean\n---@param project nvim_tree.git.Project?\nfunction FileNode:update_git_status(parent_ignored, project)\n  self.git_status = git_utils.git_status_file(parent_ignored, project, self.absolute_path, nil)\nend\n\n---@return nvim_tree.git.XY[]?\nfunction FileNode:get_git_xy()\n  if not self.git_status then\n    return nil\n  end\n\n  return self.git_status.file and { self.git_status.file }\nend\n\n---@return nvim_tree.api.highlighted_string icon\nfunction FileNode:highlighted_icon()\n  if not self.explorer.opts.renderer.icons.show.file then\n    return self:highlighted_icon_empty()\n  end\n\n  local str, hl\n\n  -- devicon if enabled and available, fallback to default\n  if self.explorer.opts.renderer.icons.web_devicons.file.enable then\n    str, hl = icons.get_icon(self.name, nil, { default = true })\n    if not self.explorer.opts.renderer.icons.web_devicons.file.color then\n      hl = nil\n    end\n  end\n\n  -- default icon from opts\n  if not str then\n    str = self.explorer.opts.renderer.icons.glyphs.default\n  end\n\n  -- default hl\n  if not hl then\n    hl = \"NvimTreeFileIcon\"\n  end\n\n  return { str = str, hl = { hl } }\nend\n\n---@return nvim_tree.api.highlighted_string name\nfunction FileNode:highlighted_name()\n  local hl\n  if vim.tbl_contains(self.explorer.opts.renderer.special_files, self.absolute_path) or vim.tbl_contains(self.explorer.opts.renderer.special_files, self.name) then\n    hl = \"NvimTreeSpecialFile\"\n  elseif self.executable then\n    hl = \"NvimTreeExecFile\"\n  elseif PICTURE_MAP[self.extension] then\n    hl = \"NvimTreeImageFile\"\n  end\n\n  return { str = self.name, hl = { hl } }\nend\n\n---Create a sanitized partial copy of a node\n---@param api_nodes table<number, nvim_tree.api.Node>? optional map of uids to api node to populate\n---@return nvim_tree.api.FileNode cloned\nfunction FileNode:clone(api_nodes)\n  local clone = Node.clone(self, api_nodes) --[[@as nvim_tree.api.FileNode]]\n\n  clone.extension = self.extension\n\n  return clone\nend\n\nreturn FileNode\n"
  },
  {
    "path": "lua/nvim-tree/node/init.lua",
    "content": "local Class = require(\"nvim-tree.classic\")\n\n---Abstract Node class.\n---@class (exact) Node: nvim_tree.Class\n---@field uid_node number vim.loop.hrtime() at construction time\n---@field type \"file\" | \"directory\" | \"link\" uv.fs_stat.result.type\n---@field explorer Explorer\n---@field absolute_path string\n---@field executable boolean\n---@field fs_stat uv.fs_stat.result?\n---@field git_status nvim_tree.git.Status?\n---@field hidden boolean\n---@field name string\n---@field parent DirectoryNode?\n---@field diag_status DiagStatus?\n---@field private is_dot boolean cached is_dotfile\nlocal Node = Class:extend()\n\n---@class (exact) NodeArgs\n---@field explorer Explorer\n---@field parent DirectoryNode?\n---@field absolute_path string\n---@field name string\n---@field fs_stat uv.fs_stat.result?\n\n---@protected\n---@param args NodeArgs\nfunction Node:new(args)\n  self.uid_node      = vim.loop.hrtime()\n  self.explorer      = args.explorer\n  self.absolute_path = args.absolute_path\n  self.executable    = false\n  self.fs_stat       = args.fs_stat\n  self.git_status    = nil\n  self.hidden        = false\n  self.name          = args.name\n  self.parent        = args.parent\n  self.diag_status   = nil\n  self.is_dot        = false\nend\n\nfunction Node:destroy()\nend\n\n---Update the git_status of the node\n---Abstract\n---@param parent_ignored boolean\n---@param project nvim_tree.git.Project?\nfunction Node:update_git_status(parent_ignored, project)\n  self:nop(parent_ignored, project)\nend\n\n---Short-format statuses\n---@return nvim_tree.git.XY[]?\nfunction Node:get_git_xy()\nend\n\n---@return boolean\nfunction Node:is_git_ignored()\n  return self.git_status ~= nil and self.git_status.file == \"!!\"\nend\n\n---Node or one of its parents begins with a dot\n---@return boolean\nfunction Node:is_dotfile()\n  if\n    self.is_dot\n    or (self.name and (self.name:sub(1, 1) == \".\"))\n    or (self.parent and self.parent:is_dotfile())\n  then\n    self.is_dot = true\n    return true\n  end\n  return false\nend\n\n---Get the highest parent of grouped nodes, nil when not grouped\n---@return DirectoryNode?\nfunction Node:get_parent_of_group()\n  if not self.parent or not self.parent.group_next then\n    return nil\n  end\n\n  local node = self.parent\n  while node do\n    if node.parent and node.parent.group_next then\n      node = node.parent\n    else\n      return node\n    end\n  end\nend\n\n---Empty highlighted icon\n---@protected\n---@return nvim_tree.api.highlighted_string icon\nfunction Node:highlighted_icon_empty()\n  return { str = \"\", hl = {} }\nend\n\n---Highlighted icon for the node\n---Empty for base Node\n---@return nvim_tree.api.highlighted_string icon\nfunction Node:highlighted_icon()\n  return self:highlighted_icon_empty()\nend\n\n---Empty highlighted name\n---@protected\n---@return nvim_tree.api.highlighted_string name\nfunction Node:highlighted_name_empty()\n  return { str = \"\", hl = {} }\nend\n\n---Highlighted name for the node\n---Empty for base Node\n---@return nvim_tree.api.highlighted_string name\nfunction Node:highlighted_name()\n  return self:highlighted_name_empty()\nend\n\n---Create a sanitized partial copy of a node, populating children recursively.\n---@param api_nodes table<number, nvim_tree.api.Node>? optional map of uids to api node to populate\n---@return nvim_tree.api.Node cloned\nfunction Node:clone(api_nodes)\n  ---@type nvim_tree.api.Node\n  local clone = {\n    uid_node      = self.uid_node,\n    type          = self.type,\n    absolute_path = self.absolute_path,\n    executable    = self.executable,\n    fs_stat       = self.fs_stat,\n    git_status    = self.git_status,\n    hidden        = self.hidden,\n    name          = self.name,\n    parent        = nil,\n    diag_severity = self.diag_status and self.diag_status.value or nil,\n  }\n\n  if api_nodes then\n    api_nodes[self.uid_node] = clone\n  end\n\n  return clone\nend\n\n---@param expansion_count integer\n---@param should_descend fun(expansion_count: integer, node: Node): boolean\n---@return boolean\nfunction Node:should_expand(expansion_count, should_descend)\n  self:nop(expansion_count, should_descend)\n  return false\nend\n\n---@param expand_opts? nvim_tree.api.node.expand.Opts\nfunction Node:expand(expand_opts)\n  if self.parent then\n    self.parent:expand(expand_opts)\n  end\nend\n\nreturn Node\n"
  },
  {
    "path": "lua/nvim-tree/node/link.lua",
    "content": "local Class = require(\"nvim-tree.classic\")\n\n---@class (exact) LinkNode: nvim_tree.Class\n---@field link_to string\n---@field fs_stat_target uv.fs_stat.result\nlocal LinkNode = Class:extend()\n\n---@class (exact) LinkNodeArgs: NodeArgs\n---@field link_to string\n---@field fs_stat_target uv.fs_stat.result\n\n---@protected\n---@param args LinkNodeArgs\nfunction LinkNode:new(args)\n  self.link_to = args.link_to\n  self.fs_stat_target = args.fs_stat_target\nend\n\nreturn LinkNode\n"
  },
  {
    "path": "lua/nvim-tree/node/root.lua",
    "content": "local DirectoryNode = require(\"nvim-tree.node.directory\")\n\n---@class (exact) RootNode: DirectoryNode\nlocal RootNode = DirectoryNode:extend()\n\n---@class RootNode\n---@overload fun(args: NodeArgs): RootNode\n\n---@protected\n---@param args NodeArgs\nfunction RootNode:new(args)\n  RootNode.super.new(self, args)\nend\n\n---Root is never a dotfile\n---@return boolean\nfunction RootNode:is_dotfile()\n  return false\nend\n\nfunction RootNode:destroy()\n  DirectoryNode.destroy(self)\nend\n\n---Create a sanitized partial copy of a node, populating children recursively.\n---@param api_nodes table<number, nvim_tree.api.Node>? optional map of uids to api node to populate\n---@return nvim_tree.api.RootNode cloned\nfunction RootNode:clone(api_nodes)\n  local clone = DirectoryNode.clone(self, api_nodes) --[[@as nvim_tree.api.RootNode]]\n\n  return clone\nend\n\nreturn RootNode\n"
  },
  {
    "path": "lua/nvim-tree/notify.lua",
    "content": "local M = {}\n\nlocal config = {\n  threshold = vim.log.levels.INFO,\n  absolute_path = true,\n}\n\nlocal title_support\n---@return boolean\nfunction M.supports_title()\n  if title_support == nil then\n    title_support = (package.loaded.notify and (vim.notify == require(\"notify\") or vim.notify == require(\"notify\").notify))\n      or (package.loaded.noice and (vim.notify == require(\"noice\").notify or vim.notify == require(\"noice.source.notify\").notify))\n      or (package.loaded.notifier and require(\"notifier.config\").has_component(\"nvim\"))\n      or false\n  end\n\n  return title_support\nend\n\nlocal modes = {\n  { name = \"trace\", level = vim.log.levels.TRACE },\n  { name = \"debug\", level = vim.log.levels.DEBUG },\n  { name = \"info\",  level = vim.log.levels.INFO },\n  { name = \"warn\",  level = vim.log.levels.WARN },\n  { name = \"error\", level = vim.log.levels.ERROR },\n}\n\ndo\n  local dispatch = function(level, msg)\n    if level < config.threshold or not msg then\n      return\n    end\n\n    vim.schedule(function()\n      if not M.supports_title() then\n        -- add title to the message, with a newline if the message is multiline\n        msg = string.format(\"[NvimTree]%s%s\", (msg:match(\"\\n\") and \"\\n\" or \" \"), msg)\n      end\n\n      vim.notify(msg, level, { title = \"NvimTree\" })\n    end)\n  end\n\n  for _, x in ipairs(modes) do\n    M[x.name] = function(msg)\n      return dispatch(x.level, msg)\n    end\n  end\nend\n\n---@param path string\n---@return string\nfunction M.render_path(path)\n  if config.absolute_path then\n    return path\n  else\n    return vim.fn.fnamemodify(path .. \"/\", \":h:t\")\n  end\nend\n\nfunction M.setup(opts)\n  opts = opts or {}\n  config.threshold = opts.notify.threshold or vim.log.levels.INFO\n  config.absolute_path = opts.notify.absolute_path\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/renderer/builder.lua",
    "content": "local notify = require(\"nvim-tree.notify\")\nlocal utils = require(\"nvim-tree.utils\")\nlocal view = require(\"nvim-tree.view\")\n\nlocal Class = require(\"nvim-tree.classic\")\n\nlocal DirectoryNode = require(\"nvim-tree.node.directory\")\n\nlocal BookmarkDecorator = require(\"nvim-tree.renderer.decorator.bookmarks\")\nlocal CopiedDecorator = require(\"nvim-tree.renderer.decorator.copied\")\nlocal CutDecorator = require(\"nvim-tree.renderer.decorator.cut\")\nlocal DiagnosticsDecorator = require(\"nvim-tree.renderer.decorator.diagnostics\")\nlocal GitDecorator = require(\"nvim-tree.renderer.decorator.git\")\nlocal HiddenDecorator = require(\"nvim-tree.renderer.decorator.hidden\")\nlocal ModifiedDecorator = require(\"nvim-tree.renderer.decorator.modified\")\nlocal OpenDecorator = require(\"nvim-tree.renderer.decorator.opened\")\nlocal Decorator = require(\"nvim-tree.renderer.decorator\")\nlocal BuiltinDecorator = require(\"nvim-tree.renderer.decorator.builtin\")\n\nlocal pad = require(\"nvim-tree.renderer.components.padding\")\n\n-- Builtin Decorators\n---@type table<nvim_tree.config.renderer.decorator, BuiltinDecorator>\nlocal BUILTIN_DECORATORS = {\n  Git = GitDecorator,\n  Open = OpenDecorator,\n  Hidden = HiddenDecorator,\n  Modified = ModifiedDecorator,\n  Bookmark = BookmarkDecorator,\n  Diagnostics = DiagnosticsDecorator,\n  Copied = CopiedDecorator,\n  Cut = CutDecorator,\n}\n\n---@class (exact) Builder\n---@field lines string[] includes icons etc.\n---@field hl_range_args HighlightRangeArgs[] highlights for lines\n---@field signs string[] line signs\n---@field extmarks table[] extra marks for right icon placement\n---@field virtual_lines table[] virtual lines for hidden count display\n---@field private explorer Explorer\n---@field private index number\n---@field private depth number\n---@field private combined_groups table<string, boolean> combined group names\n---@field private markers boolean[] indent markers\n---@field private decorators Decorator[]\n---@field private hidden_display fun(node: Node): string|nil\n---@field private api_nodes table<number, nvim_tree.api.Node>? optional map of uids to api node for user decorators\nlocal Builder = Class:extend()\n\n---@class Builder\n---@overload fun(args: BuilderArgs): Builder\n\n---@class (exact) BuilderArgs\n---@field explorer Explorer\n\n---@protected\n---@param args BuilderArgs\nfunction Builder:new(args)\n  self.explorer        = args.explorer\n  self.index           = 0\n  self.depth           = 0\n  self.hl_range_args   = {}\n  self.combined_groups = {}\n  self.lines           = {}\n  self.markers         = {}\n  self.signs           = {}\n  self.extmarks        = {}\n  self.virtual_lines   = {}\n  self.decorators      = {}\n  self.hidden_display  = Builder:setup_hidden_display_function(self.explorer.opts)\n\n  -- instantiate all the builtin and user decorator instances\n  local builtin, user\n  for _, d in ipairs(self.explorer.opts.renderer.decorators) do\n    ---@type BuiltinDecorator\n    builtin = BUILTIN_DECORATORS[d]\n\n    ---@type Decorator\n    user = type(d) == \"table\" and type(d.as) == \"function\" and d:as(Decorator)\n\n    if builtin then\n      table.insert(self.decorators, builtin({ explorer = self.explorer }))\n    elseif user then\n      table.insert(self.decorators, user())\n\n      -- clone user nodes once\n      if not self.api_nodes then\n        self.api_nodes = {}\n        self.explorer:clone(self.api_nodes)\n      end\n    end\n  end\nend\n\n---Insert ranged highlight groups into self.highlights\n---@private\n---@param groups string[]\n---@param start number\n---@param end_ number|nil\nfunction Builder:insert_highlight(groups, start, end_)\n  for _, higroup in ipairs(groups) do\n    table.insert(self.hl_range_args, { higroup = higroup, start = { self.index, start, }, finish = { self.index, end_ or -1, } })\n  end\nend\n\n---@private\n---@param highlighted_strings nvim_tree.api.highlighted_string[]\n---@return string\nfunction Builder:unwrap_highlighted_strings(highlighted_strings)\n  if not highlighted_strings then\n    return \"\"\n  end\n\n  local string = \"\"\n  for _, v in ipairs(highlighted_strings) do\n    if #v.str > 0 then\n      if v.hl and type(v.hl) == \"table\" then\n        self:insert_highlight(v.hl, #string, #string + #v.str)\n      end\n      string = string.format(\"%s%s\", string, v.str)\n    end\n  end\n  return string\nend\n\n---@private\n---@param indent_markers nvim_tree.api.highlighted_string[]\n---@param arrows? nvim_tree.api.highlighted_string[]\n---@param icon nvim_tree.api.highlighted_string\n---@param name nvim_tree.api.highlighted_string\n---@param node Node\n---@return nvim_tree.api.highlighted_string[]\nfunction Builder:format_line(indent_markers, arrows, icon, name, node)\n  local added_len = 0\n  local function add_to_end(t1, t2)\n    if not t2 or vim.tbl_isempty(t2) then\n      return\n    end\n    for _, v in ipairs(t2) do\n      if added_len > 0 then\n        table.insert(t1, { str = self.explorer.opts.renderer.icons.padding.icon })\n      end\n      table.insert(t1, v)\n    end\n\n    -- first add_to_end don't need padding\n    -- hence added_len is calculated at the end to be used next time\n    added_len = 0\n    for _, v in ipairs(t2) do\n      added_len = added_len + #v.str\n    end\n  end\n\n  local api_node = self.api_nodes and self.api_nodes[node.uid_node]\n  local b\n\n  local line = { indent_markers, arrows }\n  add_to_end(line, { icon })\n\n  for _, d in ipairs(self.decorators) do\n    b = d:as(BuiltinDecorator)\n    if b then\n      add_to_end(line, b:icons_before(node))\n    elseif api_node then\n      add_to_end(line, d:icons_before(api_node))\n    end\n  end\n\n  add_to_end(line, { name })\n\n  for _, d in ipairs(self.decorators) do\n    b = d:as(BuiltinDecorator)\n    if b then\n      add_to_end(line, b:icons_after(node))\n    elseif api_node then\n      add_to_end(line, d:icons_after(api_node))\n    end\n  end\n\n  local rights = {}\n  for _, d in ipairs(self.decorators) do\n    b = d:as(BuiltinDecorator)\n    if b then\n      add_to_end(rights, b:icons_right_align(node))\n    elseif api_node then\n      add_to_end(rights, d:icons_right_align(api_node))\n    end\n  end\n  if #rights > 0 then\n    self.extmarks[self.index] = rights\n  end\n\n  return line\nend\n\n---@private\n---@param node Node\nfunction Builder:build_signs(node)\n  local api_node = self.api_nodes and self.api_nodes[node.uid_node]\n\n  -- first in priority order\n  local d, b, sign_name\n  for i = #self.decorators, 1, -1 do\n    d = self.decorators[i]\n\n    b = d:as(BuiltinDecorator)\n    if b then\n      sign_name = b:sign_name(node)\n    elseif api_node then\n      sign_name = d:sign_name(api_node)\n    end\n\n    if sign_name then\n      self.signs[self.index] = sign_name\n      break\n    end\n  end\nend\n\n---Create a highlight group for groups with later groups overriding previous.\n---Combined group name is less than the 200 byte limit of highlight group names\n---@private\n---@param groups string[] highlight group names\n---@return string group_name \"NvimTreeCombinedHL\" .. sha256\nfunction Builder:create_combined_group(groups)\n  local combined_name = string.format(\"NvimTreeCombinedHL%s\", vim.fn.sha256(table.concat(groups)))\n\n  -- only create if necessary\n  if not self.combined_groups[combined_name] then\n    self.combined_groups[combined_name] = true\n    local combined_hl = {}\n\n    -- build the highlight, overriding values\n    for _, group in ipairs(groups) do\n      local hl = vim.api.nvim_get_hl(0, { name = group, link = false })\n      combined_hl = vim.tbl_extend(\"force\", combined_hl, hl)\n    end\n\n    -- add highlights to the global namespace\n    vim.api.nvim_set_hl(0, combined_name, combined_hl)\n\n    table.insert(self.combined_groups, combined_name)\n  end\n\n  return combined_name\nend\n\n---Calculate decorated icon and name for a node.\n---A combined highlight group will be created when there is more than one highlight.\n---A highlight group is always calculated and upserted for the case of highlights changing.\n---@private\n---@param node Node\n---@return nvim_tree.api.highlighted_string icon\n---@return nvim_tree.api.highlighted_string name\nfunction Builder:icon_name_decorated(node)\n  local api_node = self.api_nodes and self.api_nodes[node.uid_node]\n\n  -- base case\n  local icon = node:highlighted_icon()\n  local name = node:highlighted_name()\n\n  -- calculate node icon and all decorated highlight groups\n  local icon_groups = {}\n  local name_groups = {}\n  local hl_icon, hl_name\n  local b\n  for _, d in ipairs(self.decorators) do\n    -- maybe override icon\n    b = d:as(BuiltinDecorator)\n    if b then\n      icon = b:icon_node(node) or icon\n      hl_icon, hl_name = b:highlight_group_icon_name(node)\n    elseif api_node then\n      icon = d:icon_node(api_node) or icon\n      hl_icon, hl_name = d:highlight_group_icon_name(api_node)\n    end\n\n    table.insert(icon_groups, hl_icon)\n    table.insert(name_groups, hl_name)\n  end\n\n  -- add one or many icon groups\n  if #icon_groups > 1 then\n    table.insert(icon.hl, self:create_combined_group(icon_groups))\n  else\n    table.insert(icon.hl, icon_groups[1])\n  end\n\n  -- add one or many name groups\n  if #name_groups > 1 then\n    table.insert(name.hl, self:create_combined_group(name_groups))\n  else\n    table.insert(name.hl, name_groups[1])\n  end\n\n  return icon, name\nend\n\n---Insert node line into self.lines, calling Builder:build_lines for each directory\n---@private\n---@param node Node\n---@param idx integer line number starting at 1\n---@param num_children integer of node\nfunction Builder:build_line(node, idx, num_children)\n  -- various components\n  local indent_markers = pad.get_indent_markers(self.depth, idx, num_children, node, self.markers)\n  local arrows = pad.get_arrows(node)\n\n  -- decorated node icon and name\n  local icon, name = self:icon_name_decorated(node)\n\n  local line = self:format_line(indent_markers, arrows, icon, name, node)\n  table.insert(self.lines, self:unwrap_highlighted_strings(line))\n\n  self.index = self.index + 1\n\n  local dir = node:as(DirectoryNode)\n  if dir then\n    dir = dir:last_group_node()\n    if dir.open then\n      self.depth = self.depth + 1\n      self:build_lines(dir)\n      self.depth = self.depth - 1\n    end\n  end\nend\n\n---Add virtual lines for rendering hidden count information per node\n---@private\nfunction Builder:add_hidden_count_string(node, idx, num_children)\n  if not node.open then\n    return\n  end\n  local hidden_count_string = self.hidden_display(node.hidden_stats)\n  if hidden_count_string and hidden_count_string ~= \"\" then\n    local indent_markers = pad.get_indent_markers(self.depth, idx or 0, num_children or 0, node, self.markers, 1)\n    local indent_width = self.explorer.opts.renderer.indent_width\n\n    local indent_padding = string.rep(\" \", indent_width)\n    local indent_string = indent_padding .. indent_markers.str\n    local line_nr = #self.lines - 1\n    self.virtual_lines[line_nr] = self.virtual_lines[line_nr] or {}\n\n    -- NOTE: We are inserting in depth order because of current traversal\n    -- if we change the traversal, we might need to sort by depth before rendering `self.virtual_lines`\n    -- to maintain proper ordering of parent and child folder hidden count info.\n    table.insert(self.virtual_lines[line_nr], {\n      { indent_string,                                                                      indent_markers.hl },\n      { string.rep(indent_padding, (node.parent == nil and 0 or 1)) .. hidden_count_string, \"NvimTreeHiddenDisplay\" },\n    })\n  end\nend\n\n---Number of visible nodes\n---@private\n---@param nodes Node[]\n---@return integer\nfunction Builder:num_visible(nodes)\n  if not self.explorer.live_filter.filter then\n    return #nodes\n  end\n\n  local i = 0\n  for _, n in pairs(nodes) do\n    if not n.hidden then\n      i = i + 1\n    end\n  end\n  return i\nend\n\n---@private\nfunction Builder:build_lines(node)\n  if not node then\n    node = self.explorer\n  end\n  local num_children = self:num_visible(node.nodes)\n  local idx = 1\n  for _, n in ipairs(node.nodes) do\n    if not n.hidden then\n      self:build_signs(n)\n      self:build_line(n, idx, num_children)\n      idx = idx + 1\n    end\n  end\n  self:add_hidden_count_string(node)\nend\n\n---@private\n---@param root_label function|string\n---@return string\nfunction Builder:format_root_name(root_label)\n  if type(root_label) == \"function\" then\n    local label = root_label(self.explorer.absolute_path)\n    if type(label) == \"string\" then\n      return label\n    end\n  elseif type(root_label) == \"string\" then\n    return utils.path_remove_trailing(vim.fn.fnamemodify(self.explorer.absolute_path, root_label))\n  end\n  return \"???\"\nend\n\n---@private\nfunction Builder:build_header()\n  if view.is_root_folder_visible(self.explorer.absolute_path) then\n    local root_name = self:format_root_name(self.explorer.opts.renderer.root_folder_label)\n    table.insert(self.lines, root_name)\n    self:insert_highlight({ \"NvimTreeRootFolder\" }, 0, string.len(root_name))\n    self.index = 1\n  end\n\n  if self.explorer.live_filter.filter then\n    local filter_line = string.format(\"%s/%s/\", self.explorer.opts.live_filter.prefix, self.explorer.live_filter.filter)\n    table.insert(self.lines, filter_line)\n    local prefix_length = string.len(self.explorer.opts.live_filter.prefix)\n    self:insert_highlight({ \"NvimTreeLiveFilterPrefix\" }, 0,             prefix_length)\n    self:insert_highlight({ \"NvimTreeLiveFilterValue\" },  prefix_length, string.len(filter_line))\n    self.index = self.index + 1\n  end\nend\n\n---Sanitize lines for rendering.\n---Replace newlines with literal \\n\n---@private\nfunction Builder:sanitize_lines()\n  self.lines = vim.tbl_map(function(line)\n    return line and line:gsub(\"\\n\", \"\\\\n\") or \"\"\n  end, self.lines)\nend\n\n---Build all lines with highlights and signs\n---@return Builder\nfunction Builder:build()\n  self:build_header()\n  self:build_lines()\n  self:sanitize_lines()\n  return self\nend\n\n---@private\n---@param opts table\n---@return fun(node: Node): string|nil\nfunction Builder:setup_hidden_display_function(opts)\n  local hidden_display = opts.renderer.hidden_display\n  -- options are already validated, so ´hidden_display´ can ONLY be `string` or `function` if type(hidden_display) == \"string\" then\n  if type(hidden_display) == \"string\" then\n    if hidden_display == \"none\" then\n      return function()\n        return nil\n      end\n    elseif hidden_display == \"simple\" then\n      return function(hidden_stats)\n        return utils.default_format_hidden_count(hidden_stats, true)\n      end\n    else -- \"all\"\n      return function(hidden_stats)\n        return utils.default_format_hidden_count(hidden_stats, false)\n      end\n    end\n  else -- \"function\n    return function(hidden_stats)\n      -- In case of missing field such as live_filter we zero it, otherwise keep field as is\n      hidden_stats = vim.tbl_deep_extend(\"force\", {\n        live_filter = 0,\n        git         = 0,\n        buf         = 0,\n        dotfile     = 0,\n        custom      = 0,\n        bookmark    = 0,\n      }, hidden_stats or {})\n\n      local ok, result = pcall(hidden_display, hidden_stats)\n      if not ok then\n        notify.warn(\n          \"Problem occurred in the function ``opts.renderer.hidden_display`` see nvim-tree.renderer.hidden_display on :h nvim-tree\")\n        return nil\n      end\n      return result\n    end\n  end\nend\n\nreturn Builder\n"
  },
  {
    "path": "lua/nvim-tree/renderer/components/devicons.lua",
    "content": "---@alias devicons_get_icon fun(name: string, ext: string?, opts: table?): string?, string?\n---@alias devicons_setup fun(opts: table?)\n\n---@class (strict) DevIcons?\n---@field setup devicons_setup\n---@field get_icon devicons_get_icon\nlocal devicons\n\nlocal M = {}\n\n---Wrapper around nvim-web-devicons, nils if devicons not available\n---@type devicons_get_icon\nfunction M.get_icon(name, ext, opts)\n  if devicons then\n    return devicons.get_icon(name, ext, opts)\n  else\n    return nil, nil\n  end\nend\n\n---Attempt to use nvim-web-devicons if present and enabled for file or folder\n---@param opts table\nfunction M.setup(opts)\n  if opts.renderer.icons.show.file or opts.renderer.icons.show.folder then\n    local ok, di = pcall(require, \"nvim-web-devicons\")\n    if ok then\n      devicons = di --[[@as DevIcons]]\n\n      -- does nothing if already called i.e. doesn't clobber previous user setup\n      devicons.setup()\n    end\n  end\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/renderer/components/full-name.lua",
    "content": "local M = {}\n\nlocal utils = require(\"nvim-tree.utils\")\nlocal view = require(\"nvim-tree.view\")\n\nlocal function hide(win)\n  if win then\n    if vim.api.nvim_win_is_valid(win) then\n      vim.api.nvim_win_close(win, true)\n    end\n  end\nend\n\n-- reduce signcolumn/foldcolumn from window width\nlocal function effective_win_width()\n  local win_width = vim.fn.winwidth(0)\n\n  -- return zero if the window cannot be found\n  local win_id = vim.fn.win_getid()\n\n  if win_id == 0 then\n    return win_width\n  end\n\n  -- if the window does not exist the result is an empty list\n  local win_info = vim.fn.getwininfo(win_id)\n\n  -- check if result table is empty\n  if next(win_info) == nil then\n    return win_width\n  end\n\n  return win_width - win_info[1].textoff\nend\n\nlocal function show(opts)\n  local line_nr = vim.api.nvim_win_get_cursor(0)[1]\n  if vim.wo.wrap then\n    return\n  end\n  -- only work for left tree\n  if vim.api.nvim_win_get_position(0)[2] ~= 0 then\n    return\n  end\n\n  local line = vim.fn.getline(\".\")\n  local leftcol = vim.fn.winsaveview().leftcol\n  -- hide full name if left column of node in nvim-tree win is not zero\n  if leftcol ~= 0 then\n    return\n  end\n\n  local text_width = vim.fn.strdisplaywidth(vim.fn.substitute(line, \"[^[:print:]]*$\", \"\", \"g\"))\n  local win_width = effective_win_width()\n\n  -- windows width reduced by right aligned icons\n  local icon_ns_id = vim.api.nvim_get_namespaces()[\"NvimTreeExtmarks\"]\n  local icon_extmarks = vim.api.nvim_buf_get_extmarks(0, icon_ns_id, { line_nr - 1, 0 }, { line_nr - 1, -1 }, { details = true })\n  win_width = win_width - utils.extmarks_length(icon_extmarks)\n\n  if text_width < win_width then\n    return\n  end\n\n  M.popup_win = vim.api.nvim_open_win(vim.api.nvim_create_buf(false, false), false, {\n    relative  = \"win\",\n    row       = 0,\n    bufpos    = { vim.api.nvim_win_get_cursor(0)[1] - 1, 0 },\n    width     = math.min(text_width, vim.o.columns - 2),\n    height    = 1,\n    noautocmd = true,\n    style     = \"minimal\",\n    border    = \"none\"\n  })\n  vim.wo[M.popup_win].winhl = view.View.winopts.winhl\n\n  local ns_id = vim.api.nvim_get_namespaces()[\"NvimTreeHighlights\"]\n  local extmarks = vim.api.nvim_buf_get_extmarks(0, ns_id, { line_nr - 1, 0 }, { line_nr - 1, -1 }, { details = true })\n  vim.api.nvim_win_call(M.popup_win, function()\n    vim.api.nvim_buf_set_lines(0, 0, -1, true, { line })\n    for _, extmark in ipairs(extmarks) do\n      -- nvim 0.10 luadoc is incorrect: vim.api.keyset.get_extmark_item is missing the extmark_id at the start\n\n      ---@cast extmark table\n      ---@type integer\n      local col = extmark[3]\n      ---@type vim.api.keyset.extmark_details\n      local details = extmark[4]\n\n      if type(details) == \"table\" then\n        if vim.fn.has(\"nvim-0.11\") == 1 and vim.hl and vim.hl.range then\n          vim.hl.range(0, ns_id, details.hl_group, { 0, col }, { 0, details.end_col, }, {})\n        else\n          vim.api.nvim_buf_add_highlight(0, ns_id, details.hl_group, 0, col, details.end_col) ---@diagnostic disable-line: deprecated\n        end\n      end\n    end\n    vim.cmd([[ setlocal nowrap noswapfile nobuflisted buftype=nofile bufhidden=wipe ]])\n    if opts.view.cursorline then\n      vim.cmd([[ setlocal cursorline cursorlineopt=both ]])\n    end\n  end)\nend\n\nM.setup = function(opts)\n  M.config = opts.renderer\n  if not M.config.full_name then\n    return\n  end\n\n  local group = vim.api.nvim_create_augroup(\"nvim_tree_floating_node\", { clear = true })\n  vim.api.nvim_create_autocmd({ \"BufLeave\", \"CursorMoved\" }, {\n    group = group,\n    pattern = { \"NvimTree_*\" },\n    callback = function()\n      if utils.is_nvim_tree_buf(0) then\n        hide(M.popup_win)\n      end\n    end,\n  })\n\n  vim.api.nvim_create_autocmd({ \"CursorMoved\" }, {\n    group = group,\n    pattern = { \"NvimTree_*\" },\n    callback = function()\n      if utils.is_nvim_tree_buf(0) then\n        show(opts)\n      end\n    end,\n  })\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/renderer/components/init.lua",
    "content": "local M = {}\n\nM.full_name = require(\"nvim-tree.renderer.components.full-name\")\nM.devicons = require(\"nvim-tree.renderer.components.devicons\")\nM.padding = require(\"nvim-tree.renderer.components.padding\")\n\nfunction M.setup(opts)\n  M.full_name.setup(opts)\n  M.devicons.setup(opts)\n  M.padding.setup(opts)\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/renderer/components/padding.lua",
    "content": "local DirectoryNode = require(\"nvim-tree.node.directory\")\n\nlocal M = {}\n\nlocal function check_siblings_for_folder(node, with_arrows)\n  if with_arrows then\n    local has_files = false\n    local has_folders = false\n    for _, n in pairs(node.parent.nodes) do\n      if n.nodes and node.absolute_path ~= n.absolute_path then\n        has_folders = true\n      end\n      if not n.nodes then\n        has_files = true\n      end\n      if has_files and has_folders then\n        return true\n      end\n    end\n  end\n  return false\nend\n\nlocal function get_padding_indent_markers(depth, idx, nodes_number, markers, with_arrows, inline_arrows, node, early_stop)\n  local base_padding = with_arrows and (not node.nodes or depth > 0) and \"  \" or \"\"\n  local padding = (inline_arrows or depth == 0) and base_padding or \"\"\n\n  if depth > 0 then\n    local has_folder_sibling = check_siblings_for_folder(node, with_arrows)\n    local indent = string.rep(\" \", M.config.indent_width - 1)\n    markers[depth] = idx ~= nodes_number\n    for i = 1, depth - early_stop do\n      local glyph\n      if idx == nodes_number and i == depth then\n        local bottom_width = M.config.indent_width - 2 + (with_arrows and not inline_arrows and has_folder_sibling and 2 or 0)\n        glyph = M.config.indent_markers.icons.corner\n          .. string.rep(M.config.indent_markers.icons.bottom, bottom_width)\n          .. (M.config.indent_width > 1 and \" \" or \"\")\n      elseif markers[i] and i == depth then\n        glyph = M.config.indent_markers.icons.item .. indent\n      elseif markers[i] then\n        glyph = M.config.indent_markers.icons.edge .. indent\n      else\n        glyph = M.config.indent_markers.icons.none .. indent\n      end\n\n      if not with_arrows or (inline_arrows and (depth ~= i or not node.nodes)) then\n        padding = padding .. glyph\n      elseif inline_arrows then\n        padding = padding\n      elseif idx ~= nodes_number and depth == i and not node.nodes and has_folder_sibling then\n        padding = padding .. base_padding .. glyph .. base_padding\n      else\n        padding = padding .. base_padding .. glyph\n      end\n    end\n  end\n  return padding\nend\n\n---@param depth integer\n---@param idx integer\n---@param nodes_number integer\n---@param node Node\n---@param markers table\n---@param early_stop integer?\n---@return nvim_tree.api.highlighted_string\nfunction M.get_indent_markers(depth, idx, nodes_number, node, markers, early_stop)\n  local str = \"\"\n\n  local show_arrows = M.config.icons.show.folder_arrow\n  local show_markers = M.config.indent_markers.enable\n  local inline_arrows = M.config.indent_markers.inline_arrows\n  local indent_width = M.config.indent_width\n\n  if show_markers then\n    str = str .. get_padding_indent_markers(depth, idx, nodes_number, markers, show_arrows, inline_arrows, node, early_stop or 0)\n  else\n    str = str .. string.rep(\" \", depth * indent_width)\n  end\n\n  return { str = str, hl = { \"NvimTreeIndentMarker\" } }\nend\n\n---@param node Node\n---@return nvim_tree.api.highlighted_string[]?\nfunction M.get_arrows(node)\n  if not M.config.icons.show.folder_arrow then\n    return\n  end\n\n  local str\n  local hl = \"NvimTreeFolderArrowClosed\"\n\n  local dir = node:as(DirectoryNode)\n  if dir then\n    if dir.open then\n      str = M.config.icons.glyphs.folder[\"arrow_open\"] .. M.config.icons.padding.folder_arrow\n      hl = \"NvimTreeFolderArrowOpen\"\n    else\n      str = M.config.icons.glyphs.folder[\"arrow_closed\"] .. M.config.icons.padding.folder_arrow\n    end\n  elseif M.config.indent_markers.enable then\n    str = \"\"\n  else\n    str = \" \" .. string.rep(\" \", #M.config.icons.padding.folder_arrow)\n  end\n\n  return { str = str, hl = { hl } }\nend\n\nfunction M.setup(opts)\n  M.config = opts.renderer\n\n  if M.config.indent_width < 1 then\n    M.config.indent_width = 1\n  end\n\n  local function check_marker(symbol)\n    if #symbol == 0 then\n      return \" \"\n    end\n    -- return the first character from the UTF-8 encoded string; we may use utf8.codes from Lua 5.3 when available\n    return symbol:match(\"[%z\\1-\\127\\194-\\244][\\128-\\191]*\")\n  end\n\n  for k, v in pairs(M.config.indent_markers.icons) do\n    M.config.indent_markers.icons[k] = check_marker(v)\n  end\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/renderer/decorator/bookmarks.lua",
    "content": "local BuiltinDecorator = require(\"nvim-tree.renderer.decorator.builtin\")\n\n---@class (exact) BookmarkDecorator: BuiltinDecorator\n---@field private icon nvim_tree.api.highlighted_string?\nlocal BookmarkDecorator = BuiltinDecorator:extend()\n\n---@class BookmarkDecorator\n---@overload fun(args: BuiltinDecoratorArgs): BookmarkDecorator\n\n---@protected\n---@param args BuiltinDecoratorArgs\nfunction BookmarkDecorator:new(args)\n  BookmarkDecorator.super.new(self, args)\n\n  self.enabled         = true\n  self.highlight_range = self.explorer.opts.renderer.highlight_bookmarks or \"none\"\n  self.icon_placement  = self.explorer.opts.renderer.icons.bookmarks_placement or \"none\"\n\n  if self.explorer.opts.renderer.icons.show.bookmarks then\n    self.icon = {\n      str = self.explorer.opts.renderer.icons.glyphs.bookmark,\n      hl = { \"NvimTreeBookmarkIcon\" },\n    }\n    self:define_sign(self.icon)\n  end\nend\n\n---Bookmark icon: renderer.icons.show.bookmarks and node is marked\n---@param node Node\n---@return nvim_tree.api.highlighted_string[]? icons\nfunction BookmarkDecorator:icons(node)\n  if self.explorer.marks:get(node) then\n    return { self.icon }\n  end\nend\n\n---Bookmark highlight: renderer.highlight_bookmarks and node is marked\n---@param node Node\n---@return string? highlight_group\nfunction BookmarkDecorator:highlight_group(node)\n  if self.highlight_range ~= \"none\" and self.explorer.marks:get(node) then\n    return \"NvimTreeBookmarkHL\"\n  end\nend\n\nreturn BookmarkDecorator\n"
  },
  {
    "path": "lua/nvim-tree/renderer/decorator/builtin.lua",
    "content": "local Decorator = require(\"nvim-tree.renderer.decorator\")\n\n---Abstract builtin decorator\n---Overrides all methods to use a Node instead of nvim_tree.api.Node as we don't have generics.\n---\n---@class (exact) BuiltinDecorator: Decorator\n---\n---@field protected explorer Explorer\n---\n---@field icon_node                 fun(self: BuiltinDecorator, node: Node): nvim_tree.api.highlighted_string?\n---@field icons                     fun(self: BuiltinDecorator, node: Node): nvim_tree.api.highlighted_string?\n---@field highlight_group           fun(self: BuiltinDecorator, node: Node): string?\n---@field highlight_group_icon_name fun(self: BuiltinDecorator, node: Node): string?, string?\n---@field sign_name                 fun(self: BuiltinDecorator, node: Node): string?\n---@field icons_before              fun(self: BuiltinDecorator, node: Node): nvim_tree.api.highlighted_string[]?\n---@field icons_after               fun(self: BuiltinDecorator, node: Node): nvim_tree.api.highlighted_string[]?\n---@field icons_right_align         fun(self: BuiltinDecorator, node: Node): nvim_tree.api.highlighted_string[]?\nlocal BuiltinDecorator = Decorator:extend()\n\n---@class (exact) BuiltinDecoratorArgs\n---@field explorer Explorer\n\n---@protected\n---@param args BuiltinDecoratorArgs\nfunction BuiltinDecorator:new(args)\n  self.explorer = args.explorer\nend\n\nreturn BuiltinDecorator\n"
  },
  {
    "path": "lua/nvim-tree/renderer/decorator/copied.lua",
    "content": "local BuiltinDecorator = require(\"nvim-tree.renderer.decorator.builtin\")\n\n---@class (exact) CopiedDecorator: BuiltinDecorator\nlocal CopiedDecorator = BuiltinDecorator:extend()\n\n---@class CopiedDecorator\n---@overload fun(args: BuiltinDecoratorArgs): CopiedDecorator\n\n---@protected\n---@param args BuiltinDecoratorArgs\nfunction CopiedDecorator:new(args)\n  CopiedDecorator.super.new(self, args)\n\n  self.enabled         = true\n  self.highlight_range = self.explorer.opts.renderer.highlight_clipboard or \"none\"\n  self.icon_placement  = \"none\"\nend\n\n---Copied highlight: renderer.highlight_clipboard and node is copied\n---@param node Node\n---@return string? highlight_group\nfunction CopiedDecorator:highlight_group(node)\n  if self.highlight_range ~= \"none\" and self.explorer.clipboard:is_copied(node) then\n    return \"NvimTreeCopiedHL\"\n  end\nend\n\nreturn CopiedDecorator\n"
  },
  {
    "path": "lua/nvim-tree/renderer/decorator/cut.lua",
    "content": "local BuiltinDecorator = require(\"nvim-tree.renderer.decorator.builtin\")\n\n---@class (exact) CutDecorator: BuiltinDecorator\nlocal CutDecorator = BuiltinDecorator:extend()\n\n---@class CutDecorator\n---@overload fun(args: BuiltinDecoratorArgs): CutDecorator\n\n---@protected\n---@param args BuiltinDecoratorArgs\nfunction CutDecorator:new(args)\n  CutDecorator.super.new(self, args)\n\n  self.enabled         = true\n  self.highlight_range = self.explorer.opts.renderer.highlight_clipboard or \"none\"\n  self.icon_placement  = \"none\"\nend\n\n---Cut highlight: renderer.highlight_clipboard and node is cut\n---@param node Node\n---@return string? highlight_group\nfunction CutDecorator:highlight_group(node)\n  if self.highlight_range ~= \"none\" and self.explorer.clipboard:is_cut(node) then\n    return \"NvimTreeCutHL\"\n  end\nend\n\nreturn CutDecorator\n"
  },
  {
    "path": "lua/nvim-tree/renderer/decorator/diagnostics.lua",
    "content": "local diagnostics = require(\"nvim-tree.diagnostics\")\n\nlocal BuiltinDecorator = require(\"nvim-tree.renderer.decorator.builtin\")\nlocal DirectoryNode = require(\"nvim-tree.node.directory\")\n\n-- highlight groups by severity\nlocal HG_ICON = {\n  [vim.diagnostic.severity.ERROR] = \"NvimTreeDiagnosticErrorIcon\",\n  [vim.diagnostic.severity.WARN] = \"NvimTreeDiagnosticWarnIcon\",\n  [vim.diagnostic.severity.INFO] = \"NvimTreeDiagnosticInfoIcon\",\n  [vim.diagnostic.severity.HINT] = \"NvimTreeDiagnosticHintIcon\",\n}\nlocal HG_FILE = {\n  [vim.diagnostic.severity.ERROR] = \"NvimTreeDiagnosticErrorFileHL\",\n  [vim.diagnostic.severity.WARN] = \"NvimTreeDiagnosticWarnFileHL\",\n  [vim.diagnostic.severity.INFO] = \"NvimTreeDiagnosticInfoFileHL\",\n  [vim.diagnostic.severity.HINT] = \"NvimTreeDiagnosticHintFileHL\",\n}\nlocal HG_FOLDER = {\n  [vim.diagnostic.severity.ERROR] = \"NvimTreeDiagnosticErrorFolderHL\",\n  [vim.diagnostic.severity.WARN] = \"NvimTreeDiagnosticWarnFolderHL\",\n  [vim.diagnostic.severity.INFO] = \"NvimTreeDiagnosticInfoFolderHL\",\n  [vim.diagnostic.severity.HINT] = \"NvimTreeDiagnosticHintFolderHL\",\n}\n-- opts.diagnostics.icons.\nlocal ICON_KEYS = {\n  [\"error\"] = vim.diagnostic.severity.ERROR,\n  [\"warning\"] = vim.diagnostic.severity.WARN,\n  [\"info\"] = vim.diagnostic.severity.INFO,\n  [\"hint\"] = vim.diagnostic.severity.HINT,\n}\n\n---@class (exact) DiagnosticsDecorator: BuiltinDecorator\n---@field private diag_icons nvim_tree.api.highlighted_string[]?\nlocal DiagnosticsDecorator = BuiltinDecorator:extend()\n\n---@class DiagnosticsDecorator\n---@overload fun(args: BuiltinDecoratorArgs): DiagnosticsDecorator\n\n---@protected\n---@param args BuiltinDecoratorArgs\nfunction DiagnosticsDecorator:new(args)\n  DiagnosticsDecorator.super.new(self, args)\n\n  self.enabled               = true\n  self.highlight_range       = self.explorer.opts.renderer.highlight_diagnostics or \"none\"\n  self.icon_placement        = self.explorer.opts.renderer.icons.diagnostics_placement or \"none\"\n\n  local vim_diagnostic_icons = {}\n\n  if self.explorer.opts.diagnostics.diagnostic_opts then\n    local vim_diagnostic_config = vim.diagnostic.config() or {}\n    local signs                 = vim_diagnostic_config.signs or {}\n    if type(signs) == \"function\" then\n      signs = signs(0, 0)\n    end\n\n    vim_diagnostic_icons = (type(signs) == \"table\" and signs.text) or {}\n  end\n\n  if self.explorer.opts.renderer.icons.show.diagnostics then\n    self.diag_icons = {}\n    for name, sev in pairs(ICON_KEYS) do\n      self.diag_icons[sev] = {\n        str = vim_diagnostic_icons[sev] or self.explorer.opts.diagnostics.icons[name],\n        hl = { HG_ICON[sev] },\n      }\n      self:define_sign(self.diag_icons[sev])\n    end\n  end\nend\n\n---Diagnostic icon: diagnostics.enable, renderer.icons.show.diagnostics and node has status\n---@param node Node\n---@return nvim_tree.api.highlighted_string[]? icons\nfunction DiagnosticsDecorator:icons(node)\n  if node and self.diag_icons then\n    local diag_status = diagnostics.get_diag_status(node)\n    local diag_value = diag_status and diag_status.value\n\n    if diag_value then\n      return { self.diag_icons[diag_value] }\n    end\n  end\nend\n\n---Diagnostic highlight: diagnostics.enable, renderer.highlight_diagnostics and node has status\n---@param node Node\n---@return string? highlight_group\nfunction DiagnosticsDecorator:highlight_group(node)\n  if self.highlight_range == \"none\" then\n    return nil\n  end\n\n  local diag_status = diagnostics.get_diag_status(node)\n  local diag_value = diag_status and diag_status.value\n\n  if not diag_value then\n    return nil\n  end\n\n  local group\n  if node:is(DirectoryNode) then\n    group = HG_FOLDER[diag_value]\n  else\n    group = HG_FILE[diag_value]\n  end\n\n  if group then\n    return group\n  else\n    return nil\n  end\nend\n\nreturn DiagnosticsDecorator\n"
  },
  {
    "path": "lua/nvim-tree/renderer/decorator/git.lua",
    "content": "local notify = require(\"nvim-tree.notify\")\n\nlocal BuiltinDecorator = require(\"nvim-tree.renderer.decorator.builtin\")\nlocal DirectoryNode = require(\"nvim-tree.node.directory\")\n\n---@class (exact) GitHighlightedString: nvim_tree.api.highlighted_string\n---@field ord number decreasing priority\n\n---@alias GitStatusStrings \"deleted\" | \"ignored\" | \"renamed\" | \"staged\" | \"unmerged\" | \"unstaged\" | \"untracked\"\n\n---@alias GitIconsByStatus table<GitStatusStrings, GitHighlightedString> human status\n---@alias GitIconsByXY table<nvim_tree.git.XY, GitHighlightedString[]> porcelain status\n---@alias GitGlyphsByStatus table<GitStatusStrings, string> from opts\n\n---@class (exact) GitDecorator: BuiltinDecorator\n---@field private file_hl_by_xy table<nvim_tree.git.XY, string>?\n---@field private folder_hl_by_xy table<nvim_tree.git.XY, string>?\n---@field private icons_by_status GitIconsByStatus?\n---@field private icons_by_xy GitIconsByXY?\nlocal GitDecorator = BuiltinDecorator:extend()\n\n---@class GitDecorator\n---@overload fun(args: BuiltinDecoratorArgs): GitDecorator\n\n---@protected\n---@param args BuiltinDecoratorArgs\nfunction GitDecorator:new(args)\n  GitDecorator.super.new(self, args)\n\n  self.enabled         = self.explorer.opts.git.enable\n  self.highlight_range = self.explorer.opts.renderer.highlight_git or \"none\"\n  self.icon_placement  = self.explorer.opts.renderer.icons.git_placement or \"none\"\n\n  if not self.enabled then\n    return\n  end\n\n  if self.highlight_range ~= \"none\" then\n    self:build_file_folder_hl_by_xy()\n  end\n\n  if self.explorer.opts.renderer.icons.show.git then\n    self:build_icons_by_status(self.explorer.opts.renderer.icons.glyphs.git)\n    self:build_icons_by_xy(self.icons_by_status)\n\n    for _, icon in pairs(self.icons_by_status) do\n      self:define_sign(icon)\n    end\n  end\nend\n\n---@param glyphs GitGlyphsByStatus\nfunction GitDecorator:build_icons_by_status(glyphs)\n  self.icons_by_status           = {}\n  self.icons_by_status.staged    = { str = glyphs.staged, hl = { \"NvimTreeGitStagedIcon\" }, ord = 1 }\n  self.icons_by_status.unstaged  = { str = glyphs.unstaged, hl = { \"NvimTreeGitDirtyIcon\" }, ord = 2 }\n  self.icons_by_status.renamed   = { str = glyphs.renamed, hl = { \"NvimTreeGitRenamedIcon\" }, ord = 3 }\n  self.icons_by_status.deleted   = { str = glyphs.deleted, hl = { \"NvimTreeGitDeletedIcon\" }, ord = 4 }\n  self.icons_by_status.unmerged  = { str = glyphs.unmerged, hl = { \"NvimTreeGitMergeIcon\" }, ord = 5 }\n  self.icons_by_status.untracked = { str = glyphs.untracked, hl = { \"NvimTreeGitNewIcon\" }, ord = 6 }\n  self.icons_by_status.ignored   = { str = glyphs.ignored, hl = { \"NvimTreeGitIgnoredIcon\" }, ord = 7 }\nend\n\n---@param icons GitIconsByStatus\nfunction GitDecorator:build_icons_by_xy(icons)\n  self.icons_by_xy = {\n    [\"M \"] = { icons.staged },\n    [\" M\"] = { icons.unstaged },\n    [\"C \"] = { icons.staged },\n    [\" C\"] = { icons.unstaged },\n    [\"CM\"] = { icons.unstaged },\n    [\" T\"] = { icons.unstaged },\n    [\"T \"] = { icons.staged },\n    [\"TM\"] = { icons.staged, icons.unstaged },\n    [\"MM\"] = { icons.staged, icons.unstaged },\n    [\"MD\"] = { icons.staged },\n    [\"A \"] = { icons.staged },\n    [\"AD\"] = { icons.staged },\n    [\" A\"] = { icons.untracked },\n    -- not sure about this one\n    [\"AA\"] = { icons.unmerged, icons.untracked },\n    [\"AU\"] = { icons.unmerged, icons.untracked },\n    [\"AM\"] = { icons.staged, icons.unstaged },\n    [\"??\"] = { icons.untracked },\n    [\"R \"] = { icons.renamed },\n    [\" R\"] = { icons.renamed },\n    [\"RM\"] = { icons.unstaged, icons.renamed },\n    [\"UU\"] = { icons.unmerged },\n    [\"UD\"] = { icons.unmerged },\n    [\"UA\"] = { icons.unmerged },\n    [\" D\"] = { icons.deleted },\n    [\"D \"] = { icons.deleted },\n    [\"DA\"] = { icons.unstaged },\n    [\"RD\"] = { icons.deleted },\n    [\"DD\"] = { icons.deleted },\n    [\"DU\"] = { icons.deleted, icons.unmerged },\n    [\"!!\"] = { icons.ignored },\n    dirty  = { icons.unstaged },\n  }\nend\n\nfunction GitDecorator:build_file_folder_hl_by_xy()\n  self.file_hl_by_xy = {\n    [\"M \"] = \"NvimTreeGitFileStagedHL\",\n    [\"C \"] = \"NvimTreeGitFileStagedHL\",\n    [\"AA\"] = \"NvimTreeGitFileStagedHL\",\n    [\"AD\"] = \"NvimTreeGitFileStagedHL\",\n    [\"MD\"] = \"NvimTreeGitFileStagedHL\",\n    [\"T \"] = \"NvimTreeGitFileStagedHL\",\n    [\"TT\"] = \"NvimTreeGitFileStagedHL\",\n    [\" M\"] = \"NvimTreeGitFileDirtyHL\",\n    [\"CM\"] = \"NvimTreeGitFileDirtyHL\",\n    [\" C\"] = \"NvimTreeGitFileDirtyHL\",\n    [\" T\"] = \"NvimTreeGitFileDirtyHL\",\n    [\"MM\"] = \"NvimTreeGitFileDirtyHL\",\n    [\"AM\"] = \"NvimTreeGitFileDirtyHL\",\n    dirty  = \"NvimTreeGitFileDirtyHL\",\n    [\"A \"] = \"NvimTreeGitFileStagedHL\",\n    [\"??\"] = \"NvimTreeGitFileNewHL\",\n    [\"AU\"] = \"NvimTreeGitFileMergeHL\",\n    [\"UU\"] = \"NvimTreeGitFileMergeHL\",\n    [\"UD\"] = \"NvimTreeGitFileMergeHL\",\n    [\"DU\"] = \"NvimTreeGitFileMergeHL\",\n    [\"UA\"] = \"NvimTreeGitFileMergeHL\",\n    [\" D\"] = \"NvimTreeGitFileDeletedHL\",\n    [\"DD\"] = \"NvimTreeGitFileDeletedHL\",\n    [\"RD\"] = \"NvimTreeGitFileDeletedHL\",\n    [\"D \"] = \"NvimTreeGitFileDeletedHL\",\n    [\"R \"] = \"NvimTreeGitFileRenamedHL\",\n    [\"RM\"] = \"NvimTreeGitFileRenamedHL\",\n    [\" R\"] = \"NvimTreeGitFileRenamedHL\",\n    [\"!!\"] = \"NvimTreeGitFileIgnoredHL\",\n    [\" A\"] = \"NvimTreeGitFileNewHL\",\n  }\n\n  self.folder_hl_by_xy = {}\n  for k, v in pairs(self.file_hl_by_xy) do\n    self.folder_hl_by_xy[k] = v:gsub(\"File\", \"Folder\")\n  end\nend\n\n---Git icons: git.enable, renderer.icons.show.git and node has status\n---@param node Node\n---@return nvim_tree.api.highlighted_string[]? icons\nfunction GitDecorator:icons(node)\n  if not self.icons_by_xy then\n    return nil\n  end\n\n  local git_xy = node:get_git_xy()\n  if git_xy == nil then\n    return nil\n  end\n\n  local inserted = {}\n  local iconss = {}\n\n  for _, s in pairs(git_xy) do\n    local icons = self.icons_by_xy[s]\n    if not icons then\n      if self.highlight_range == \"none\" then\n        notify.warn(string.format(\"Unrecognized git state '%s'\", git_xy))\n      end\n      return nil\n    end\n\n    for _, icon in pairs(icons) do\n      if #icon.str > 0 then\n        if not inserted[icon] then\n          table.insert(iconss, icon)\n          inserted[icon] = true\n        end\n      end\n    end\n  end\n\n  if #iconss == 0 then\n    return nil\n  end\n\n  -- sort icons so it looks slightly better\n  table.sort(iconss, function(a, b)\n    return a.ord < b.ord\n  end)\n\n  return iconss\nend\n\n---Get the first icon as the sign if appropriate\n---@param node Node\n---@return string|nil name\nfunction GitDecorator:sign_name(node)\n  if self.icon_placement ~= \"signcolumn\" then\n    return\n  end\n\n  local icons = self:icons(node)\n  if icons and #icons > 0 then\n    return icons[1].hl[1]\n  end\nend\n\n---Git highlight: git.enable, renderer.highlight_git and node has status\n---@param node Node\n---@return string? highlight_group\nfunction GitDecorator:highlight_group(node)\n  if self.highlight_range == \"none\" then\n    return nil\n  end\n\n  local git_xy = node:get_git_xy()\n  if not git_xy then\n    return nil\n  end\n\n  if node:is(DirectoryNode) then\n    return self.folder_hl_by_xy[git_xy[1]]\n  else\n    return self.file_hl_by_xy[git_xy[1]]\n  end\nend\n\nreturn GitDecorator\n"
  },
  {
    "path": "lua/nvim-tree/renderer/decorator/hidden.lua",
    "content": "local BuiltinDecorator = require(\"nvim-tree.renderer.decorator.builtin\")\nlocal DirectoryNode = require(\"nvim-tree.node.directory\")\n\n---@class (exact) HiddenDecorator: BuiltinDecorator\n---@field private icon nvim_tree.api.highlighted_string?\nlocal HiddenDecorator = BuiltinDecorator:extend()\n\n---@class HiddenDecorator\n---@overload fun(args: BuiltinDecoratorArgs): HiddenDecorator\n\n---@protected\n---@param args BuiltinDecoratorArgs\nfunction HiddenDecorator:new(args)\n  HiddenDecorator.super.new(self, args)\n\n  self.enabled         = true\n  self.highlight_range = self.explorer.opts.renderer.highlight_hidden or \"none\"\n  self.icon_placement  = self.explorer.opts.renderer.icons.hidden_placement or \"none\"\n\n  if self.explorer.opts.renderer.icons.show.hidden then\n    self.icon = {\n      str = self.explorer.opts.renderer.icons.glyphs.hidden,\n      hl = { \"NvimTreeHiddenIcon\" },\n    }\n    self:define_sign(self.icon)\n  end\nend\n\n---Hidden icon: renderer.icons.show.hidden and node starts with `.` (dotfile).\n---@param node Node\n---@return nvim_tree.api.highlighted_string[]? icons\nfunction HiddenDecorator:icons(node)\n  if node:is_dotfile() then\n    return { self.icon }\n  end\nend\n\n---Hidden highlight: renderer.highlight_hidden and node starts with `.` (dotfile).\n---@param node Node\n---@return string? highlight_group\nfunction HiddenDecorator:highlight_group(node)\n  if self.highlight_range == \"none\" or not node:is_dotfile() then\n    return nil\n  end\n\n  if node:is(DirectoryNode) then\n    return \"NvimTreeHiddenFolderHL\"\n  else\n    return \"NvimTreeHiddenFileHL\"\n  end\nend\n\nreturn HiddenDecorator\n"
  },
  {
    "path": "lua/nvim-tree/renderer/decorator/init.lua",
    "content": "local DecoratorInterface = require(\"nvim-tree._meta.api.decorator\")\n\n---\n---Abstract decorator\n---\n---@class (exact) Decorator: nvim_tree.api.Decorator\nlocal Decorator = DecoratorInterface:extend()\n\n---Maybe highlight groups for icon and name\n---@param node nvim_tree.api.Node\n---@return string? icon highlight group\n---@return string? name highlight group\nfunction Decorator:highlight_group_icon_name(node)\n  local icon_hl, name_hl\n\n  if self.enabled and self.highlight_range ~= \"none\" then\n    local hl = self:highlight_group(node)\n\n    if self.highlight_range == \"all\" or self.highlight_range == \"icon\" then\n      icon_hl = hl\n    end\n    if self.highlight_range == \"all\" or self.highlight_range == \"name\" then\n      name_hl = hl\n    end\n  end\n\n  return icon_hl, name_hl\nend\n\n---Maybe icon sign\n---@param node nvim_tree.api.Node\n---@return string? name\nfunction Decorator:sign_name(node)\n  if not self.enabled or self.icon_placement ~= \"signcolumn\" then\n    return\n  end\n\n  local icons = self:icons(node)\n  if icons and #icons > 0 then\n    return icons[1].hl[1]\n  end\nend\n\n---Icons when \"before\"\n---@param node nvim_tree.api.Node\n---@return nvim_tree.api.highlighted_string[]? icons\nfunction Decorator:icons_before(node)\n  if not self.enabled or self.icon_placement ~= \"before\" then\n    return\n  end\n\n  return self:icons(node)\nend\n\n---Icons when \"after\"\n---@param node nvim_tree.api.Node\n---@return nvim_tree.api.highlighted_string[]? icons\nfunction Decorator:icons_after(node)\n  if not self.enabled or self.icon_placement ~= \"after\" then\n    return\n  end\n\n  return self:icons(node)\nend\n\n---Icons when \"right_align\"\n---@param node nvim_tree.api.Node\n---@return nvim_tree.api.highlighted_string[]? icons\nfunction Decorator:icons_right_align(node)\n  if not self.enabled or self.icon_placement ~= \"right_align\" then\n    return\n  end\n\n  return self:icons(node)\nend\n\n---Define a sign\n---@protected\n---@param icon nvim_tree.api.highlighted_string?\nfunction Decorator:define_sign(icon)\n  if icon and #icon.hl > 0 then\n    local name = icon.hl[1]\n\n    if not vim.tbl_isempty(vim.fn.sign_getdefined(name)) then\n      vim.fn.sign_undefine(name)\n    end\n\n    -- don't render sign if empty\n    if #icon.str < 1 then\n      return\n    end\n\n    -- byte index of the next character, allowing for wide\n    local bi = vim.fn.byteidx(icon.str, 1)\n\n    -- first (wide) character, falls back to empty string\n    local text = string.sub(icon.str, 1, bi)\n    vim.fn.sign_define(name, {\n      text = text,\n      texthl = name,\n    })\n  end\nend\n\n---@type Decorator\nreturn Decorator\n"
  },
  {
    "path": "lua/nvim-tree/renderer/decorator/modified.lua",
    "content": "local buffers = require(\"nvim-tree.buffers\")\n\nlocal BuiltinDecorator = require(\"nvim-tree.renderer.decorator.builtin\")\nlocal DirectoryNode = require(\"nvim-tree.node.directory\")\n\n---@class (exact) ModifiedDecorator: BuiltinDecorator\n---@field private icon nvim_tree.api.highlighted_string?\nlocal ModifiedDecorator = BuiltinDecorator:extend()\n\n---@class ModifiedDecorator\n---@overload fun(args: BuiltinDecoratorArgs): ModifiedDecorator\n\n---@protected\n---@param args BuiltinDecoratorArgs\nfunction ModifiedDecorator:new(args)\n  ModifiedDecorator.super.new(self, args)\n\n  self.enabled         = true\n  self.highlight_range = self.explorer.opts.renderer.highlight_modified or \"none\"\n  self.icon_placement  = self.explorer.opts.renderer.icons.modified_placement or \"none\"\n\n  if self.explorer.opts.renderer.icons.show.modified then\n    self.icon = {\n      str = self.explorer.opts.renderer.icons.glyphs.modified,\n      hl = { \"NvimTreeModifiedIcon\" },\n    }\n    self:define_sign(self.icon)\n  end\nend\n\n---Modified icon: modified.enable, renderer.icons.show.modified and node is modified\n---@param node Node\n---@return nvim_tree.api.highlighted_string[]? icons\nfunction ModifiedDecorator:icons(node)\n  if buffers.is_modified(node) then\n    return { self.icon }\n  end\nend\n\n---Modified highlight: modified.enable, renderer.highlight_modified and node is modified\n---@param node Node\n---@return string? highlight_group\nfunction ModifiedDecorator:highlight_group(node)\n  if self.highlight_range == \"none\" or not buffers.is_modified(node) then\n    return nil\n  end\n\n  if node:is(DirectoryNode) then\n    return \"NvimTreeModifiedFolderHL\"\n  else\n    return \"NvimTreeModifiedFileHL\"\n  end\nend\n\nreturn ModifiedDecorator\n"
  },
  {
    "path": "lua/nvim-tree/renderer/decorator/opened.lua",
    "content": "local buffers = require(\"nvim-tree.buffers\")\n\nlocal BuiltinDecorator = require(\"nvim-tree.renderer.decorator.builtin\")\n\n---@class (exact) OpenDecorator: BuiltinDecorator\n---@field private icon? nvim_tree.api.highlighted_string\nlocal OpenDecorator = BuiltinDecorator:extend()\n\n---@class OpenDecorator\n---@overload fun(args: BuiltinDecoratorArgs): OpenDecorator\n\n---@protected\n---@param args BuiltinDecoratorArgs\nfunction OpenDecorator:new(args)\n  OpenDecorator.super.new(self, args)\n\n  self.enabled         = true\n  self.highlight_range = self.explorer.opts.renderer.highlight_opened_files or \"none\"\n  self.icon_placement  = \"none\"\nend\n\n---Opened highlight: renderer.highlight_opened_files and node has an open buffer\n---@param node Node\n---@return string? highlight_group\nfunction OpenDecorator:highlight_group(node)\n  if self.highlight_range ~= \"none\" and buffers.is_opened(node) then\n    return \"NvimTreeOpenedHL\"\n  end\nend\n\nreturn OpenDecorator\n"
  },
  {
    "path": "lua/nvim-tree/renderer/init.lua",
    "content": "local log = require(\"nvim-tree.log\")\nlocal view = require(\"nvim-tree.view\")\nlocal events = require(\"nvim-tree.events\")\n\nlocal Class = require(\"nvim-tree.classic\")\nlocal Builder = require(\"nvim-tree.renderer.builder\")\n\nlocal SIGN_GROUP = \"NvimTreeRendererSigns\"\n\nlocal namespace_highlights_id = vim.api.nvim_create_namespace(\"NvimTreeHighlights\")\nlocal namespace_extmarks_id = vim.api.nvim_create_namespace(\"NvimTreeExtmarks\")\nlocal namespace_virtual_lines_id = vim.api.nvim_create_namespace(\"NvimTreeVirtualLines\")\n\n---@alias HighlightRangeArgs { higroup:string, start:integer[], finish:integer[] } named arguments for vim.hl.range\n\n---@class (exact) Renderer: nvim_tree.Class\n---@field explorer Explorer\nlocal Renderer = Class:extend()\n\n---@class Renderer\n---@overload fun(args: RendererArgs): Renderer\n\n---@class (exact) RendererArgs\n---@field explorer Explorer\n\n---@protected\n---@param args RendererArgs\nfunction Renderer:new(args)\n  self.explorer = args.explorer\nend\n\n---@private\n---@param bufnr number\n---@param lines string[]\n---@param hl_range_args HighlightRangeArgs[]\n---@param signs string[]\n---@param extmarks table[] extra marks for right icon placement\n---@param virtual_lines table[] virtual lines for hidden count display\nfunction Renderer:_draw(bufnr, lines, hl_range_args, signs, extmarks, virtual_lines)\n  if vim.fn.has(\"nvim-0.10\") == 1 then\n    vim.api.nvim_set_option_value(\"modifiable\", true, { buf = bufnr })\n  else\n    vim.api.nvim_buf_set_option(bufnr, \"modifiable\", true) ---@diagnostic disable-line: deprecated\n  end\n\n  vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines)\n  self:render_hl(bufnr, hl_range_args)\n\n  if vim.fn.has(\"nvim-0.10\") == 1 then\n    vim.api.nvim_set_option_value(\"modifiable\", false, { buf = bufnr })\n  else\n    vim.api.nvim_buf_set_option(bufnr, \"modifiable\", false) ---@diagnostic disable-line: deprecated\n  end\n\n  vim.fn.sign_unplace(SIGN_GROUP)\n  for i, sign_name in pairs(signs) do\n    vim.fn.sign_place(0, SIGN_GROUP, sign_name, bufnr, { lnum = i + 1 })\n  end\n\n  vim.api.nvim_buf_clear_namespace(bufnr, namespace_extmarks_id, 0, -1)\n  for i, extname in pairs(extmarks) do\n    for _, mark in ipairs(extname) do\n      vim.api.nvim_buf_set_extmark(bufnr, namespace_extmarks_id, i, -1, {\n        virt_text     = { { mark.str, mark.hl } },\n        virt_text_pos = \"right_align\",\n        hl_mode       = \"combine\",\n      })\n    end\n  end\n\n  vim.api.nvim_buf_clear_namespace(bufnr, namespace_virtual_lines_id, 0, -1)\n  for line_nr, vlines in pairs(virtual_lines) do\n    vim.api.nvim_buf_set_extmark(bufnr, namespace_virtual_lines_id, line_nr, 0, {\n      virt_lines         = vlines,\n      virt_lines_above   = false,\n      virt_lines_leftcol = true,\n    })\n  end\nend\n\n---@private\n---@param bufnr integer\n---@param hl_range_args HighlightRangeArgs[]\nfunction Renderer:render_hl(bufnr, hl_range_args)\n  if not bufnr or not vim.api.nvim_buf_is_loaded(bufnr) then\n    return\n  end\n  vim.api.nvim_buf_clear_namespace(bufnr, namespace_highlights_id, 0, -1)\n  for _, args in ipairs(hl_range_args) do\n    if vim.fn.has(\"nvim-0.11\") == 1 and vim.hl and vim.hl.range then\n      vim.hl.range(bufnr, namespace_highlights_id, args.higroup, args.start, args.finish, {})\n    else\n      vim.api.nvim_buf_add_highlight(bufnr, namespace_highlights_id, args.higroup, args.start[1], args.start[2], args.finish[2]) ---@diagnostic disable-line: deprecated\n    end\n  end\nend\n\nfunction Renderer:draw()\n  local bufnr = view.get_bufnr()\n  if not bufnr or not vim.api.nvim_buf_is_loaded(bufnr) then\n    return\n  end\n\n  local profile = log.profile_start(\"draw\")\n\n  local cursor = vim.api.nvim_win_get_cursor(view.get_winnr() or 0)\n\n  local builder = Builder(self.explorer):build()\n\n  self:_draw(bufnr, builder.lines, builder.hl_range_args, builder.signs, builder.extmarks, builder.virtual_lines)\n\n  if cursor and #builder.lines >= cursor[1] then\n    vim.api.nvim_win_set_cursor(view.get_winnr() or 0, cursor)\n  end\n\n  view.grow_from_content()\n\n  log.profile_end(profile)\n\n  events._dispatch_on_tree_rendered(bufnr, view.get_winnr())\nend\n\nreturn Renderer\n"
  },
  {
    "path": "lua/nvim-tree/utils.lua",
    "content": "local M = {\n  debouncers = {},\n}\n\nM.is_unix = vim.fn.has(\"unix\") == 1\nM.is_macos = vim.fn.has(\"mac\") == 1 or vim.fn.has(\"macunix\") == 1\nM.is_wsl = vim.fn.has(\"wsl\") == 1\n-- false for WSL\nM.is_windows = vim.fn.has(\"win32\") == 1 or vim.fn.has(\"win32unix\") == 1\n\n---@param haystack string\n---@param needle string\n---@return boolean\nfunction M.str_find(haystack, needle)\n  return vim.fn.stridx(haystack, needle) ~= -1\nend\n\nlocal path_separator = package.config:sub(1, 1)\n---@param paths string[]\n---@return string\nfunction M.path_join(paths)\n  return table.concat(vim.tbl_map(M.path_remove_trailing, paths), path_separator)\nend\n\n---@param path string\n---@return fun(): string\nfunction M.path_split(path)\n  return path:gmatch(\"[^\" .. path_separator .. \"]+\" .. path_separator .. \"?\")\nend\n\n--- Get the basename of the given path.\n---@param path string\n---@return string\nfunction M.path_basename(path)\n  path = M.path_remove_trailing(path)\n  local i = path:match(\"^.*()\" .. path_separator)\n  if not i then\n    return path\n  end\n  return path:sub(i + 1, #path)\nend\n\n--- Check if there are parentheses before brackets, it causes problems for windows.\n--- Refer to issue #2862 and #2961 for more details.\nlocal function has_parentheses_and_brackets(path)\n  local _, i_parentheses = path:find(\"(\", 1, true)\n  local _, i_brackets = path:find(\"[\", 1, true)\n  if i_parentheses and i_brackets then\n    return true\n  end\n  return false\nend\n\n--- Path normalizations for windows only\nlocal function win_norm_path(path)\n  if path == nil then\n    return path\n  end\n  local norm_path = path\n  -- Normalize for issue #2862 and #2961\n  if has_parentheses_and_brackets(norm_path) then\n    norm_path = norm_path:gsub(\"/\", \"\\\\\")\n  end\n  -- Normalize the drive letter\n  norm_path = norm_path:gsub(\"^%l:\", function(drive)\n    return drive:upper()\n  end)\n  return norm_path\nend\n\n--- Get a path relative to another path.\n---@param path string\n---@param relative_to string|nil\n---@return string\nfunction M.path_relative(path, relative_to)\n  if relative_to == nil then\n    return path\n  end\n\n  local norm_path = path\n  if M.is_windows then\n    norm_path = win_norm_path(norm_path)\n  end\n\n  local _, r = norm_path:find(M.path_add_trailing(relative_to), 1, true)\n  local p = norm_path\n  if r then\n    -- take the relative path starting after '/'\n    -- if somehow given a completely matching path,\n    -- returns \"\"\n    p = norm_path:sub(r + 1)\n  end\n  return p\nend\n\n---@param path string\n---@return string\nfunction M.path_add_trailing(path)\n  if path:sub(-1) == path_separator then\n    return path\n  end\n\n  return path .. path_separator\nend\n\n---@param path string\n---@return string\nfunction M.path_remove_trailing(path)\n  local p, _ = path:gsub(path_separator .. \"$\", \"\")\n  return p\nend\n\nM.path_separator = path_separator\n\n---@param extmarks vim.api.keyset.get_extmark_item[] as per vim.api.nvim_buf_get_extmarks\n---@return number\nfunction M.extmarks_length(extmarks)\n  local length = 0\n  for _, extmark in ipairs(extmarks) do\n    local details = extmark[4]\n    if details and details.virt_text then\n      for _, text in ipairs(details.virt_text) do\n        length = length + vim.fn.strchars(text[1])\n      end\n    end\n  end\n  return length\nend\n\nM.default_format_hidden_count = function(hidden_count, simple)\n  local parts = {}\n  local total_count = 0\n  for reason, count in pairs(hidden_count) do\n    total_count = total_count + count\n    if count > 0 then\n      table.insert(parts, reason .. \": \" .. tostring(count))\n    end\n  end\n\n  local hidden_count_string = table.concat(parts, \", \") -- if empty then is \"\" (empty string)\n  if simple then\n    hidden_count_string = \"\"\n  end\n  if total_count > 0 then\n    return \"(\" .. tostring(total_count) .. (simple and \" hidden\" or \" total \") .. hidden_count_string .. \")\"\n  end\n  return nil\nend\n\nfunction M.rename_loaded_buffers(old_path, new_path)\n  -- delete new if it exists\n  for _, buf in pairs(vim.api.nvim_list_bufs()) do\n    if vim.api.nvim_buf_is_loaded(buf) then\n      local buf_name = vim.api.nvim_buf_get_name(buf)\n      if buf_name == new_path then\n        vim.api.nvim_buf_delete(buf, { force = true })\n      end\n    end\n  end\n\n  -- rename old to new\n  for _, buf in pairs(vim.api.nvim_list_bufs()) do\n    if vim.api.nvim_buf_is_loaded(buf) then\n      local buf_name = vim.api.nvim_buf_get_name(buf)\n      local exact_match = buf_name == old_path\n      local child_match = (buf_name:sub(1, #old_path) == old_path and buf_name:sub(#old_path + 1, #old_path + 1) == path_separator)\n      if exact_match or child_match then\n        vim.api.nvim_buf_set_name(buf, new_path .. buf_name:sub(#old_path + 1))\n        -- to avoid the 'overwrite existing file' error message on write for\n        -- normal files\n        local buftype\n        if vim.fn.has(\"nvim-0.10\") == 1 then\n          buftype = vim.api.nvim_get_option_value(\"buftype\", { buf = buf })\n        else\n          buftype = vim.api.nvim_buf_get_option(buf, \"buftype\") ---@diagnostic disable-line: deprecated\n        end\n\n        if buftype == \"\" then\n          vim.api.nvim_buf_call(buf, function()\n            vim.cmd(\"silent! write!\")\n            vim.cmd(\"edit\")\n          end)\n        end\n      end\n    end\n  end\nend\n\nlocal is_windows_drive = function(path)\n  return (M.is_windows) and (path:match(\"^%a:\\\\$\") ~= nil)\nend\n\n---@param path string path to file or directory\n---@return boolean\nfunction M.file_exists(path)\n  if not (M.is_windows or M.is_wsl) then\n    local _, error = vim.loop.fs_stat(path)\n    return error == nil\n  end\n\n  -- Windows is case-insensetive, but case-preserving\n  -- If a file's name is being changed into itself\n  -- with different casing, windows will falsely\n  -- report that file is already existing, so a hand-rolled\n  -- implementation of checking for existance is needed.\n  -- Same holds for WSL, since it can sometimes\n  -- access Windows files directly.\n  -- For more details see (#3117).\n\n  if is_windows_drive(path) then\n    return vim.fn.isdirectory(path) == 1\n  end\n\n  local parent = vim.fn.fnamemodify(path, \":h\")\n  local filename = vim.fn.fnamemodify(path, \":t\")\n\n  local handle = vim.loop.fs_scandir(parent)\n  if not handle then\n    -- File can not exist if its parent directory does not exist\n    return false\n  end\n\n  while true do\n    local name, _ = vim.loop.fs_scandir_next(handle)\n    if not name then\n      break\n    end\n    if name == filename then\n      return true\n    end\n  end\n\n  return false\nend\n\n---@param path string\n---@return string\nfunction M.canonical_path(path)\n  if M.is_windows and path:match(\"^%a:\") then\n    return path:sub(1, 1):upper() .. path:sub(2)\n  end\n  return path\nend\n\n--- Escapes special characters in string for windows, refer to issue #2862 and #2961 for more details.\nlocal function escape_special_char_for_windows(path)\n  if has_parentheses_and_brackets(path) then\n    return path:gsub(\"\\\\\", \"/\"):gsub(\"/ \", \"\\\\ \")\n  end\n  return path:gsub(\"%(\", \"\\\\(\"):gsub(\"%)\", \"\\\\)\")\nend\n\n--- Escapes special characters in string if windows else returns unmodified string.\n---@param path string\n---@return string|nil\nfunction M.escape_special_chars(path)\n  if path == nil then\n    return path\n  end\n  return M.is_windows and escape_special_char_for_windows(path) or path\nend\n\nlocal function round(value)\n  -- Amount of digits to round to after floating point.\n  local digits = 2\n  local round_number = 10 ^ digits\n  return math.floor((value * round_number) + 0.5) / round_number\nend\n\nfunction M.format_bytes(bytes)\n  local units = { \"B\", \"K\", \"M\", \"G\", \"T\", \"P\", \"E\", \"Z\", \"Y\" }\n  local i = \"i\" -- bInary\n\n  bytes = math.max(bytes, 0)\n  local pow = math.floor((bytes and math.log(bytes) or 0) / math.log(1024))\n  pow = math.min(pow, #units)\n\n  local value = round(bytes / (1024 ^ pow))\n\n  pow = pow + 1\n\n  -- units[pow] == nil when size == 0 B or size >= 1024 YiB\n  if units[pow] == nil or pow == 1 then\n    if bytes < 1024 then\n      return bytes .. \" \" .. units[1]\n    else\n      -- Use the biggest adopted multiple of 2 instead of bytes.\n      value = round(bytes / (1024 ^ (#units - 1)))\n      -- For big numbers decimal part is not useful.\n      return string.format(\"%.0f %s%s%s\", value, units[#units], i, units[1])\n    end\n  else\n    return value .. \" \" .. units[pow] .. i .. units[1]\n  end\nend\n\nfunction M.key_by(tbl, key)\n  local keyed = {}\n  for _, val in ipairs(tbl) do\n    if val[key] then\n      keyed[val[key]] = val\n    end\n  end\n  return keyed\nend\n\nfunction M.bool_record(tbl, key)\n  local keyed = {}\n  for _, val in ipairs(tbl) do\n    keyed[val[key]] = true\n  end\n  return keyed\nend\n\nlocal function timer_stop_close(timer)\n  if timer:is_active() then\n    timer:stop()\n  end\n  if not timer:is_closing() then\n    timer:close()\n  end\nend\n\n---Execute callback timeout ms after the latest invocation with context.\n---Waiting invocations for that context will be discarded.\n---Invocation will be rescheduled while a callback is being executed.\n---Caller must ensure that callback performs the same or functionally equivalent actions.\n---\n---@param context string identifies the callback to debounce\n---@param timeout number ms to wait\n---@param callback function to execute on completion\nfunction M.debounce(context, timeout, callback)\n  -- all execution here is done in a synchronous context; no thread safety required\n\n  M.debouncers[context] = M.debouncers[context] or {}\n  local debouncer = M.debouncers[context]\n\n  -- cancel waiting or executing timer\n  if debouncer.timer then\n    timer_stop_close(debouncer.timer)\n  end\n\n  local timer = vim.loop.new_timer()\n  if not timer then\n    return\n  end\n  debouncer.timer = timer\n  timer:start(timeout, 0, function()\n    timer_stop_close(timer)\n\n    -- reschedule when callback is running\n    if debouncer.executing then\n      M.debounce(context, timeout, callback)\n      return\n    end\n\n    -- call back at a safe time\n    debouncer.executing = true\n    vim.schedule(function()\n      callback()\n      debouncer.executing = false\n\n      -- no other timer waiting\n      if debouncer.timer == timer then\n        M.debouncers[context] = nil\n      end\n    end)\n  end)\nend\n\n---@param path string\n---@return integer|nil\n---@return integer|nil\nfunction M.get_win_buf_from_path(path)\n  for _, w in pairs(vim.api.nvim_tabpage_list_wins(0)) do\n    local b = vim.api.nvim_win_get_buf(w)\n    if vim.api.nvim_buf_get_name(b) == path then\n      return w, b\n    end\n  end\n  return nil, nil\nend\n\nfunction M.clear_prompt()\n  if vim.opt.cmdheight._value ~= 0 then\n    vim.cmd(\"normal! :\")\n  end\nend\n\n--- Return a new table with values from array\n---@param array table\n---@return table\nfunction M.array_shallow_clone(array)\n  local to = {}\n  for _, v in ipairs(array) do\n    table.insert(to, v)\n  end\n  return to\nend\n\n--- Remove and return item from array if present.\n---@param array table\n---@param item any\n---@return any|nil removed\nfunction M.array_remove(array, item)\n  if not array then\n    return nil\n  end\n  for i, v in ipairs(array) do\n    if v == item then\n      table.remove(array, i)\n      return v\n    end\n  end\nend\n\n---@param array table\n---@return table\nfunction M.array_remove_nils(array)\n  return vim.tbl_filter(function(v)\n    return v ~= nil\n  end, array)\nend\n\n--- Is the buffer named NvimTree_[0-9]+ a tree? filetype is \"NvimTree\" or not readable file.\n--- This is cheap, as the readable test should only ever be needed when resuming a vim session.\n---@param bufnr number|nil may be 0 or nil for current\n---@return boolean\nfunction M.is_nvim_tree_buf(bufnr)\n  if bufnr == nil then\n    bufnr = 0\n  end\n  if vim.api.nvim_buf_is_valid(bufnr) then\n    local bufname = vim.api.nvim_buf_get_name(bufnr)\n    if vim.fn.fnamemodify(bufname, \":t\"):match(\"^NvimTree_[0-9]+$\") then\n      if vim.bo[bufnr].filetype == \"NvimTree\" then\n        return true\n      elseif vim.fn.filereadable(bufname) == 0 then\n        return true\n      end\n    end\n  end\n  return false\nend\n\n--- path is an executable file or directory\n---@param absolute_path string\n---@return boolean\nfunction M.is_executable(absolute_path)\n  if M.is_windows or M.is_wsl then\n    --- executable detection on windows is buggy and not performant hence it is disabled\n    return false\n  else\n    return vim.loop.fs_access(absolute_path, \"X\") or false\n  end\nend\n\n---List of all option info/values\n---@param opts vim.api.keyset.option passed directly to vim.api.nvim_get_option_info2 and vim.api.nvim_get_option_value\n---@param was_set boolean filter was_set\n---@return { info: vim.api.keyset.get_option_info, val: any }[]\nfunction M.enumerate_options(opts, was_set)\n  local res = {}\n\n  local infos = vim.tbl_filter(function(info)\n    if opts.buf and info.scope ~= \"buf\" then\n      return false\n    elseif opts.win and info.scope ~= \"win\" then\n      return false\n    else\n      return true\n    end\n  end, vim.api.nvim_get_all_options_info())\n\n  for _, info in vim.spairs(infos) do\n    local _, info2 = pcall(vim.api.nvim_get_option_info2, info.name, opts)\n    if not was_set or info2.was_set then\n      local val = pcall(vim.api.nvim_get_option_value, info.name, opts)\n      table.insert(res, { info = info2, val = val })\n    end\n  end\n\n  return res\nend\n\n---Filter out nodes that are descendants of other nodes in the list.\n---When a directory is selected along with its children, only the directory needs to be operated on.\n---@param nodes Node[]\n---@return Node[]\nfunction M.filter_descendant_nodes(nodes)\n  return vim.tbl_filter(function(node)\n    local parent = node.parent\n    while parent do\n      if vim.tbl_contains(nodes, parent) then\n        return false\n      end\n      parent = parent.parent\n    end\n    return true\n  end, nodes)\nend\n\n---Build confirmation prompt strings based on default_yes config.\n---@param prompt_select string\n---@param default_yes boolean\n---@return string prompt_input\n---@return string[] items_short\n---@return string[] items_long\nfunction M.confirm_prompt(prompt_select, default_yes)\n  if default_yes then\n    return prompt_select .. \" Y/n: \", { \"\", \"n\" }, { \"Yes\", \"No\" }\n  else\n    return prompt_select .. \" y/N: \", { \"\", \"y\" }, { \"No\", \"Yes\" }\n  end\nend\n\n---Check if the current mode is visual or select (v, V, CTRL-V, s, S, CTRL-S).\n---@return boolean\nfunction M.is_visual_mode()\n  local mode = vim.api.nvim_get_mode().mode\n  local visual_modes = {\n    v = true,\n    V = true,\n    [\"\\22\"] = true, -- \\22 is CTRL-V\n    s = true,\n    S = true,\n    [\"\\19\"] = true, -- \\19 is CTRL-S\n  }\n  return visual_modes[mode] == true or false\nend\n\n---Exit visual mode synchronously.\nfunction M.exit_visual_mode()\n  local esc = vim.api.nvim_replace_termcodes(\"<Esc>\", true, false, true)\n  vim.api.nvim_feedkeys(esc, \"nx\", false)\nend\n\n---Get the visual selection range nodes, exiting visual mode.\n---@return Node[]?\nfunction M.get_visual_nodes()\n  local explorer = require(\"nvim-tree.core\").get_explorer()\n  if not explorer then\n    return nil\n  end\n  local start_line = vim.fn.line(\"v\")\n  local end_line = vim.fn.line(\".\")\n  if start_line > end_line then\n    start_line, end_line = end_line, start_line\n  end\n  local nodes = explorer:get_nodes_in_range(start_line, end_line)\n  M.exit_visual_mode()\n  return nodes\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/view.lua",
    "content": "local events = require(\"nvim-tree.events\")\nlocal utils = require(\"nvim-tree.utils\")\nlocal log = require(\"nvim-tree.log\")\nlocal notify = require(\"nvim-tree.notify\")\n\n---@class OpenInWinOpts\n---@field hijack_current_buf boolean|nil default true\n---@field resize boolean|nil default true\n---@field winid number|nil 0 or nil for current\n\nlocal M = {}\n\nlocal DEFAULT_MIN_WIDTH = 30\nlocal DEFAULT_MAX_WIDTH = -1\nlocal DEFAULT_LINES_EXCLUDED = {\n  \"root\",\n}\nlocal DEFAULT_PADDING = 1\n\nM.View = {\n  adaptive_size        = false,\n  centralize_selection = false,\n  tabpages             = {},\n  cursors              = {},\n  hide_root_folder     = false,\n  live_filter          = {\n    prev_focused_node = nil,\n  },\n  winopts              = {\n    relativenumber = false,\n    number         = false,\n    list           = false,\n    foldenable     = false,\n    winfixwidth    = true,\n    winfixheight   = true,\n    spell          = false,\n    signcolumn     = \"yes\",\n    foldmethod     = \"manual\",\n    foldcolumn     = \"0\",\n    cursorcolumn   = false,\n    cursorline     = true,\n    cursorlineopt  = \"both\",\n    colorcolumn    = \"0\",\n    wrap           = false,\n    winhl          = table.concat({\n      \"EndOfBuffer:NvimTreeEndOfBuffer\",\n      \"CursorLine:NvimTreeCursorLine\",\n      \"CursorLineNr:NvimTreeCursorLineNr\",\n      \"LineNr:NvimTreeLineNr\",\n      \"WinSeparator:NvimTreeWinSeparator\",\n      \"StatusLine:NvimTreeStatusLine\",\n      \"StatusLineNC:NvimTreeStatuslineNC\",\n      \"SignColumn:NvimTreeSignColumn\",\n      \"Normal:NvimTreeNormal\",\n      \"NormalNC:NvimTreeNormalNC\",\n      \"NormalFloat:NvimTreeNormalFloat\",\n      \"FloatBorder:NvimTreeNormalFloatBorder\",\n    }, \",\"),\n  },\n}\n\n-- The initial state of a tab\nlocal tabinitial = {\n  -- The position of the cursor { line, column }\n  cursor = { 0, 0 },\n  -- The NvimTree window number\n  winnr = nil,\n}\n\nlocal BUFNR_PER_TAB = {}\n\n---@type { name: string, value: any }[]\nlocal BUFFER_OPTIONS = {\n  { name = \"bufhidden\",  value = \"wipe\" },\n  { name = \"buflisted\",  value = false },\n  { name = \"buftype\",    value = \"nofile\" },\n  { name = \"filetype\",   value = \"NvimTree\" },\n  { name = \"modifiable\", value = false },\n  { name = \"swapfile\",   value = false },\n}\n\n---@param bufnr integer\n---@return boolean\nlocal function matches_bufnr(bufnr)\n  for _, b in pairs(BUFNR_PER_TAB) do\n    if b == bufnr then\n      return true\n    end\n  end\n  return false\nend\n\nlocal function wipe_rogue_buffer()\n  for _, bufnr in ipairs(vim.api.nvim_list_bufs()) do\n    if not matches_bufnr(bufnr) and utils.is_nvim_tree_buf(bufnr) then\n      pcall(vim.api.nvim_buf_delete, bufnr, { force = true })\n    end\n  end\nend\n\n---@param bufnr integer|boolean|nil\nlocal function create_buffer(bufnr)\n  wipe_rogue_buffer()\n\n  local tab = vim.api.nvim_get_current_tabpage()\n  BUFNR_PER_TAB[tab] = bufnr or vim.api.nvim_create_buf(false, false)\n\n  bufnr = M.get_bufnr()\n  for _, option in ipairs(BUFFER_OPTIONS) do\n    vim.api.nvim_set_option_value(option.name, option.value, { buf = bufnr })\n  end\n\n  vim.api.nvim_buf_set_name(M.get_bufnr(), \"NvimTree_\" .. tab)\n\n  require(\"nvim-tree.keymap\").on_attach(M.get_bufnr())\n\n  events._dispatch_tree_attached_post(M.get_bufnr())\nend\n\n---@param size (fun():integer)|integer|string\n---@return integer\nlocal function get_size(size)\n  if type(size) == \"number\" then\n    return size\n  elseif type(size) == \"function\" then\n    return get_size(size())\n  end\n  local size_as_number = tonumber(size:sub(0, -2))\n  local percent_as_decimal = size_as_number / 100\n  return math.floor(vim.o.columns * percent_as_decimal)\nend\n\n---@param size (fun():integer)|integer|nil\n---@return integer\nlocal function get_width(size)\n  if size then\n    return get_size(size)\n  else\n    return get_size(M.View.width)\n  end\nend\n\nlocal move_tbl = {\n  left = \"H\",\n  right = \"L\",\n}\n\n-- setup_tabpage sets up the initial state of a tab\n---@param tabpage integer\nlocal function setup_tabpage(tabpage)\n  local winnr = vim.api.nvim_get_current_win()\n  M.View.tabpages[tabpage] = vim.tbl_extend(\"force\", M.View.tabpages[tabpage] or tabinitial, { winnr = winnr })\nend\n\nlocal function set_window_options_and_buffer()\n  pcall(vim.api.nvim_command, \"buffer \" .. M.get_bufnr())\n\n  if vim.fn.has(\"nvim-0.10\") == 1 then\n    local eventignore = vim.api.nvim_get_option_value(\"eventignore\", {})\n    vim.api.nvim_set_option_value(\"eventignore\", \"all\", {})\n\n    for k, v in pairs(M.View.winopts) do\n      vim.api.nvim_set_option_value(k, v, { scope = \"local\" })\n    end\n\n    vim.api.nvim_set_option_value(\"eventignore\", eventignore, {})\n  else\n    local eventignore = vim.api.nvim_get_option(\"eventignore\") ---@diagnostic disable-line: deprecated\n    vim.api.nvim_set_option(\"eventignore\", \"all\") ---@diagnostic disable-line: deprecated\n\n    -- #3009 vim.api.nvim_win_set_option does not set local scope without explicit winid.\n    -- Revert to opt_local instead of propagating it through for just the 0.10 path.\n    for k, v in pairs(M.View.winopts) do\n      vim.opt_local[k] = v\n    end\n\n    vim.api.nvim_set_option(\"eventignore\", eventignore) ---@diagnostic disable-line: deprecated\n  end\nend\n\n---@return table\nlocal function open_win_config()\n  if type(M.View.float.open_win_config) == \"function\" then\n    return M.View.float.open_win_config()\n  else\n    return M.View.float.open_win_config\n  end\nend\n\nlocal function open_window()\n  if M.View.float.enable then\n    vim.api.nvim_open_win(0, true, open_win_config())\n  else\n    vim.api.nvim_command(\"vsp\")\n    M.reposition_window()\n  end\n  setup_tabpage(vim.api.nvim_get_current_tabpage())\n  set_window_options_and_buffer()\nend\n\n---@param buf integer\n---@return boolean\nlocal function is_buf_displayed(buf)\n  return vim.api.nvim_buf_is_valid(buf) and vim.fn.buflisted(buf) == 1\nend\n\n---@return number|nil\nlocal function get_alt_or_next_buf()\n  local alt_buf = vim.fn.bufnr(\"#\")\n  if is_buf_displayed(alt_buf) then\n    return alt_buf\n  end\n\n  for _, buf in ipairs(vim.api.nvim_list_bufs()) do\n    if is_buf_displayed(buf) then\n      return buf\n    end\n  end\nend\n\nlocal function switch_buf_if_last_buf()\n  if #vim.api.nvim_list_wins() == 1 then\n    local buf = get_alt_or_next_buf()\n    if buf then\n      vim.cmd(\"sb\" .. buf)\n    else\n      vim.cmd(\"new\")\n    end\n  end\nend\n\n-- save_tab_state saves any state that should be preserved across redraws.\n---@param tabnr integer\nlocal function save_tab_state(tabnr)\n  local tabpage = tabnr or vim.api.nvim_get_current_tabpage()\n  M.View.cursors[tabpage] = vim.api.nvim_win_get_cursor(M.get_winnr(tabpage) or 0)\nend\n\n---@param tabpage integer\nlocal function close(tabpage)\n  if not M.is_visible({ tabpage = tabpage }) then\n    return\n  end\n  save_tab_state(tabpage)\n  switch_buf_if_last_buf()\n  local tree_win = M.get_winnr(tabpage)\n  local current_win = vim.api.nvim_get_current_win()\n  for _, win in pairs(vim.api.nvim_tabpage_list_wins(tabpage)) do\n    if vim.api.nvim_win_get_config(win).relative == \"\" then\n      local prev_win = vim.fn.winnr(\"#\") -- this tab only\n      if tree_win == current_win and prev_win > 0 then\n        vim.api.nvim_set_current_win(vim.fn.win_getid(prev_win))\n      end\n      if vim.api.nvim_win_is_valid(tree_win or 0) then\n        local success, error = pcall(vim.api.nvim_win_close, tree_win or 0, true)\n        if not success then\n          notify.debug(\"Failed to close window: \" .. error)\n          return\n        end\n      end\n      return\n    end\n  end\nend\n\nfunction M.close_this_tab_only()\n  close(vim.api.nvim_get_current_tabpage())\nend\n\nfunction M.close_all_tabs()\n  for tabpage, _ in pairs(M.View.tabpages) do\n    close(tabpage)\n  end\nend\n\n---@param tabpage integer|nil\nfunction M.close(tabpage)\n  if M.View.tab.sync.close then\n    M.close_all_tabs()\n  elseif tabpage then\n    close(tabpage)\n  else\n    M.close_this_tab_only()\n  end\nend\n\n---@param options table|nil\nfunction M.open(options)\n  if M.is_visible() then\n    return\n  end\n\n  local profile = log.profile_start(\"view open\")\n\n  events._dispatch_on_tree_pre_open()\n  create_buffer()\n  open_window()\n  M.resize()\n\n  local opts = options or { focus_tree = true }\n  if not opts.focus_tree then\n    vim.cmd(\"wincmd p\")\n  end\n  events._dispatch_on_tree_open()\n\n  log.profile_end(profile)\nend\n\nlocal function grow()\n  local starts_at = (M.is_root_folder_visible(require(\"nvim-tree.core\").get_cwd()) and M.View.root_excluded) and 1 or 0\n  local lines = vim.api.nvim_buf_get_lines(M.get_bufnr(), starts_at, -1, false)\n  -- number of columns of right-padding to indicate end of path\n  local padding = get_size(M.View.padding)\n\n  -- account for sign/number columns etc.\n  local wininfo = vim.fn.getwininfo(M.get_winnr())\n  if type(wininfo) == \"table\" and type(wininfo[1]) == \"table\" then\n    padding = padding + wininfo[1].textoff\n  end\n\n  local final_width = M.View.initial_width\n  local max_width = get_width(M.View.max_width)\n  if max_width == -1 then\n    max_width = math.huge\n  end\n\n  local ns_id = vim.api.nvim_get_namespaces()[\"NvimTreeExtmarks\"]\n  for i, l in pairs(lines) do\n    local line_nr = starts_at + i - 1\n    local line_width = vim.fn.strchars(l)\n    -- also add space for right-aligned icons\n    local extmarks = vim.api.nvim_buf_get_extmarks(M.get_bufnr(), ns_id, { line_nr, 0 }, { line_nr, -1 }, { details = true })\n    line_width = line_width + utils.extmarks_length(extmarks) + padding\n    final_width = math.max(final_width, line_width)\n    if final_width >= max_width then\n      final_width = max_width\n      break\n    end\n  end\n  M.resize(final_width)\nend\n\nfunction M.grow_from_content()\n  if M.View.adaptive_size then\n    grow()\n  end\nend\n\n---@param size string|number|nil\nfunction M.resize(size)\n  if M.View.float.enable and not M.View.adaptive_size then\n    -- if the floating windows's adaptive size is not desired, then the\n    -- float size should be defined in view.float.open_win_config\n    return\n  end\n\n  if type(size) == \"string\" then\n    size = vim.trim(size)\n    local first_char = size:sub(1, 1)\n    size = tonumber(size)\n\n    if first_char == \"+\" or first_char == \"-\" then\n      size = M.View.width + size\n    end\n  end\n\n  if type(size) == \"number\" and size <= 0 then\n    return\n  end\n\n  if size then\n    M.View.width = size\n    M.View.height = size\n  end\n\n  if not M.is_visible() then\n    return\n  end\n\n  local winnr = M.get_winnr() or 0\n\n  local new_size = get_width()\n\n  if new_size ~= vim.api.nvim_win_get_width(winnr) then\n    vim.api.nvim_win_set_width(winnr, new_size)\n    if not M.View.preserve_window_proportions then\n      vim.cmd(\":wincmd =\")\n    end\n  end\n\n  events._dispatch_on_tree_resize(new_size)\nend\n\nfunction M.reposition_window()\n  local move_to = move_tbl[M.View.side]\n  vim.api.nvim_command(\"wincmd \" .. move_to)\n  M.resize()\nend\n\nlocal function set_current_win()\n  local current_tab = vim.api.nvim_get_current_tabpage()\n  M.View.tabpages[current_tab].winnr = vim.api.nvim_get_current_win()\nend\n\n---Open the tree in the a window\n---@param opts OpenInWinOpts|nil\nfunction M.open_in_win(opts)\n  opts = opts or { hijack_current_buf = true, resize = true }\n  events._dispatch_on_tree_pre_open()\n  if opts.winid and vim.api.nvim_win_is_valid(opts.winid) then\n    vim.api.nvim_set_current_win(opts.winid)\n  end\n  create_buffer(opts.hijack_current_buf and vim.api.nvim_get_current_buf())\n  setup_tabpage(vim.api.nvim_get_current_tabpage())\n  set_current_win()\n  set_window_options_and_buffer()\n  if opts.resize then\n    M.reposition_window()\n    M.resize()\n  end\n  events._dispatch_on_tree_open()\nend\n\nfunction M.abandon_current_window()\n  local tab = vim.api.nvim_get_current_tabpage()\n  BUFNR_PER_TAB[tab] = nil\n  if M.View.tabpages[tab] then\n    M.View.tabpages[tab].winnr = nil\n  end\nend\n\nfunction M.abandon_all_windows()\n  for tab, _ in pairs(vim.api.nvim_list_tabpages()) do\n    BUFNR_PER_TAB[tab] = nil\n    if M.View.tabpages[tab] then\n      M.View.tabpages[tab].winnr = nil\n    end\n  end\nend\n\n---@param opts table|nil\n---@return boolean\nfunction M.is_visible(opts)\n  if opts and opts.tabpage then\n    if M.View.tabpages[opts.tabpage] == nil then\n      return false\n    end\n    local winnr = M.View.tabpages[opts.tabpage].winnr\n    return winnr and vim.api.nvim_win_is_valid(winnr)\n  end\n\n  if opts and opts.any_tabpage then\n    for _, v in pairs(M.View.tabpages) do\n      if v.winnr and vim.api.nvim_win_is_valid(v.winnr) then\n        return true\n      end\n    end\n    return false\n  end\n\n  return M.get_winnr() ~= nil and vim.api.nvim_win_is_valid(M.get_winnr() or 0)\nend\n\n---@param opts table|nil\nfunction M.set_cursor(opts)\n  if M.is_visible() then\n    pcall(vim.api.nvim_win_set_cursor, M.get_winnr(), opts)\n  end\nend\n\n---@param winnr number|nil\n---@param open_if_closed boolean|nil\nfunction M.focus(winnr, open_if_closed)\n  local wnr = winnr or M.get_winnr()\n\n  if vim.api.nvim_win_get_tabpage(wnr or 0) ~= vim.api.nvim_win_get_tabpage(0) then\n    M.close()\n    M.open()\n    wnr = M.get_winnr()\n  elseif open_if_closed and not M.is_visible() then\n    M.open()\n  end\n\n  if wnr then\n    vim.api.nvim_set_current_win(wnr)\n  end\nend\n\n--- Retrieve the winid of the open tree.\n---@param opts? nvim_tree.api.tree.winid.Opts\n---@return number? winid unlike get_winnr(), this returns nil if the nvim-tree window is not visible\nfunction M.winid(opts)\n  local tabpage = opts and opts.tabpage\n  if tabpage == 0 then\n    tabpage = vim.api.nvim_get_current_tabpage()\n  end\n  if M.is_visible({ tabpage = tabpage }) then\n    return M.get_winnr(tabpage)\n  else\n    return nil\n  end\nend\n\n--- Restores the state of a NvimTree window if it was initialized before.\nfunction M.restore_tab_state()\n  local tabpage = vim.api.nvim_get_current_tabpage()\n  M.set_cursor(M.View.cursors[tabpage])\nend\n\n--- Returns the window number for nvim-tree within the tabpage specified\n---@param tabpage number|nil (optional) the number of the chosen tabpage. Defaults to current tabpage.\n---@return number|nil\nfunction M.get_winnr(tabpage)\n  tabpage = tabpage or vim.api.nvim_get_current_tabpage()\n  local tabinfo = M.View.tabpages[tabpage]\n  if tabinfo and tabinfo.winnr and vim.api.nvim_win_is_valid(tabinfo.winnr) then\n    return tabinfo.winnr\n  end\nend\n\n--- Returns the current nvim tree bufnr\n---@return number\nfunction M.get_bufnr()\n  return BUFNR_PER_TAB[vim.api.nvim_get_current_tabpage()]\nend\n\nfunction M._prevent_buffer_override()\n  local view_winnr = M.get_winnr()\n  local view_bufnr = M.get_bufnr()\n\n  -- need to schedule to let the new buffer populate the window\n  -- because this event needs to be run on bufWipeout.\n  -- Otherwise the curwin/curbuf would match the view buffer and the view window.\n  vim.schedule(function()\n    local curwin = vim.api.nvim_get_current_win()\n    local curwinconfig = vim.api.nvim_win_get_config(curwin)\n    local curbuf = vim.api.nvim_win_get_buf(curwin)\n    local bufname = vim.api.nvim_buf_get_name(curbuf)\n\n    if not bufname:match(\"NvimTree\") then\n      for i, tabpage in ipairs(M.View.tabpages) do\n        if tabpage.winnr == view_winnr then\n          M.View.tabpages[i] = nil\n          break\n        end\n      end\n    end\n    if curwin ~= view_winnr or bufname == \"\" or curbuf == view_bufnr then\n      return\n    end\n\n    -- patch to avoid the overriding window to be fixed in size\n    -- might need a better patch\n    vim.cmd(\"setlocal nowinfixwidth\")\n    vim.cmd(\"setlocal nowinfixheight\")\n    M.open({ focus_tree = false })\n\n    local explorer = require(\"nvim-tree.core\").get_explorer()\n    if explorer then\n      explorer.renderer:draw()\n    end\n\n    pcall(vim.api.nvim_win_close, curwin, { force = true })\n\n    -- to handle opening a file using :e when nvim-tree is on floating mode\n    -- falling back to the current window instead of creating a new one\n    if curwinconfig.relative ~= \"\" then\n      require(\"nvim-tree.actions.node.open-file\").fn(\"edit_in_place\", bufname)\n    else\n      require(\"nvim-tree.actions.node.open-file\").fn(\"edit\", bufname)\n    end\n  end)\nend\n\n---@param cwd string|nil\n---@return boolean\nfunction M.is_root_folder_visible(cwd)\n  return cwd ~= \"/\" and not M.View.hide_root_folder\nend\n\n-- used on ColorScheme event\nfunction M.reset_winhl()\n  local winnr = M.get_winnr()\n  if winnr and vim.api.nvim_win_is_valid(winnr) then\n    vim.wo[M.get_winnr()].winhl = M.View.winopts.winhl\n  end\nend\n\n---Check if width determined or calculated on-fly\n---@return boolean\nfunction M.is_width_determined()\n  return type(M.View.width) ~= \"function\"\nend\n\n---Configure width-related config\n---@param width string|function|number|table|nil\nfunction M.configure_width(width)\n  if type(width) == \"table\" then\n    M.View.adaptive_size = true\n    M.View.width = width.min or DEFAULT_MIN_WIDTH\n    M.View.max_width = width.max or DEFAULT_MAX_WIDTH\n    local lines_excluded = width.lines_excluded or DEFAULT_LINES_EXCLUDED\n    M.View.root_excluded = vim.tbl_contains(lines_excluded, \"root\")\n    M.View.padding = width.padding or DEFAULT_PADDING\n  elseif width == nil then\n    if M.config.width ~= nil then\n      -- if we had input config - fallback to it\n      M.configure_width(M.config.width)\n    else\n      -- otherwise - restore initial width\n      M.View.width = M.View.initial_width\n    end\n  else\n    M.View.adaptive_size = false\n    M.View.width = width\n  end\nend\n\nfunction M.setup(opts)\n  local options = opts.view or {}\n  M.View.centralize_selection = options.centralize_selection\n  M.View.side = (options.side == \"right\") and \"right\" or \"left\"\n  M.View.height = options.height\n  M.View.hide_root_folder = opts.renderer.root_folder_label == false\n  M.View.tab = opts.tab\n  M.View.preserve_window_proportions = options.preserve_window_proportions\n  M.View.winopts.cursorline = options.cursorline\n  M.View.winopts.cursorlineopt = options.cursorlineopt\n  M.View.winopts.number = options.number\n  M.View.winopts.relativenumber = options.relativenumber\n  M.View.winopts.signcolumn = options.signcolumn\n  M.View.float = options.float\n  M.on_attach = opts.on_attach\n\n  M.config = options\n  M.configure_width(options.width)\n\n  M.View.initial_width = get_width()\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree/watcher.lua",
    "content": "local notify = require(\"nvim-tree.notify\")\nlocal log = require(\"nvim-tree.log\")\nlocal utils = require(\"nvim-tree.utils\")\n\nlocal Class = require(\"nvim-tree.classic\")\n\nlocal MESSAGE_EMFILE = \"fs.inotify.max_user_watches exceeded, see https://github.com/nvim-tree/nvim-tree.lua/wiki/Troubleshooting\"\n\nlocal FS_EVENT_FLAGS = {\n  -- inotify or equivalent will be used; fallback to stat has not yet been implemented\n  stat = false,\n  -- recursive is not functional in neovim's libuv implementation\n  recursive = false,\n}\n\nlocal M = {\n  config = {},\n}\n\n---Registry of all events\n---@type Event[]\nlocal events = {}\n\n---@class (exact) Event: nvim_tree.Class\n---@field destroyed boolean\n---@field private path string\n---@field private fs_event uv.uv_fs_event_t?\n---@field private listeners function[]\nlocal Event = Class:extend()\n\n---@class Event\n---@overload fun(args: EventArgs): Event\n\n---@class (exact) EventArgs\n---@field path string\n\n---@protected\n---@param args EventArgs\nfunction Event:new(args)\n  self.destroyed = false\n  self.path      = args.path\n  self.fs_event  = nil\n  self.listeners = {}\nend\n\n---Static factory method\n---Creates and starts an Event\n---nil on failure to start\n---@param args EventArgs\n---@return Event?\nfunction Event:create(args)\n  log.line(\"watcher\", \"Event:create '%s'\", args.path)\n\n  local event = Event(args)\n\n  if event:start() then\n    events[event.path] = event\n    return event\n  else\n    return nil\n  end\nend\n\n---@return boolean\nfunction Event:start()\n  log.line(\"watcher\", \"Event:start '%s'\", self.path)\n\n  local rc, _, name\n\n  self.fs_event, _, name = vim.loop.new_fs_event()\n  if not self.fs_event then\n    self.fs_event = nil\n    notify.warn(string.format(\"Could not initialize an fs_event watcher for path %s : %s\", self.path, name))\n    return false\n  end\n\n  local event_cb = vim.schedule_wrap(function(err, filename)\n    if err then\n      log.line(\"watcher\", \"event_cb '%s' '%s' FAIL : %s\", self.path, filename, err)\n\n      -- do nothing if watchers have already been disabled\n      if not M.config.filesystem_watchers.enable then\n        return\n      end\n\n      -- EMFILE is catastrophic\n      if name == \"EMFILE\" then\n        M.disable_watchers(MESSAGE_EMFILE)\n        return\n      end\n\n      local message = string.format(\"File system watcher failed (%s) for path %s, halting watcher.\", err, self.path)\n      if err == \"EPERM\" and (utils.is_windows or utils.is_wsl) then\n        -- on directory removal windows will cascade the filesystem events out of order\n        log.line(\"watcher\", message)\n        self:destroy()\n      else\n        self:destroy(message)\n      end\n    else\n      log.line(\"watcher\", \"event_cb '%s' '%s'\", self.path, filename)\n      for _, listener in ipairs(self.listeners) do\n        listener(filename)\n      end\n    end\n  end)\n\n  rc, _, name = self.fs_event:start(self.path, FS_EVENT_FLAGS, event_cb)\n  if rc ~= 0 then\n    if name == \"EMFILE\" then\n      M.disable_watchers(MESSAGE_EMFILE)\n    else\n      notify.warn(string.format(\"Could not start the fs_event watcher for path %s : %s\", self.path, name))\n    end\n    return false\n  end\n\n  return true\nend\n\n---@param listener function\nfunction Event:add(listener)\n  table.insert(self.listeners, listener)\nend\n\n---@param listener function\nfunction Event:remove(listener)\n  utils.array_remove(self.listeners, listener)\n  if #self.listeners == 0 then\n    self:destroy()\n  end\nend\n\n---@param message string|nil\nfunction Event:destroy(message)\n  log.line(\"watcher\", \"Event:destroy '%s'\", self.path)\n\n  if self.fs_event then\n    if message then\n      notify.warn(message)\n    end\n\n    local rc, _, name = self.fs_event:stop()\n    if rc ~= 0 then\n      notify.warn(string.format(\"Could not stop the fs_event watcher for path %s : %s\", self.path, name))\n    end\n    self.fs_event = nil\n  end\n\n  self.destroyed = true\n  events[self.path] = nil\nend\n\n---Registry of all watchers\n---@type Watcher[]\nlocal watchers = {}\n\n---@class (exact) Watcher: nvim_tree.Class\n---@field data table user data\n---@field destroyed boolean\n---@field private path string\n---@field private callback fun(watcher: Watcher)\n---@field private files string[]?\n---@field private listener fun(filename: string)?\n---@field private event Event\nlocal Watcher = Class:extend()\n\n---@class Watcher\n---@overload fun(args: WatcherArgs): Watcher\n\n---@class (exact) WatcherArgs\n---@field path string\n---@field files string[]|nil\n---@field callback fun(watcher: Watcher)\n---@field data table? user data\n\n---@protected\n---@param args WatcherArgs\nfunction Watcher:new(args)\n  self.data      = args.data\n  self.destroyed = false\n  self.path      = args.path\n  self.callback  = args.callback\n  self.files     = args.files\n  self.listener  = nil\nend\n\n---Static factory method\n---Creates and starts a Watcher\n---nil on failure to create Event\n---@param args WatcherArgs\n---@return Watcher|nil\nfunction Watcher:create(args)\n  log.line(\"watcher\", \"Watcher:create '%s' %s\", args.path, vim.inspect(args.files))\n\n  local event = events[args.path] or Event:create({ path = args.path })\n  if not event then\n    return nil\n  end\n\n  local watcher = Watcher(args)\n\n  watcher.event = event\n\n  watcher:start()\n\n  table.insert(watchers, watcher)\n\n  return watcher\nend\n\nfunction Watcher:start()\n  self.listener = function(filename)\n    if not self.files or vim.tbl_contains(self.files, filename) then\n      self.callback(self)\n    end\n  end\n\n  self.event:add(self.listener)\nend\n\nfunction Watcher:destroy()\n  log.line(\"watcher\", \"Watcher:destroy '%s'\", self.path)\n\n  self.event:remove(self.listener)\n\n  utils.array_remove(\n    watchers,\n    self\n  )\n\n  self.destroyed = true\nend\n\nM.Watcher = Watcher\n\n--- Permanently disable watchers and purge all state following a catastrophic error.\n---@param msg string\nfunction M.disable_watchers(msg)\n  notify.warn(string.format(\"Disabling watchers: %s\", msg))\n  M.config.filesystem_watchers.enable = false\n  require(\"nvim-tree\").purge_all_state()\nend\n\nfunction M.purge_watchers()\n  log.line(\"watcher\", \"purge_watchers\")\n\n  for _, w in ipairs(utils.array_shallow_clone(watchers)) do\n    w:destroy()\n  end\n\n  for _, e in pairs(events) do\n    e:destroy()\n  end\nend\n\n--- Windows NT will present directories that cannot be enumerated.\n--- Detect these by attempting to start an event monitor.\n---@param path string\n---@return boolean\nfunction M.is_fs_event_capable(path)\n  if not utils.is_windows then\n    return true\n  end\n\n  local fs_event = vim.loop.new_fs_event()\n  if not fs_event then\n    return false\n  end\n\n  if fs_event:start(path, FS_EVENT_FLAGS, function() end) ~= 0 then\n    return false\n  end\n\n  if fs_event:stop() ~= 0 then\n    return false\n  end\n\n  return true\nend\n\nfunction M.setup(opts)\n  M.config.filesystem_watchers = opts.filesystem_watchers\nend\n\nreturn M\n"
  },
  {
    "path": "lua/nvim-tree.lua",
    "content": "local api = require(\"nvim-tree.api\")\nlocal log = require(\"nvim-tree.log\")\nlocal view = require(\"nvim-tree.view\")\nlocal utils = require(\"nvim-tree.utils\")\nlocal actions = require(\"nvim-tree.actions\")\nlocal core = require(\"nvim-tree.core\")\nlocal notify = require(\"nvim-tree.notify\")\nlocal config = require(\"nvim-tree.config\")\n\nlocal M = {\n  init_root = \"\",\n}\n\n--- Helper function to execute some explorer method safely\n---@param fn string # key of explorer\n---@param ... any|nil\n---@return function|nil\nlocal function explorer_fn(fn, ...)\n  local explorer = core.get_explorer()\n  if explorer then\n    return explorer[fn](explorer, ...)\n  end\nend\n\n--- Update the tree root to a directory or the directory containing\n---@param path string relative or absolute\n---@param bufnr number|nil\nfunction M.change_root(path, bufnr)\n  -- skip if current file is in ignore_list\n  if type(bufnr) == \"number\" then\n    local ft\n\n    if vim.fn.has(\"nvim-0.10\") == 1 then\n      ft = vim.api.nvim_get_option_value(\"filetype\", { buf = bufnr }) or \"\"\n    else\n      ft = vim.api.nvim_buf_get_option(bufnr, \"filetype\") or \"\" ---@diagnostic disable-line: deprecated\n    end\n\n    for _, value in pairs(config.g.update_focused_file.update_root.ignore_list) do\n      if utils.str_find(path, value) or utils.str_find(ft, value) then\n        return\n      end\n    end\n  end\n\n  -- don't find inexistent\n  if vim.fn.filereadable(path) == 0 then\n    return\n  end\n\n  local cwd = core.get_cwd()\n  if cwd == nil then\n    return\n  end\n\n  local vim_cwd = vim.fn.getcwd()\n\n  -- test if in vim_cwd\n  if utils.path_relative(path, vim_cwd) ~= path then\n    if vim_cwd ~= cwd then\n      explorer_fn(\"change_dir\", vim_cwd)\n    end\n    return\n  end\n  -- test if in cwd\n  if utils.path_relative(path, cwd) ~= path then\n    return\n  end\n\n  -- otherwise test M.init_root\n  if config.g.prefer_startup_root and utils.path_relative(path, M.init_root) ~= path then\n    explorer_fn(\"change_dir\", M.init_root)\n    return\n  end\n  -- otherwise root_dirs\n  for _, dir in pairs(config.g.root_dirs) do\n    dir = vim.fn.fnamemodify(dir, \":p\")\n    if utils.path_relative(path, dir) ~= path then\n      explorer_fn(\"change_dir\", dir)\n      return\n    end\n  end\n  -- finally fall back to the folder containing the file\n  explorer_fn(\"change_dir\", vim.fn.fnamemodify(path, \":p:h\"))\nend\n\nfunction M.tab_enter()\n  if view.is_visible({ any_tabpage = true }) then\n    local bufname = vim.api.nvim_buf_get_name(0)\n\n    local ft\n    if vim.fn.has(\"nvim-0.10\") == 1 then\n      ft = vim.api.nvim_get_option_value(\"filetype\", { buf = 0 }) or \"\"\n    else\n      ft = vim.api.nvim_buf_get_option(0, \"ft\") ---@diagnostic disable-line: deprecated\n    end\n\n    for _, filter in ipairs(config.g.tab.sync.ignore) do\n      if bufname:match(filter) ~= nil or ft:match(filter) ~= nil then\n        return\n      end\n    end\n    view.open({ focus_tree = false })\n\n    local explorer = core.get_explorer()\n    if explorer then\n      explorer.renderer:draw()\n    end\n  end\nend\n\nfunction M.open_on_directory()\n  local should_proceed = config.g.hijack_directories.auto_open or view.is_visible()\n  if not should_proceed then\n    return\n  end\n\n  local buf = vim.api.nvim_get_current_buf()\n  local bufname = vim.api.nvim_buf_get_name(buf)\n  if vim.fn.isdirectory(bufname) ~= 1 then\n    return\n  end\n\n\n  local explorer = core.get_explorer()\n  if not explorer then\n    core.init(bufname)\n  end\n\n  explorer_fn(\"force_dirchange\", bufname, true, false)\nend\n\nlocal function manage_netrw()\n  if config.g.hijack_netrw then\n    vim.cmd(\"silent! autocmd! FileExplorer *\")\n    vim.cmd(\"autocmd VimEnter * ++once silent! autocmd! FileExplorer *\")\n  end\n  if config.g.disable_netrw then\n    vim.g.loaded_netrw = 1\n    vim.g.loaded_netrwPlugin = 1\n  end\nend\n\nlocal function setup_autocommands()\n  local augroup_id = vim.api.nvim_create_augroup(\"NvimTree\", { clear = true })\n  local function create_nvim_tree_autocmd(name, custom_opts)\n    local default_opts = { group = augroup_id }\n    vim.api.nvim_create_autocmd(name, vim.tbl_extend(\"force\", default_opts, custom_opts))\n  end\n\n  -- prevent new opened file from opening in the same window as nvim-tree\n  create_nvim_tree_autocmd(\"BufWipeout\", {\n    pattern = \"NvimTree_*\",\n    callback = function()\n      if not utils.is_nvim_tree_buf(0) then\n        return\n      end\n      if config.g.actions.open_file.eject then\n        view._prevent_buffer_override()\n      else\n        view.abandon_current_window()\n      end\n    end,\n  })\n\n  if config.g.tab.sync.open then\n    create_nvim_tree_autocmd(\"TabEnter\", { callback = vim.schedule_wrap(M.tab_enter) })\n  end\n  if config.g.sync_root_with_cwd then\n    create_nvim_tree_autocmd(\"DirChanged\", {\n      callback = function()\n        actions.tree.change_dir.fn(vim.loop.cwd())\n      end,\n    })\n  end\n  if config.g.update_focused_file.enable then\n    create_nvim_tree_autocmd(\"BufEnter\", {\n      callback = function(event)\n        local exclude = config.g.update_focused_file.exclude\n        if type(exclude) == \"function\" and exclude(event) then\n          return\n        end\n        utils.debounce(\"BufEnter:find_file\", config.g.view.debounce_delay, function()\n          actions.tree.find_file.fn()\n        end)\n      end,\n    })\n  end\n\n  if config.g.hijack_directories.enable and (config.g.disable_netrw or config.g.hijack_netrw) then\n    create_nvim_tree_autocmd({ \"BufEnter\", \"BufNewFile\" }, { callback = M.open_on_directory, nested = true })\n  end\n\n  if config.g.view.centralize_selection then\n    create_nvim_tree_autocmd(\"BufEnter\", {\n      pattern = \"NvimTree_*\",\n      callback = function()\n        vim.schedule(function()\n          vim.api.nvim_buf_call(0, function()\n            local is_term_mode = vim.api.nvim_get_mode().mode == \"t\"\n            if is_term_mode then\n              return\n            end\n            vim.cmd([[norm! zz]])\n          end)\n        end)\n      end,\n    })\n  end\n\n  if config.g.diagnostics.enable then\n    create_nvim_tree_autocmd(\"DiagnosticChanged\", {\n      callback = function(ev)\n        log.line(\"diagnostics\", \"DiagnosticChanged\")\n        require(\"nvim-tree.diagnostics\").update_lsp(ev)\n      end,\n    })\n    create_nvim_tree_autocmd(\"User\", {\n      pattern = \"CocDiagnosticChange\",\n      callback = function()\n        log.line(\"diagnostics\", \"CocDiagnosticChange\")\n        require(\"nvim-tree.diagnostics\").update_coc()\n      end,\n    })\n  end\n\n  if config.g.view.float.enable and config.g.view.float.quit_on_focus_loss then\n    create_nvim_tree_autocmd(\"WinLeave\", {\n      pattern = \"NvimTree_*\",\n      callback = function()\n        if utils.is_nvim_tree_buf(0) then\n          view.close()\n        end\n      end,\n    })\n  end\n\n  -- Handles event dispatch when tree is closed by `:q`\n  create_nvim_tree_autocmd(\"WinClosed\", {\n    pattern = \"*\",\n    ---@param ev vim.api.keyset.create_autocmd.callback_args\n    callback = function(ev)\n      if not vim.api.nvim_buf_is_valid(ev.buf) then\n        return\n      end\n      if vim.api.nvim_get_option_value(\"filetype\", { buf = ev.buf }) == \"NvimTree\" then\n        require(\"nvim-tree.events\")._dispatch_on_tree_close()\n      end\n    end,\n  })\nend\n\nfunction M.purge_all_state()\n  view.close_all_tabs()\n  view.abandon_all_windows()\n  local explorer = core.get_explorer()\n  if explorer then\n    require(\"nvim-tree.git\").purge_state()\n    explorer:destroy()\n    core.reset_explorer()\n  end\n  -- purge orphaned that were not destroyed by their nodes\n  require(\"nvim-tree.watcher\").purge_watchers()\nend\n\n---@param config_user? nvim_tree.config user supplied subset of config\nfunction M.setup(config_user)\n  if vim.fn.has(\"nvim-0.9\") == 0 then\n    notify.warn(\"nvim-tree.lua requires Neovim 0.9 or higher\")\n    return\n  end\n\n  M.init_root = vim.fn.getcwd()\n\n  config.setup(config_user)\n\n  manage_netrw()\n\n  require(\"nvim-tree.notify\").setup(config.g)\n  require(\"nvim-tree.log\").setup(config.g)\n\n  if log.enabled(\"config\") then\n    log.line(\"config\", \"default config + user\")\n    log.raw(\"config\", \"%s\\n\", vim.inspect(config.g))\n  end\n\n  require(\"nvim-tree.actions\").setup(config.g)\n  require(\"nvim-tree.keymap\").setup(config.g)\n  require(\"nvim-tree.appearance\").setup()\n  require(\"nvim-tree.diagnostics\").setup(config.g)\n  require(\"nvim-tree.explorer\"):setup(config.g)\n  require(\"nvim-tree.explorer.watch\").setup(config.g)\n  require(\"nvim-tree.git\").setup(config.g)\n  require(\"nvim-tree.git.utils\").setup(config.g)\n  require(\"nvim-tree.view\").setup(config.g)\n  require(\"nvim-tree.lib\").setup(config.g)\n  require(\"nvim-tree.renderer.components\").setup(config.g)\n  require(\"nvim-tree.buffers\").setup(config.g)\n  require(\"nvim-tree.help\").setup(config.g)\n  require(\"nvim-tree.watcher\").setup(config.g)\n\n  setup_autocommands()\n\n  if vim.g.NvimTreeSetup == 1 then\n    -- subsequent calls to setup\n    M.purge_all_state()\n  end\n\n  vim.g.NvimTreeSetup = 1\n  vim.api.nvim_exec_autocmds(\"User\", { pattern = \"NvimTreeSetup\" })\n\n  require(\"nvim-tree.api.impl\").hydrate_post_setup(api)\nend\n\nvim.g.NvimTreeRequired = 1\nvim.api.nvim_exec_autocmds(\"User\", { pattern = \"NvimTreeRequired\" })\n\nreturn M\n"
  },
  {
    "path": "plugin/nvim-tree.lua",
    "content": "require(\"nvim-tree.commands\").setup()\n"
  },
  {
    "path": "release-please-config.json",
    "content": "{\n  \"$schema\": \"https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json\",\n  \"include-v-in-tag\": true,\n  \"bootstrap-sha\": \"34780aca5bac0a58c163ea30719a276fead1bd95\",\n  \"packages\": {\n    \".\": {\n      \"package-name\": \"nvim-tree\",\n      \"release-type\": \"simple\"\n    }\n  }\n}\n"
  },
  {
    "path": "scripts/doc-comments.sh",
    "content": "#!/usr/bin/env bash\n\nout=$(grep -nr \"^--- @\" lua)\n\nif [ \"$out\" ]; then\n\tlast_file=\"\"\n\twhile read -r line; do\n\t\tfile=\"$(echo \"$line\" | cut -d: -f1)\"\n\t\tif [[ \"$file\" != \"$last_file\" ]]; then\n\t\t\techo \"$file:\" >&2\n\t\t\tlast_file=\"$file\"\n\t\tfi\n\t\techo \"$line\" | awk -F: '{ printf(\"  line %s: %s\\n\", $2, $3) }' >&2\n\tdone <<< \"$out\"\n\texit 1\nfi\n"
  },
  {
    "path": "scripts/help-defaults.sh",
    "content": "#!/usr/bin/env sh\n\n# run after changing default config or keymap.lua M.on_attach_default\n# scrapes and updates nvim-tree-lua.txt\n# run from repository root: scripts/help-defaults.sh  OR  make help-update\n\nset -e\n\n#\n# Operate on a temporary file as sed -i writes the file thousands of times.\n#\nWIP=\"/tmp/nvim-tree-lua.txt\"\ncp \"doc/nvim-tree-lua.txt\" \"${WIP}\"\n\n\n#\n# Inject default config\n#\nbegin=\"config-default-start\"\nend=\"config-default-end\"\ninject=\"config-default-injection-placeholder\"\n\n# scrape config.default, indented at 2\nsed -n -E \"/${begin}/,/${end}/{ /${begin}/d; /${end}/d; p; }\" lua/nvim-tree/config.lua > /tmp/config.default.2.lua\n\n# indent to match help\nsed -E \"s/^  /      /\" /tmp/config.default.2.lua > /tmp/config.default.6.lua\n\n# inject then remove the placeholder\nsed -i -E \"/${inject}/r /tmp/config.default.6.lua\" \"${WIP}\"\nsed -i -E \"/${inject}/d\" \"${WIP}\"\n\n#\n# Inject default mappings\n#\n\nbegin=\"BEGIN_ON_ATTACH_DEFAULT\"\nend=\"END_ON_ATTACH_DEFAULT\"\n\n# scrape ON_ATTACH_DEFAULT, indented at 2\nsed -n -E \"/${begin}/,/${end}/{ /${begin}/d; /${end}/d; p; }\" lua/nvim-tree/keymap.lua > /tmp/ON_ATTACH_DEFAULT.lua\n\n# help lua\nsed -i -E \"/${begin}/,/${end}/{ /${begin}/{p; r /tmp/ON_ATTACH_DEFAULT.lua\n           }; /${end}/p; d; }\" \"${WIP}\"\n\n# help human\n# extract mode, lhs, api, desc; handle both \"n\" and {\"n\", \"x\"} mode forms\necho > /tmp/ON_ATTACH_DEFAULT.help\nsed -E '\n  s/^ *vim\\.keymap\\.set\\(\\{([^}]+)\\}, *\"([^\"]+)\",.*api(.*),.*opts\\(\"([^\"]*)\".*/\\1 \\2 \\3 \\4/\n  t reformat\n  s/^ *vim\\.keymap\\.set\\(\"(.)\", *\"([^\"]+)\",.*api(.*),.*opts\\(\"([^\"]*)\".*/\\1 \\2 \\3 \\4/\n  t reformat\n  d\n  :reformat\n  s/\"//g\n  s/, //g\n' /tmp/ON_ATTACH_DEFAULT.lua | while read -r mode lhs apipath desc\ndo\n  printf ' %-17.17s %-4.4s %-26.26s %s\\n' \"\\`${lhs}\\`\" \"${mode}\" \"${desc}\" \"|nvim_tree.api${apipath}()|\" >> /tmp/ON_ATTACH_DEFAULT.help\ndone\necho >> /tmp/ON_ATTACH_DEFAULT.help\nbegin=\"Show the mappings:\"\nend=\"======\"\nsed -i -E \"/${begin}/,/${end}/{ /${begin}/{p; r /tmp/ON_ATTACH_DEFAULT.help\n           }; /${end}/p; d; }\" \"${WIP}\"\n\n#\n# complete\n#\nmv \"${WIP}\" \"doc/nvim-tree-lua.txt\"\n"
  },
  {
    "path": "scripts/luals-check.sh",
    "content": "#!/usr/bin/env sh\n\n# Performs a lua-language-server check on all lua files.\n# $VIMRUNTIME specifies neovim runtime path, defaults to \"/usr/share/nvim/runtime\" if unset.\n#\n# Call with codestyle-check param to enable only codestyle-check\n#\n# lua-language-server is inconsisent about which parameters must be absolute paths therefore we pass every path as absolute\n\nif [ $# -eq 1 ] && [ \"${1}\" != \"codestyle-check\" ] || [ $# -gt 1 ] ; then\n\techo \"usage: ${0} [codestyle-check]\" 1>&2\n\texit 1\nfi\n\nDIR_NVT=\"${PWD}\"\n\nif [ ! -f \"${DIR_NVT}/scripts/luals-check.sh\" ]; then\n\techo \"Must be run from nvim-tree root\" 1>&2\n\texit 1\nfi\n\nif [ -z \"${VIMRUNTIME}\" ]; then\n\texport VIMRUNTIME=\"/usr/share/nvim/runtime\"\n\techo \"Defaulting to VIMRUNTIME=${VIMRUNTIME}\"\nfi\n\nif [ ! -d \"${VIMRUNTIME}\" ]; then\n\techo \"\\$VIMRUNTIME=${VIMRUNTIME} not found\" 1>&2\n\texit 1\nfi\n\nDIR_OUT=\"${DIR_NVT}/luals-out\"\nLUARC=\"${DIR_OUT}/luarc.json\"\nRC=0\n\n# clear previous output\nrm -rf \"${DIR_OUT}\"\nmkdir \"${DIR_OUT}\"\n\n# create the luarc.json for the requested check\ncase \"${1}\" in\n\t\"codestyle-check\")\n\t\tjq \\\n\t\t\t'.diagnostics.neededFileStatus[] = \"None\" | .diagnostics.neededFileStatus.\"codestyle-check\" = \"Any\"' \\\n\t\t\t\"${DIR_NVT}/.luarc.json\" > \"${LUARC}\"\n\n\t\t;;\n\t*)\n\t\tcp \"${DIR_NVT}/.luarc.json\" \"${LUARC}\"\n\t\t;;\nesac\n\nfor SRC in lua scripts; do\n\tDIR_SRC=\"${DIR_NVT}/${SRC}\"\n\tFILE_OUT=\"${DIR_OUT}/out.${SRC}.log\"\n\techo \"Checking ${SRC}/\"\n\n\tlua-language-server --check=\"${DIR_SRC}\" --configpath=\"${LUARC}\" --checklevel=Information --logpath=\"${DIR_OUT}\" --loglevel=error 2>&1 | tee \"${FILE_OUT}\"\n\n\tif ! grep --quiet \"Diagnosis completed, no problems found\" \"${FILE_OUT}\"; then\n\t\tRC=1\n\tfi\ndone\n\nexit \"${RC}\"\n"
  },
  {
    "path": "scripts/setup-hooks.sh",
    "content": "#!/usr/bin/env bash\n\nln -sf ../../.hooks/pre-commit.sh .git/hooks/pre-commit\n"
  },
  {
    "path": "scripts/vimdoc.sh",
    "content": "#!/usr/bin/env sh\n\n# Wrapper around Nvim make targets:\n#\n# make doc - gen_vimdoc.lua\n#   Generates doc/nvim-tree-lua.txt \n#   Uses nvim-tree sources defined in scripts/vimdoc_config.lua\n#   Shims above into src/gen/gen_vimdoc.lua, replacing Nvim's config.\n#\n# make lintdoc - lintdoc.lua\n#   Validates doc/nvim-tree-lua.txt \n#   Desired:\n#   - tags valid\n#   - links valid\n#   Also:\n#   - brand spelling, notably Nvim and Lua\n#\n# There are some hardcoded expectations which we work around as commented.\n\nset -e\n\nif [ $# -ne 1 ] || [ \"${1}\" != \"doc\" ] && [ \"${1}\" != \"lintdoc\" ]; then\n\techo \"usage: ${0} <doc|lintdoc>\" 1>&2\n\texit 1\nfi\n\nDIR_NVIM_SRC_DEF=\"/tmp/src/neovim-stable\"\n\nif [ ! -d \"lua/nvim-tree\" ]; then\n\techo \"Must be run from nvim-tree root\" 1>&2\n\texit 1\nfi\n\nif [ -z \"${DIR_NVIM_SRC}\" ] && [ -d \"${DIR_NVIM_SRC_DEF}\" ]; then\n\texport DIR_NVIM_SRC=\"${DIR_NVIM_SRC_DEF}\"\nfi\n\nif [ ! -d \"${DIR_NVIM_SRC}\" ]; then\n\tcat << EOM\n\nNvim source v0.11+ is required to run ${0}\n\nUnavailable: ${DIR_NVIM_SRC_DEF} or \\$DIR_NVIM_SRC=${DIR_NVIM_SRC}\n\nPlease:\n  mkdir -p ${DIR_NVIM_SRC_DEF}\n  curl -L 'https://github.com/neovim/neovim/archive/refs/tags/stable.tar.gz' | tar zx --directory $(dirname \"${DIR_NVIM_SRC_DEF}\")\n\tor use your own e.g.\n  export DIR_NVIM_SRC=\"\\${HOME}/src/neovim\"\n\nEOM\nexit 1\nfi\n\ncleanup() {\n\t# remove source link\n\trm -fv \"${DIR_NVIM_SRC}/runtime/lua/nvim_tree\"\n\n\t# remove our config\n\trm -fv \"${DIR_NVIM_SRC}/src/gen/vimdoc_config.lua\"\n\trm -fv \"${DIR_NVIM_SRC}/runtime/lua/placeholder.lua\"\n\n\t# remove generated help\n\trm -fv \"${DIR_NVIM_SRC}/runtime/doc/nvim-tree-lua.txt\"\n\n\t# revert generator if present\n\tif [ -f \"${DIR_NVIM_SRC}/src/gen/gen_vimdoc.lua.org\" ]; then\n\t\tmv -v \"${DIR_NVIM_SRC}/src/gen/gen_vimdoc.lua.org\" \"${DIR_NVIM_SRC}/src/gen/gen_vimdoc.lua\"\n\tfi\n}\n\n# clean up any previous failed runs\ncleanup\n\n# runtime/doc is hardcoded, copy the help in\ncp -v \"doc/nvim-tree-lua.txt\" \"${DIR_NVIM_SRC}/runtime/doc\"\n\n# setup doc generation\nif [ \"${1}\" = \"doc\" ]; then\n\t# runtime/lua is available, link our sources in there\n\t# gen_vimdoc.lua doesn't like dashes in lua module names\n\t#   -> use nvim_tree instead of nvim-tree\n\tln -sv \"${PWD}/lua/nvim-tree\" \"${DIR_NVIM_SRC}/runtime/lua/nvim_tree\"\n\n\t# modify gen_vimdoc.lua to use our config, backing up original\n\tcp \"${DIR_NVIM_SRC}/src/gen/gen_vimdoc.lua\" \"${DIR_NVIM_SRC}/src/gen/gen_vimdoc.lua.org\"\n\tsed -i -E 's/spairs\\(config\\)/spairs\\(require(\"gen.vimdoc_config\")\\)/g' \"${DIR_NVIM_SRC}/src/gen/gen_vimdoc.lua\"\n\n\t# leave a generic placeholder to bridge between nvim.gen_vimdoc.Config\n\techo \"---@brief placeholder\" > \"${DIR_NVIM_SRC}/runtime/lua/placeholder.lua\"\n\n\t# copy our config\n\tcp -v \"scripts/vimdoc_config.lua\" \"${DIR_NVIM_SRC}/src/gen\"\nfi\n\n# run from within Nvim source\ncd \"${DIR_NVIM_SRC}\"\nmake \"${1}\"\ncd -\n\n# copy the generated help out\ncp -v \"${DIR_NVIM_SRC}/runtime/doc/nvim-tree-lua.txt\" \"doc\"\n\n# cleanup as everything succeeded\ncleanup\n"
  },
  {
    "path": "scripts/vimdoc_config.lua",
    "content": "--nvim-tree configuration for Nvim's gen_vimdoc.lua\n--Returned config is injected into the above.\n--Execute with `make doc`, see scripts/vimdoc.sh for details.\n\n--gen_vimdoc keys by filename:   -- FIXME: Using f_base will confuse `_meta/protocol.lua` with `protocol.lua`\n--Hence we must ensure that filenames are unique within each nvim.gen_vimdoc.Config[]\n\n---@class (exact) Src\n---@field helptag string must be globally unique\n---@field section string arbitrary\n---@field path string relative to cwd\n\nlocal base = \"runtime/lua/nvim_tree/\"\nlocal placeholder = \"runtime/lua/placeholder.lua\"\n\n---@type Src[]\nlocal srcs_config = {\n  { helptag = \"nvim-tree-config\",                     section = \"Config\",                      path = base .. \"_meta/config.lua\", },\n\n  { helptag = \"nvim-tree-config-sort\",                section = \"Config: sort\",                path = base .. \"_meta/config/sort.lua\", },\n  { helptag = \"nvim-tree-config-view\",                section = \"Config: view\",                path = base .. \"_meta/config/view.lua\", },\n  { helptag = \"nvim-tree-config-renderer\",            section = \"Config: renderer\",            path = base .. \"_meta/config/renderer.lua\", },\n  { helptag = \"nvim-tree-config-hijack-directories\",  section = \"Config: hijack_directories\",  path = base .. \"_meta/config/hijack_directories.lua\", },\n  { helptag = \"nvim-tree-config-update-focused-file\", section = \"Config: update_focused_file\", path = base .. \"_meta/config/update_focused_file.lua\", },\n  { helptag = \"nvim-tree-config-system-open\",         section = \"Config: system_open\",         path = base .. \"_meta/config/system_open.lua\", },\n  { helptag = \"nvim-tree-config-git\",                 section = \"Config: git\",                 path = base .. \"_meta/config/git.lua\", },\n  { helptag = \"nvim-tree-config-diagnostics\",         section = \"Config: diagnostics\",         path = base .. \"_meta/config/diagnostics.lua\", },\n  { helptag = \"nvim-tree-config-modified\",            section = \"Config: modified\",            path = base .. \"_meta/config/modified.lua\", },\n  { helptag = \"nvim-tree-config-filters\",             section = \"Config: filters\",             path = base .. \"_meta/config/filters.lua\", },\n  { helptag = \"nvim-tree-config-live-filter\",         section = \"Config: live_filter\",         path = base .. \"_meta/config/live_filter.lua\", },\n  { helptag = \"nvim-tree-config-filesystem-watchers\", section = \"Config: filesystem_watchers\", path = base .. \"_meta/config/filesystem_watchers.lua\", },\n  { helptag = \"nvim-tree-config-actions\",             section = \"Config: actions\",             path = base .. \"_meta/config/actions.lua\", },\n  { helptag = \"nvim-tree-config-trash\",               section = \"Config: trash\",               path = base .. \"_meta/config/trash.lua\", },\n  { helptag = \"nvim-tree-config-tab\",                 section = \"Config: tab\",                 path = base .. \"_meta/config/tab.lua\", },\n  { helptag = \"nvim-tree-config-notify\",              section = \"Config: notify\",              path = base .. \"_meta/config/notify.lua\", },\n  { helptag = \"nvim-tree-config-bookmarks\",           section = \"Config: bookmarks\",           path = base .. \"_meta/config/bookmarks.lua\", },\n  { helptag = \"nvim-tree-config-help\",                section = \"Config: help\",                path = base .. \"_meta/config/help.lua\", },\n  { helptag = \"nvim-tree-config-ui\",                  section = \"Config: ui\",                  path = base .. \"_meta/config/ui.lua\", },\n  { helptag = \"nvim-tree-config-experimental\",        section = \"Config: experimental\",        path = base .. \"_meta/config/experimental.lua\", },\n  { helptag = \"nvim-tree-config-log\",                 section = \"Config: log\",                 path = base .. \"_meta/config/log.lua\", },\n\n  { helptag = \"nvim-tree-config-default\",             section = \"Config: Default\",             path = base .. \"_meta/config/default.lua\", },\n\n  { helptag = \"nvim-tree-api\",                        section = \"PLACEHOLDER\",                 path = placeholder, },\n}\n\n---@type Src[]\nlocal srcs_api = {\n  { helptag = \"nvim-tree-api\",            section = \"API\",             path = base .. \"api.lua\", },\n  { helptag = \"nvim-tree-api-appearance\", section = \"API: appearance\", path = base .. \"_meta/api/appearance.lua\", },\n  { helptag = \"nvim-tree-api-commands\",   section = \"API: commands\",   path = base .. \"_meta/api/commands.lua\", },\n  { helptag = \"nvim-tree-api-config\",     section = \"API: config\",     path = base .. \"_meta/api/config.lua\", },\n  { helptag = \"nvim-tree-api-events\",     section = \"API: events\",     path = base .. \"_meta/api/events.lua\", },\n  { helptag = \"nvim-tree-api-filter\",     section = \"API: filter\",     path = base .. \"_meta/api/filter.lua\", },\n  { helptag = \"nvim-tree-api-fs\",         section = \"API: fs\",         path = base .. \"_meta/api/fs.lua\", },\n  { helptag = \"nvim-tree-api-git\",        section = \"API: git\",        path = base .. \"_meta/api/git.lua\", },\n  { helptag = \"nvim-tree-api-map\",        section = \"API: map\",        path = base .. \"_meta/api/map.lua\", },\n  { helptag = \"nvim-tree-api-marks\",      section = \"API: marks\",      path = base .. \"_meta/api/marks.lua\", },\n  { helptag = \"nvim-tree-api-node\",       section = \"API: node\",       path = base .. \"_meta/api/node.lua\", },\n  { helptag = \"nvim-tree-api-tree\",       section = \"API: tree\",       path = base .. \"_meta/api/tree.lua\", },\n\n  { helptag = \"nvim-tree-class\",          section = \"PLACEHOLDER\",     path = placeholder, },\n}\n\n---@type Src[]\nlocal srcs_class = {\n  { helptag = \"nvim-tree-class\",                   section = \"Class: Class\",             path = base .. \"classic.lua\", },\n  { helptag = \"nvim-tree-class-decorator\",         section = \"Class: Decorator\",         path = base .. \"_meta/api/decorator.lua\", },\n  { helptag = \"nvim-tree-class-decorator-example\", section = \"Class: Decorator example\", path = base .. \"_meta/api/decorator_example.lua\", },\n}\n\n---Map paths to file names\n---File names are the unique key that gen_vimdoc.lua uses\n---@param srcs Src[]\n---@return string[] file names\nlocal function section_order(srcs)\n  return vim.tbl_map(function(src)\n    return vim.fn.fnamemodify(src.path, \":t\")\n  end, srcs)\nend\n\n---Extract paths\n---@param srcs Src[]\n---@return string[] file names\nlocal function files(srcs)\n  return vim.tbl_map(function(src)\n    return src.path\n  end, srcs)\nend\n\n---Find a Src or error.\n---Name is the (sometimes specifically hardcoded) mangled case filename with .lua stripped\n---@param name string\n---@param srcs Src[]\n---@return Src?\nlocal function src_by_name(name, srcs)\n  for _, s in ipairs(srcs) do\n    if s.path:match(name:lower() .. \".lua$\") then\n      return s\n    end\n  end\n  error(string.format(\"\\n\\nPath for lower, extension stripped file name='%s' not found in\\nsrcs=%s\\n\", name, vim.inspect(srcs)))\nend\n\n-- generator doesn't strip _meta\nlocal function normalise_module(fun)\n  fun.module = fun.module and fun.module:gsub(\"._meta\", \"\", 1) or nil\nend\n\n---HACK\n---Problem:\n--- Generator generates fields for a class' methods.\n--- This is a problem as method fields don't have a module and aren't transformed.\n--- Method field fun only contains: classvar, desc, name and (function) type\n---Solution:\n--- Collect a map of \"class:method\" to modules when the real method passes through fn_xform\n--- This works as the real method function is processed before the field method.\n---@type table<string, string>\nlocal modules_by_method = {}\n\n-- @type nvim.gen_vimdoc.Config[]\nreturn {\n  -- Config\n  {\n    filename = \"nvim-tree-lua.txt\",\n    section_order = section_order(srcs_config),\n    files = files(srcs_config),\n    section_fmt = function(name) return src_by_name(name, srcs_config).section end,\n    helptag_fmt = function(name) return src_by_name(name, srcs_config).helptag end,\n  },\n  -- API\n  {\n    filename = \"nvim-tree-lua.txt\",\n    section_order = section_order(srcs_api),\n    files = files(srcs_api),\n    section_fmt = function(name) return src_by_name(name, srcs_api).section end,\n    helptag_fmt = function(name) return src_by_name(name, srcs_api).helptag end,\n\n    fn_xform = function(fun)\n      if (fun.module) then\n        normalise_module(fun)\n\n        -- remove the module prefix from the left aligned function name\n        -- default fn_helptag_fmt adds it back to the help tag\n        local replaced\n        fun.name, replaced = fun.name:gsub(\"^\" .. fun.module .. \"%.\", \"\", 1)\n        if (replaced ~= 1) then\n          error(string.format(\"\\n\\nfun.name='%s' does not start with module\\nfun=%s\", fun.name, vim.inspect(fun)))\n        end\n      end\n    end,\n  },\n  -- Classes\n  {\n    filename = \"nvim-tree-lua.txt\",\n    section_order = section_order(srcs_class),\n    files = files(srcs_class),\n    section_fmt = function(name) return src_by_name(name, srcs_class).section end,\n    helptag_fmt = function(name) return src_by_name(name, srcs_class).helptag end,\n\n    fn_xform = function(fun)\n      if (fun.module) then\n        normalise_module(fun)\n\n        -- strip the class file from the module\n        fun.module = fun.module:gsub(\"%.[^%.]*$\", \"\", 1)\n\n        -- record the module for the method\n        modules_by_method[fun.classvar .. \":\" .. fun.name] = fun.module\n      end\n    end,\n\n    -- fn_helptag_fmt_common derived\n    --  module prepended to classes\n    --  module is fetched from modules_by_method when fun.module unavailable\n    fn_helptag_fmt = function(fun)\n      local fn_sfx = fun.table and \"\" or \"()\"\n      local module = fun.module or modules_by_method[fun.classvar .. \":\" .. fun.name]\n      return string.format(\"%s.%s:%s%s\", module, fun.classvar, fun.name, fn_sfx)\n    end,\n  },\n}\n"
  }
]