[
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\ninsert_final_newline = true\nindent_style = space\nindent_size = 2\ncharset = utf-8\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yml",
    "content": "name: Bug Report\ndescription: File a bug/issue\ntitle: \"bug: \"\nlabels: [bug]\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        **Before** reporting an issue, make sure to read the [documentation](https://github.com/folke/trouble.nvim)\n        and search [existing issues](https://github.com/folke/trouble.nvim/issues).\n\n        Usage questions such as ***\"How do I...?\"*** belong in [Discussions](https://github.com/folke/trouble.nvim/discussions) and will be closed.\n  - type: checkboxes\n    attributes:\n      label: Did you check docs and existing issues?\n      description: Make sure you checked all of the below before submitting an issue\n      options:\n        - label: I have read all the trouble.nvim docs\n          required: true\n        - label: I have updated the plugin to the latest version before submitting this issue\n          required: true\n        - label: I have searched the existing issues of trouble.nvim\n          required: true\n        - label: I have searched the existing issues of plugins related to this issue\n          required: true\n  - type: input\n    attributes:\n      label: \"Neovim version (nvim -v)\"\n      placeholder: \"0.8.0 commit db1b0ee3b30f\"\n    validations:\n      required: true\n  - type: input\n    attributes:\n      label: \"Operating system/version\"\n      placeholder: \"MacOS 11.5\"\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: Describe the bug\n      description: A clear and concise description of what the bug is. Please include any related errors you see in Neovim.\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: Steps To Reproduce\n      description: Steps to reproduce the behavior.\n      placeholder: |\n        1.\n        2. \n        3.\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: Expected Behavior\n      description: A concise description of what you expected to happen.\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: Repro\n      description: Minimal `init.lua` to reproduce this issue. Save as `repro.lua` and run with `nvim -u repro.lua`\n      value: |\n        vim.env.LAZY_STDPATH = \".repro\"\n        load(vim.fn.system(\"curl -s https://raw.githubusercontent.com/folke/lazy.nvim/main/bootstrap.lua\"))()\n\n        require(\"lazy.minit\").repro({\n          spec = {\n            { \"folke/trouble.nvim\", opts = {} },\n            -- add any other plugins here\n          },\n        })\n      render: lua\n    validations:\n      required: false\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\ncontact_links:\n  - name: Ask a question\n    url: https://github.com/folke/trouble.nvim/discussions\n    about: Use Github discussions instead\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.yml",
    "content": "name: Feature Request\ndescription: Suggest a new feature\ntitle: \"feature: \"\nlabels: [enhancement]\nbody:\n  - type: checkboxes\n    attributes:\n      label: Did you check the docs?\n      description: Make sure you read all the docs before submitting a feature request\n      options:\n        - label: I have read all the trouble.nvim docs\n          required: true\n  - type: textarea\n    validations:\n      required: true\n    attributes:\n      label: Is your feature request related to a problem? Please describe.\n      description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n  - type: textarea\n    validations:\n      required: true\n    attributes:\n      label: Describe the solution you'd like\n      description: A clear and concise description of what you want to happen.\n  - type: textarea\n    validations:\n      required: true\n    attributes:\n      label: Describe alternatives you've considered\n      description: A clear and concise description of any alternative solutions or features you've considered.\n  - type: textarea\n    validations:\n      required: false\n    attributes:\n      label: Additional context\n      description: Add any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "## Description\n\n<!-- Describe the big picture of your changes to communicate to the maintainers\n  why we should accept this pull request. -->\n\n## Related Issue(s)\n\n<!--\n  If this PR fixes any issues, please link to the issue here.\n  - Fixes #<issue_number>\n-->\n\n## Screenshots\n\n<!-- Add screenshots of the changes if applicable. -->\n\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: CI\n\non:\n  push:\n    branches: [main, master]\n  pull_request:\n\njobs:\n  ci:\n    uses: folke/github/.github/workflows/ci.yml@main\n    secrets: inherit\n    with:\n      plugin: trouble.nvim\n      repo: folke/trouble.nvim\n"
  },
  {
    "path": ".github/workflows/labeler.yml",
    "content": "name: \"PR Labeler\"\non:\n  - pull_request_target\n\njobs:\n  labeler:\n    uses: folke/github/.github/workflows/labeler.yml@main\n    secrets: inherit\n"
  },
  {
    "path": ".github/workflows/pr.yml",
    "content": "name: PR Title\n\non:\n  pull_request_target:\n    types:\n      - opened\n      - edited\n      - synchronize\n      - reopened\n      - ready_for_review\n\npermissions:\n  pull-requests: read\n\njobs:\n  pr-title:\n    uses: folke/github/.github/workflows/pr.yml@main\n    secrets: inherit\n"
  },
  {
    "path": ".github/workflows/stale.yml",
    "content": "name: Stale Issues & PRs\n\non:\n  schedule:\n    - cron: \"30 1 * * *\"\n\njobs:\n  stale:\n    if: contains(fromJSON('[\"folke\", \"LazyVim\"]'), github.repository_owner)\n    uses: folke/github/.github/workflows/stale.yml@main\n    secrets: inherit\n"
  },
  {
    "path": ".github/workflows/update.yml",
    "content": "name: Update Repo\n\non:\n  workflow_dispatch:\n  schedule:\n    # Run every hour\n    - cron: \"0 * * * *\"\n\njobs:\n  update:\n    if: contains(fromJSON('[\"folke\", \"LazyVim\"]'), github.repository_owner)\n    uses: folke/github/.github/workflows/update.yml@main\n    secrets: inherit\n"
  },
  {
    "path": ".gitignore",
    "content": "*.log\n/.repro\n/.tests\n/build\n/debug\n/doc/tags\nfoo.*\nnode_modules\ntt.*\n"
  },
  {
    "path": ".lua-format",
    "content": "# https://github.com/Koihik/LuaFormatter/blob/master/docs/Style-Config.md\n#column_limit: 100\n#indent_width: 4\n#continuation_indent_width: 4\n#use_tab: false\n#chop_down_parameter: true\n#chop_down_table: true\n#chop_down_kv_table: true\n#single_quote_to_double_quote: true\n#spaces_inside_table_braces: true\n#align_parameter: true\n#keep_simple_control_block_one_line: true\n#extra_sep_at_table_end: true\n"
  },
  {
    "path": ".markdownlint-cli2.yaml",
    "content": "config:\n  MD013: false\n  MD033: false\n"
  },
  {
    "path": ".neoconf.json",
    "content": "{\n  \"neodev\": {\n    \"library\": {\n      \"plugins\": [\n        \"plenary.nvim\",\n        \"nvim-web-devicons\",\n        \"telescope.nvim\",\n        \"lazy.nvim\"\n      ]\n    }\n  },\n  \"lspconfig\": {\n    \"lua_ls\": {\n      \"Lua.runtime.version\": \"LuaJIT\",\n      \"Lua.workspace.checkThirdParty\": false\n    }\n  }\n}\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\n\n## [3.7.1](https://github.com/folke/trouble.nvim/compare/v3.7.0...v3.7.1) (2025-01-28)\n\n\n### Bug Fixes\n\n* **lsp:** make_position_params with offset encoding. Closes [#606](https://github.com/folke/trouble.nvim/issues/606) ([6f380b8](https://github.com/folke/trouble.nvim/commit/6f380b8826fb819c752c8fd7daaee9ef96d4c689))\n\n## [3.7.0](https://github.com/folke/trouble.nvim/compare/v3.6.0...v3.7.0) (2025-01-15)\n\n\n### Features\n\n* **config:** add `close` counterparts to jump split actions ([#584](https://github.com/folke/trouble.nvim/issues/584)) ([928e6d0](https://github.com/folke/trouble.nvim/commit/928e6d01c83b87137a7baf7221fdd070aed3b313))\n* **preview:** allow sources to decorate the preview buffer/window ([affd249](https://github.com/folke/trouble.nvim/commit/affd249ab579c1380da8513b9f850463c6408e9b))\n* **sources:** added snacks picker source ([fa32f71](https://github.com/folke/trouble.nvim/commit/fa32f71be4d6c7c2cd4db5bf89cd836248c7bd67))\n\n\n### Bug Fixes\n\n* **format:** for \"attempt to index local 'signs' (a boolean value)\" errors in nvim-0.10.1+ ([#579](https://github.com/folke/trouble.nvim/issues/579)) ([2e7cb80](https://github.com/folke/trouble.nvim/commit/2e7cb80e2a4f64373228b78cb2080c423d771ef8))\n* **lsp:** always use actual symbol kind names. See [#568](https://github.com/folke/trouble.nvim/issues/568) ([11bcbc0](https://github.com/folke/trouble.nvim/commit/11bcbc0361420875b8bd803267cd532a350c398b))\n* **lsp:** handle invalid line positions ([1a2efaf](https://github.com/folke/trouble.nvim/commit/1a2efaf06d2966ffe3a1ef4a90d0bd8b9d870643))\n* **lsp:** use new vim.str_byteindex if available to calculate start positions of LSP ranges ([86746d2](https://github.com/folke/trouble.nvim/commit/86746d2b5890139a0270c6693ece219912fd73c0))\n* **lsp:** use old-style args for vim.str_byteindex. Fixes [#604](https://github.com/folke/trouble.nvim/issues/604) ([c633e85](https://github.com/folke/trouble.nvim/commit/c633e8559adf529b85167a4cb489d7358e9efb1a))\n* **snacks:** use filtered items instead of all ([2423cd2](https://github.com/folke/trouble.nvim/commit/2423cd20ae2faadec9edd7013617f7b80a3ae628))\n\n\n### Performance Improvements\n\n* **debug:** don't create obj dumps ([1fe80c7](https://github.com/folke/trouble.nvim/commit/1fe80c7cdf86d6a92ab83c0d1dac1cf8aff68b0d))\n* **treesitter:** set regions early ([#587](https://github.com/folke/trouble.nvim/issues/587)) ([20aa858](https://github.com/folke/trouble.nvim/commit/20aa858a86a09458c3851464eab0c5560b5249c0))\n\n## [3.6.0](https://github.com/folke/trouble.nvim/compare/v3.5.2...v3.6.0) (2024-07-21)\n\n\n### Features\n\n* allow disabling a key ([891e76d](https://github.com/folke/trouble.nvim/commit/891e76df4628d5bb3ad41edb4269592c19b35537))\n\n\n### Bug Fixes\n\n* **text:** skip treesitter when buf is no longer valid. Fixes [#556](https://github.com/folke/trouble.nvim/issues/556) ([05694b4](https://github.com/folke/trouble.nvim/commit/05694b4e7d67fe1c46503e92a7b812fa58d92702))\n\n## [3.5.2](https://github.com/folke/trouble.nvim/compare/v3.5.1...v3.5.2) (2024-07-19)\n\n\n### Bug Fixes\n\n* **util:** concealcursor ([e01c99e](https://github.com/folke/trouble.nvim/commit/e01c99eb36c93c77e8985ce9615a75bb73c8c7cf))\n\n\n### Performance Improvements\n\n* **treesitter:** incremental parsing for highlighter ([85154ce](https://github.com/folke/trouble.nvim/commit/85154cedf9b5bf64e56046d493cad7afc3416621))\n\n## [3.5.1](https://github.com/folke/trouble.nvim/compare/v3.5.0...v3.5.1) (2024-07-04)\n\n\n### Bug Fixes\n\n* **command:** weird issue with number keys. no idea when this happens. oh well... Fixes [#528](https://github.com/folke/trouble.nvim/issues/528) ([95568c6](https://github.com/folke/trouble.nvim/commit/95568c61416ff3dea2b6177deeb8a51130d6fd7a))\n* **diagnostics:** ruff generates a `vim.NIL` diag code. Closes [#527](https://github.com/folke/trouble.nvim/issues/527) ([edd9684](https://github.com/folke/trouble.nvim/commit/edd9684089b19684d5dad90bd5fcfceb48719212))\n* **lsp:** use caller text for call locations in incoming lsp calls. Fixes [#529](https://github.com/folke/trouble.nvim/issues/529) ([12dc19a](https://github.com/folke/trouble.nvim/commit/12dc19a8aba6f964fc6c4060649782a2e57de0cf))\n\n## [3.5.0](https://github.com/folke/trouble.nvim/compare/v3.4.3...v3.5.0) (2024-07-04)\n\n\n### Features\n\n* added explicit support for mini.icons ([42dcb58](https://github.com/folke/trouble.nvim/commit/42dcb58e95723f833135d5cf406c38bd54304389))\n\n\n### Bug Fixes\n\n* **telescope:** item path. Fixes [#521](https://github.com/folke/trouble.nvim/issues/521) ([6e19371](https://github.com/folke/trouble.nvim/commit/6e1937138b2c292ac0d3e8d9bfc36a29a515a380))\n* **telescope:** use (lnum, 0) for telescope item without col ([#524](https://github.com/folke/trouble.nvim/issues/524)) ([25204b7](https://github.com/folke/trouble.nvim/commit/25204b7e134005dfcb694a0b6d227c98ce3ad164))\n\n## [3.4.3](https://github.com/folke/trouble.nvim/compare/v3.4.2...v3.4.3) (2024-06-23)\n\n\n### Bug Fixes\n\n* **item:** empty filenames ([77f17d1](https://github.com/folke/trouble.nvim/commit/77f17d1bb29b32e06f75afa5c4fe0eba6f5ab397))\n* **promise:** vim.loop. Fixes [#513](https://github.com/folke/trouble.nvim/issues/513) ([1acfb6c](https://github.com/folke/trouble.nvim/commit/1acfb6c45c38f07f4d0a5e4cbdd60c9bb6880908))\n* **util:** crlf. Fixes [#518](https://github.com/folke/trouble.nvim/issues/518) ([032fa2c](https://github.com/folke/trouble.nvim/commit/032fa2c36a7c8738eb1e1d2f52a433be085f603a))\n* **utils:** use `vim.loop or vim.ev` declared in the beginning of a file ([#519](https://github.com/folke/trouble.nvim/issues/519)) ([235dc61](https://github.com/folke/trouble.nvim/commit/235dc61cf49b61e7970897c3eed51b1b30121b9e))\n\n## [3.4.2](https://github.com/folke/trouble.nvim/compare/v3.4.1...v3.4.2) (2024-06-14)\n\n\n### Bug Fixes\n\n* correct invalid float positions. Fixes [#502](https://github.com/folke/trouble.nvim/issues/502) ([88a40f1](https://github.com/folke/trouble.nvim/commit/88a40f1cc3af846b520ae167f0177b5faa148c86))\n* **diagnostics:** custom format for code. Fixes [#508](https://github.com/folke/trouble.nvim/issues/508) ([ada78fa](https://github.com/folke/trouble.nvim/commit/ada78fae41fc05d52883f19fb5e22d5a61e0ef08))\n* fixup ([60b0ac3](https://github.com/folke/trouble.nvim/commit/60b0ac3772e991bc194207afc28368a5f15d913a))\n* **highlights:** link TroubleBasename to TroubleFilename. Fixes [#507](https://github.com/folke/trouble.nvim/issues/507) ([276e7b7](https://github.com/folke/trouble.nvim/commit/276e7b7a8764cd59de5c8a588771a54a979ab3c3))\n* **main:** handle windows with changed buffers ([286c044](https://github.com/folke/trouble.nvim/commit/286c04474cbb24894d233e6b0c00f1e6c8d2ae54))\n* **view:** dont go to main when not in the trouble window when closing ([8d5e05c](https://github.com/folke/trouble.nvim/commit/8d5e05c0d0ce7a2c630ce92cb3cc923044848063))\n\n## [3.4.1](https://github.com/folke/trouble.nvim/compare/v3.4.0...v3.4.1) (2024-06-12)\n\n\n### Bug Fixes\n\n* **fzf:** added descriptions ([5e45bb7](https://github.com/folke/trouble.nvim/commit/5e45bb78f8da3444d35616934c180fce3742c439))\n\n## [3.4.0](https://github.com/folke/trouble.nvim/compare/v3.3.0...v3.4.0) (2024-06-11)\n\n\n### Features\n\n* added fzf-lua integration ([d14323f](https://github.com/folke/trouble.nvim/commit/d14323fe3461b89e91fb569148b44731655ae196))\n* **fzf-lua:** added smart open/add that will use selection or all when nothing selected. ([bed3c5b](https://github.com/folke/trouble.nvim/commit/bed3c5b79298d94d4981d86ed699c70f58ceccff))\n\n\n### Bug Fixes\n\n* **fzf-lua:** smart-open on windows ([4d0f045](https://github.com/folke/trouble.nvim/commit/4d0f0454ae2a246ec3e0ff541a347164dac23b7b))\n* initialize `auto_open`. Fixes [#489](https://github.com/folke/trouble.nvim/issues/489) ([0793267](https://github.com/folke/trouble.nvim/commit/0793267d3d4b782e46161931b7cbaaf062a892d7))\n* **spec:** properly process actions. Fixes [#494](https://github.com/folke/trouble.nvim/issues/494) ([3082f4b](https://github.com/folke/trouble.nvim/commit/3082f4b10fe9f0a8aa922065b998bc37115c4bef))\n* **telescope:** autmatically select telescope_files mode if list are files without locations. Fixes [#466](https://github.com/folke/trouble.nvim/issues/466) ([1ad6b14](https://github.com/folke/trouble.nvim/commit/1ad6b141316f90a658c6d654516092d43e3e596c))\n* **telescope:** set end_pos to end of word ([4deb811](https://github.com/folke/trouble.nvim/commit/4deb8111e7ffa48a4a27bad1ecdfb7779f4efb7d))\n* **views:** pending should be considered open. Fixes [#492](https://github.com/folke/trouble.nvim/issues/492) ([57b50a6](https://github.com/folke/trouble.nvim/commit/57b50a6dc129f3a82c3bdd9f81b9f2d4e770ac09))\n\n## [3.3.0](https://github.com/folke/trouble.nvim/compare/v3.2.0...v3.3.0) (2024-06-07)\n\n\n### Features\n\n* **lsp:** most lsp sources now support `params.include_current`. Fixes [#482](https://github.com/folke/trouble.nvim/issues/482) ([29d19d4](https://github.com/folke/trouble.nvim/commit/29d19d4f2102306176578f1fe537fbd9740b19e1))\n* **window:** more options for mapping keys ([fdcfc5a](https://github.com/folke/trouble.nvim/commit/fdcfc5a200491e9509e56e04c6b3cdee8ada3153))\n* you can now use `dd` and `d` to delete items in the trouble list. Fixes [#149](https://github.com/folke/trouble.nvim/issues/149). Fixes [#347](https://github.com/folke/trouble.nvim/issues/347) ([e879302](https://github.com/folke/trouble.nvim/commit/e879302d003bf5bda746a36365431d4a72cf3226))\n\n\n### Bug Fixes\n\n* **api:** only refresh on open if there's no action. Fixes [#488](https://github.com/folke/trouble.nvim/issues/488) ([2661f46](https://github.com/folke/trouble.nvim/commit/2661f4612209cbbc1106fb9537666ea0133e4859))\n* **preview:** fixed mouse clicks in the preview main window. Fixes [#484](https://github.com/folke/trouble.nvim/issues/484) ([98d9ed7](https://github.com/folke/trouble.nvim/commit/98d9ed74aec4e82171de3ae0541cdd078558e546))\n* **telescope:** show error when use tries to add when telescope picker does not exist ([c11dc27](https://github.com/folke/trouble.nvim/commit/c11dc2777d52da2c8da25836817e43608ec951a5))\n* use vim.loop for nvim 0.9 in view/init.lua ([#487](https://github.com/folke/trouble.nvim/issues/487)) ([791278e](https://github.com/folke/trouble.nvim/commit/791278e498e1147520e4214982767f77ca4a99df))\n* **view:** when calling open when the view is already open, do a refresh. See [#485](https://github.com/folke/trouble.nvim/issues/485) ([39595e8](https://github.com/folke/trouble.nvim/commit/39595e883e2f91456413ca4df287575d31665940))\n\n## [3.2.0](https://github.com/folke/trouble.nvim/compare/v3.1.0...v3.2.0) (2024-06-06)\n\n\n### Features\n\n* **lsp:** add incoming/outgoing calls to lsp mode ([8adafc1](https://github.com/folke/trouble.nvim/commit/8adafc14d8fe2a4471a0311ff72927250390d7bd))\n* **lsp:** added support for showing locations from lsp execute commands ([b1d16ac](https://github.com/folke/trouble.nvim/commit/b1d16ac02d787e40165130e0cd09474ce639b175))\n* promise class ([84f0c6d](https://github.com/folke/trouble.nvim/commit/84f0c6d047dbf182622f3d89bc47ec4a70c900b2))\n\n\n### Bug Fixes\n\n* **api:** show error when an invalid mode was used. Fixes [#465](https://github.com/folke/trouble.nvim/issues/465) ([4b1914c](https://github.com/folke/trouble.nvim/commit/4b1914c5cdbf7be18fee797c410df2faa2be13f2))\n* **format:** pos format. See [#472](https://github.com/folke/trouble.nvim/issues/472) ([abdfa1d](https://github.com/folke/trouble.nvim/commit/abdfa1daeb9713470a9b61676a82f24f32e31900))\n* **lsp:** check for nil on faulty lsp results ([d7f69ff](https://github.com/folke/trouble.nvim/commit/d7f69ff5638cf1864cabac54ade1b1694adfe085))\n* **lsp:** dont process nil results ([06a4892](https://github.com/folke/trouble.nvim/commit/06a48922e83b114a78c63ec770819b4afacd2166))\n* **lsp:** send request only to needed clients ([c147a75](https://github.com/folke/trouble.nvim/commit/c147a75c421b2df6986d82f61657ccec2f302091))\n* **lsp:** use document uri of document symbols don't have an uri set. Fixes [#480](https://github.com/folke/trouble.nvim/issues/480) ([358f0ee](https://github.com/folke/trouble.nvim/commit/358f0ee6ce4c379a3b0c37bb04ab6587c86e285a))\n* **preview:** hide winbar when previewing in main. Fixes [#464](https://github.com/folke/trouble.nvim/issues/464) ([250ea79](https://github.com/folke/trouble.nvim/commit/250ea79c810a3e5fff846c788792441f1c795c92))\n* **preview:** respect fold settings. Fixes [#459](https://github.com/folke/trouble.nvim/issues/459) ([29d1bb8](https://github.com/folke/trouble.nvim/commit/29d1bb81adc847e89ddbbf5b11ff0079daf7cc0a))\n* **preview:** set correct extmark priorities in preview highlight. Fixes [#476](https://github.com/folke/trouble.nvim/issues/476) ([13ad959](https://github.com/folke/trouble.nvim/commit/13ad95902cf479b0fa091a77368af0e03b486fe3))\n* **view:** correctly set folding options to wo. See [#477](https://github.com/folke/trouble.nvim/issues/477) ([9151797](https://github.com/folke/trouble.nvim/commit/915179759c9459b69faae90a38da6fc1ca6b90d7))\n* **view:** ensure fold settings are correct for the trouble views. See [#477](https://github.com/folke/trouble.nvim/issues/477) ([b5181b6](https://github.com/folke/trouble.nvim/commit/b5181b65912c704d5378f8fe6889924f0182c357))\n* **view:** execute actions on first render ([97bfb74](https://github.com/folke/trouble.nvim/commit/97bfb74826476b26634b5321c5d8dfbc46e41497))\n* **window:** account for winbar for preview in main. Fixes [#468](https://github.com/folke/trouble.nvim/issues/468) ([23ded52](https://github.com/folke/trouble.nvim/commit/23ded52593d017fd7d6042215460419801e35481))\n* **window:** set default winblend=0. See [#468](https://github.com/folke/trouble.nvim/issues/468) ([e296940](https://github.com/folke/trouble.nvim/commit/e2969409cf3f38f69913cc8fd9aa13137aabe760))\n\n\n### Performance Improvements\n\n* use promises for fetching sections ([e49a490](https://github.com/folke/trouble.nvim/commit/e49a49044cca072c4aca1cb3a5013aa92ac3b4f9))\n\n## [3.1.0](https://github.com/folke/trouble.nvim/compare/v3.0.0...v3.1.0) (2024-05-31)\n\n\n### Features\n\n* added severity filter keymap and improved filtering actions ([7842dbb](https://github.com/folke/trouble.nvim/commit/7842dbb70f088cbaae969004bd2fbae09b2a2d26))\n* only open trouble when results (optionally). Fixes [#450](https://github.com/folke/trouble.nvim/issues/450) ([8fbd2ab](https://github.com/folke/trouble.nvim/commit/8fbd2abb3ff42ebb134e389f405bfa9140db1fe3))\n* **telescope:** allow passing additional trouble options to telescope open/add. Fixes [#457](https://github.com/folke/trouble.nvim/issues/457) ([4eaaf9c](https://github.com/folke/trouble.nvim/commit/4eaaf9cf8b967010998ccfc4af525b3e6d70b8b5))\n\n\n### Bug Fixes\n\n* close section session when needed ([2caf73d](https://github.com/folke/trouble.nvim/commit/2caf73d2d136625d77c0d25cc3b5d5e1e0bef3d0))\n* **fold:** start folding with closest non leaf node. Fixes [#420](https://github.com/folke/trouble.nvim/issues/420) ([f248c69](https://github.com/folke/trouble.nvim/commit/f248c6941ba5a48be531cbb25aac32e1042c65ad))\n* **follow:** improve the way follow works ([cf81aac](https://github.com/folke/trouble.nvim/commit/cf81aaca820017388fc630c534774c95b58233f2))\n* **format:** compat old signs ([0e843ed](https://github.com/folke/trouble.nvim/commit/0e843edbdc1b25ca6a5468d636b22e7035a4ad69))\n* **format:** fallback to sign_defined. Fixes [#448](https://github.com/folke/trouble.nvim/issues/448) ([36545cb](https://github.com/folke/trouble.nvim/commit/36545cb88fa999f211bfc341998f501803bf5434))\n* **lsp:** batch get offset position for lsp results. See [#452](https://github.com/folke/trouble.nvim/issues/452) ([96c30dc](https://github.com/folke/trouble.nvim/commit/96c30dc6ae10e42ab47c1f68d7f715bf01100c48))\n* **lsp:** correctly clear location cache ([7ea94a6](https://github.com/folke/trouble.nvim/commit/7ea94a6366141878758938010e4a0818a56721ad))\n* **lsp:** exclude locations that match the current line ([8c03e13](https://github.com/folke/trouble.nvim/commit/8c03e133bc88fb7c242e9915d06f0a8978511c29))\n* make sure line is always a string passed to get_line_col ([5a12185](https://github.com/folke/trouble.nvim/commit/5a12185787896da209738bd41cbe4133d82ce9bb))\n* **preview:** correctly load non-scratch buffers ([965f56f](https://github.com/folke/trouble.nvim/commit/965f56f3e17baee4213cf50637f92de4be32d8e9))\n* **preview:** correctly pass options to create scratch buffers. Fixes [#451](https://github.com/folke/trouble.nvim/issues/451) ([c50c7e3](https://github.com/folke/trouble.nvim/commit/c50c7e35d4f504d6336875994109c546ff0634b5))\n* **preview:** don't error on invalid positions ([6112c3c](https://github.com/folke/trouble.nvim/commit/6112c3c5c903a05178276a083edc756ba3cb65a0))\n* **qf:** only listen for TextChanged in the main buffer. See [#201](https://github.com/folke/trouble.nvim/issues/201) ([f75992f](https://github.com/folke/trouble.nvim/commit/f75992f9a1b93cc4490dca28f93acc921c25419e))\n* **qf:** update qflist on TextChanged to update pos. Fixes [#201](https://github.com/folke/trouble.nvim/issues/201) ([c1d9294](https://github.com/folke/trouble.nvim/commit/c1d9294eb73479fd4007237613eb7e945cd84e20))\n* stop ([bda8de4](https://github.com/folke/trouble.nvim/commit/bda8de4205f06c3939b8b59e4da1f3713d04ea05))\n* **telescope:** remove filter on `buf = 0`. See [#399](https://github.com/folke/trouble.nvim/issues/399) ([f776ab0](https://github.com/folke/trouble.nvim/commit/f776ab0ff1658f052b7345d4bbd5961b443ea8a0))\n* **view:** restore loc on first render and dont delete last loc if trouble window was never visisted. See [#367](https://github.com/folke/trouble.nvim/issues/367) ([51bf510](https://github.com/folke/trouble.nvim/commit/51bf51068d929173157ebcfb863115760c837355))\n\n\n### Performance Improvements\n\n* **lsp:** cache location requests ([6053627](https://github.com/folke/trouble.nvim/commit/6053627943020d9774c75ec637eb06847a79c7a1))\n* **lsp:** optimize batch fetching lsp item locations. Fixes [#452](https://github.com/folke/trouble.nvim/issues/452) ([a6f1af5](https://github.com/folke/trouble.nvim/commit/a6f1af567fc987306f0f328e78651bab1bfe874e))\n* much faster treesitter highlighter ([d4de08d](https://github.com/folke/trouble.nvim/commit/d4de08d9314a9ddf7278ee16efb58d0efe332bc8))\n* prevent autocmd leaks ([9e3391c](https://github.com/folke/trouble.nvim/commit/9e3391ce735f4f6fa98fe70ba9a3e444f2fd539a))\n* **preview:** re-use existing preview when preview is for the same file ([a415b64](https://github.com/folke/trouble.nvim/commit/a415b64b8a702ab6388e3aaaf16306750fc53f79))\n\n## [3.0.0](https://github.com/folke/trouble.nvim/compare/v2.10.0...v3.0.0) (2024-05-30)\n\n\n### ⚠ BREAKING CHANGES\n\n* Trouble v3 is now merged in main. You may need to update your configs.\n\n### Features\n\n* `Trouble` now shows vim.ui.select to chose a mode ([0189184](https://github.com/folke/trouble.nvim/commit/01891844a9adb3b5b2de508724024d516a2b891a))\n* added basename/dirname ([bb3740a](https://github.com/folke/trouble.nvim/commit/bb3740a1c41e83bcd59c3fe04714a85b445c4742))\n* added help ([68ac238](https://github.com/folke/trouble.nvim/commit/68ac238aeef333a37dc95d875bed46a2698793d5))\n* added kind symbol highlights ([de08657](https://github.com/folke/trouble.nvim/commit/de086574208b19b762055487334ce50ca95cc008))\n* added lpeg parser for parsing `:Trouble` args into lua tables ([b25ef53](https://github.com/folke/trouble.nvim/commit/b25ef53117b0bdc5733d26e42a55c7f32daadbe5))\n* added missing fold keymaps. folding is now feature complete ([9fb1be0](https://github.com/folke/trouble.nvim/commit/9fb1be0915202989bd17e0c9768be23ae7b15010))\n* added multiline option back ([d80e978](https://github.com/folke/trouble.nvim/commit/d80e978f70cc3c026ad028dafbde9e3ad45ba54c))\n* added proper api ([a327003](https://github.com/folke/trouble.nvim/commit/a3270035999dc965176ccd140dcc9afe57f0934a))\n* added support for formatting fields with a treesitter language ([21cfee9](https://github.com/folke/trouble.nvim/commit/21cfee9e4e026482c1c9719156aae3152b2c590a))\n* allow items without buf ([c3b01ce](https://github.com/folke/trouble.nvim/commit/c3b01ce7662dda3a542c52aa1521f8467300c84a))\n* allow top-level filter ([12447df](https://github.com/folke/trouble.nvim/commit/12447df2a81205b8bda12dd1c9271c1c0059184f))\n* **config:** added `auto_jump` to jump to the item when there's only one. Fixes [#409](https://github.com/folke/trouble.nvim/issues/409) ([94a84ab](https://github.com/folke/trouble.nvim/commit/94a84ab884757b1a9f697807e7bdace8b8919afb))\n* **config:** added keymap to inspect an item. Useful for dev ([9da1a47](https://github.com/folke/trouble.nvim/commit/9da1a4783bc0d87427f5cbf6964321774e0bb1bc))\n* **config:** set `focus=false` by default ([c7e5398](https://github.com/folke/trouble.nvim/commit/c7e539819e6d21428a747f46715a23b3d1a204b6))\n* **diagnostics:** added support for diagnostics signs on Neovim &gt;= 0.10.0. Fixes [#369](https://github.com/folke/trouble.nvim/issues/369), fixes [#389](https://github.com/folke/trouble.nvim/issues/389) ([6303740](https://github.com/folke/trouble.nvim/commit/6303740eb1a0730b5654d554ba38bd9614c87e28))\n* **filter:** added filetype filter ([e541444](https://github.com/folke/trouble.nvim/commit/e5414444bdbd5fb70a954ad24abeaa0866179f62))\n* **filter:** easier filtering of any values ([1b528d8](https://github.com/folke/trouble.nvim/commit/1b528d8f3b91fe07ab4f27cbe8eee65b0532192b))\n* **filter:** range filter ([34a06d6](https://github.com/folke/trouble.nvim/commit/34a06d6f4bd32b37f685a36a5037c1556ce6b88f))\n* filters, formatters and sorters are now configurable ([c16679d](https://github.com/folke/trouble.nvim/commit/c16679ddf67f28b5df0735f488497a4c1e7881ee))\n* **format:** formats now support `{one|two}`. First field that returns a value will be used ([26ad82e](https://github.com/folke/trouble.nvim/commit/26ad82eb3c81a434ade3d7bebd97e02565ac717e))\n* global view filters and easy toggling of just items of the current buffer ([11e7c39](https://github.com/folke/trouble.nvim/commit/11e7c39803ff33c68346019a46172fe9be5f3f6d))\n* improved commandline parser and completion ([f7eccfb](https://github.com/folke/trouble.nvim/commit/f7eccfbddef64f3379cf6997617cb41e3005f355))\n* initial commit of rewrite ([d9542ca](https://github.com/folke/trouble.nvim/commit/d9542ca97e37a43844d9088bf453bbb257de423c))\n* item hierarchies and directory grouping ([d2ed413](https://github.com/folke/trouble.nvim/commit/d2ed41320e548149d024634d8a6aa1b5c40396a1))\n* Item.get_lang and Item.get_ft ([498da6b](https://github.com/folke/trouble.nvim/commit/498da6bcff8170f620506f66dd71465b3565baaa))\n* **item:** util method to add missing text to items ([de9e7e6](https://github.com/folke/trouble.nvim/commit/de9e7e68ebb7aa36d78a68207d42943d49a31a85))\n* **lsp:** added `lsp_incoming_calls` and `lsp_outgoing_calls`. Closes [#222](https://github.com/folke/trouble.nvim/issues/222) ([b855469](https://github.com/folke/trouble.nvim/commit/b855469429f10c74a3314432ba2735a32115cbb2))\n* **lsp:** document symbols caching and compat with Neovim 0.9.5 ([2f49b92](https://github.com/folke/trouble.nvim/commit/2f49b920b0822a3d06c25d24d840830016168a82))\n* main window tracking ([23a0631](https://github.com/folke/trouble.nvim/commit/23a06316607fe2d2ab311fdb5bb45157c2d8ec91))\n* make the preview action a toggle ([86da179](https://github.com/folke/trouble.nvim/commit/86da1794855f71ec592efec2a6a65911f08892a2))\n* preview can now be shown in a split/float ([e2919eb](https://github.com/folke/trouble.nvim/commit/e2919eb565ccc66d4adad729b4e323a718d41953))\n* preview is now fully configurable ([b99110a](https://github.com/folke/trouble.nvim/commit/b99110adc3815f1b7b8fe3dd40f4c9da315f7cca))\n* **preview:** option to force loading real buffers in preview. Fixes [#435](https://github.com/folke/trouble.nvim/issues/435) ([ccacba2](https://github.com/folke/trouble.nvim/commit/ccacba22b2c1946cfe1b9f767f7880bcd031ad7c))\n* **preview:** use a float to show preview in the main window instead of messing with the main window itself ([9e0311d](https://github.com/folke/trouble.nvim/commit/9e0311d177af7cd6750d88280d589ceca4f7685a))\n* **preview:** window var to know a window is a preview win ([dcecbb9](https://github.com/folke/trouble.nvim/commit/dcecbb9b9d67770c8df4c1c49b91631fbdae8ae5))\n* **qf:** add treesitter highlighting to quickfix/loclist. Fixes [#441](https://github.com/folke/trouble.nvim/issues/441) ([325d681](https://github.com/folke/trouble.nvim/commit/325d681953611336cdfdf08a3d71e5125c5f89a5))\n* **render:** `{field:ts}` will now use the treesitter lang of the item buffer for highlighting ([21af85c](https://github.com/folke/trouble.nvim/commit/21af85cc97860e3bcf157891c2598af517f9b421))\n* **render:** added support for rendering multiple sections ([332b25b](https://github.com/folke/trouble.nvim/commit/332b25b09c7159a82322ec97f7aa1717133ffa6f))\n* **source:** added lsp source ([3969907](https://github.com/folke/trouble.nvim/commit/39699074cd18cdeb5e9a29e80ffd2d239c58ac7e))\n* **source:** added quickfix source ([3507b7b](https://github.com/folke/trouble.nvim/commit/3507b7b694ddee5921c7004c6bed0b71ab8e0920))\n* **sources:** added support for loading external sources ([89ac6f1](https://github.com/folke/trouble.nvim/commit/89ac6f1a7f238ca65d964569786b205a496ad213))\n* **sources:** added telescope source ([39069e2](https://github.com/folke/trouble.nvim/commit/39069e2f4139c7ae28cf7e16fe610b8462fb3939))\n* **source:** sources can now have multiple child sources ([c433301](https://github.com/folke/trouble.nvim/commit/c4333014a770eca5a66c7685f4063d8de13517db))\n* **source:** sources now always execute in the context of the main window even when a preview is active ([4eab561](https://github.com/folke/trouble.nvim/commit/4eab56122bb335a5627405791e4691af5043493e))\n* **statusline:** added statusline component ([5c0b163](https://github.com/folke/trouble.nvim/commit/5c0b1639c83266427489bb515bffd8b8bc055809))\n* **statusline:** allow 'fixing' the statusline bg color based on a hl_group. Fixes [#411](https://github.com/folke/trouble.nvim/issues/411) ([986b44d](https://github.com/folke/trouble.nvim/commit/986b44d4471ee8b8a708a0172fb0829a9c858543))\n* **statusline:** statusline api ([c219a1a](https://github.com/folke/trouble.nvim/commit/c219a1a9f56a70ac55e89325c333fcfd0616ea97))\n* **telescope:** added option to add telescope results to trouble, without clearing the existing results. Fixes [#370](https://github.com/folke/trouble.nvim/issues/370) ([a7119ab](https://github.com/folke/trouble.nvim/commit/a7119abb0cd1b1ef058fc99c036230bbe153504f))\n* **tree:** added `flatten()` to get all items from the tree ([35c0236](https://github.com/folke/trouble.nvim/commit/35c0236ceb78fc37a94f5882f736416ebb15c306))\n* Trouble v3 is now merged in main. You may need to update your configs. ([1b362b8](https://github.com/folke/trouble.nvim/commit/1b362b861eacb9b2367ce92129fad86352707311))\n* **util:** better notify functions ([c68c915](https://github.com/folke/trouble.nvim/commit/c68c915353dc5162e5d7494b5ca0919f0e336318))\n* **util:** fast get_lines for a buffer ([6940cd8](https://github.com/folke/trouble.nvim/commit/6940cd8c6913e834254e7221ede0ca6a38c81fc0))\n* **util:** fast plain text split ([6a30aec](https://github.com/folke/trouble.nvim/commit/6a30aec15ca90e8b396a381f8f1b7c452f6ce68a))\n* **util:** make throttles configurable ([8c297c1](https://github.com/folke/trouble.nvim/commit/8c297c171547e8c81fc918a7f31b3c2a8ce58512))\n* **view:** added support for pinned views. Main window of the view will stay the same as long as its a valid window ([17131e2](https://github.com/folke/trouble.nvim/commit/17131e2b9a0b7d17046cb9b7ed9b7eeb36b6423a))\n* **view:** expose some params in the trouble window var. Fixes [#357](https://github.com/folke/trouble.nvim/issues/357) ([a4b9849](https://github.com/folke/trouble.nvim/commit/a4b9849ce7ec14213069034403f5fc96d174046b))\n* **view:** follow now also scrolls to the file in the list when not on an item ([30b939e](https://github.com/folke/trouble.nvim/commit/30b939efebd8559e9e84c95e3658c447f702b1c2))\n* **view:** follow the current item in the list ([e76e280](https://github.com/folke/trouble.nvim/commit/e76e280701e643e8a3074edcb86e819298e7df12))\n* **view:** when toggling a trouble list, restore to the last location. Fixes [#367](https://github.com/folke/trouble.nvim/issues/367) ([1d951f5](https://github.com/folke/trouble.nvim/commit/1d951f5c13fd56933e9170b84bfcbbf8ca1a582b))\n* **window:** added possibility to override TroubleNormalNC. Fixes [#216](https://github.com/folke/trouble.nvim/issues/216) ([daa5157](https://github.com/folke/trouble.nvim/commit/daa5157e3f0f6cf80ca473d7e43bd73734d6594d))\n* **window:** added support for showing a floating window over the main window ([3525169](https://github.com/folke/trouble.nvim/commit/35251698e7836ecb3ee981efe2efe7bcb64ca5f3))\n* **window:** allow setting width/height a size for splits. either will be used based on position ([e8ee9b0](https://github.com/folke/trouble.nvim/commit/e8ee9b01ee46f9cd2e2a8fe8b883320f3212608d))\n* **window:** expose some window vars for integration with other plugins (edgy.nvim) ([aae1da8](https://github.com/folke/trouble.nvim/commit/aae1da81cba50ca207ce3681e5ef927bd369f074))\n\n\n### Bug Fixes\n\n* add vim to parser globals ([6077342](https://github.com/folke/trouble.nvim/commit/6077342efe9e7f77756dd064cdb2129ffea5674f))\n* **api:** make sure new=true works when opening with a mode string ([398ac76](https://github.com/folke/trouble.nvim/commit/398ac76019a74bbbe0c2b77c0a0a7a1e4ce71c3d))\n* better defaults for lsp/diagnostics ([569416d](https://github.com/folke/trouble.nvim/commit/569416d52f1953c35ba8494d7cab2be1b01fe409))\n* better way of creating preview buffers ([667b010](https://github.com/folke/trouble.nvim/commit/667b010ba81d7ce49fa2cca6cb80c0b85b328543))\n* **command:** improved command completion ([9f59aac](https://github.com/folke/trouble.nvim/commit/9f59aac5ccdb3b5c2c1425176e0112efd7b84a16))\n* **commmand:** show mode descriptions ([ce488b9](https://github.com/folke/trouble.nvim/commit/ce488b9b4ddc0f9a61d48d0ae8974c22e5472e0c))\n* **config:** fixed some highlights to use the latest treesitter hl groups ([63313cd](https://github.com/folke/trouble.nvim/commit/63313cd5a1e55d1ec870f9716f2e6cc9b6471cfc))\n* deprecated tbl_islist ([#436](https://github.com/folke/trouble.nvim/issues/436)) ([5aa7993](https://github.com/folke/trouble.nvim/commit/5aa79935f1de301ed3592981ad7031c166cc5c84))\n* diagnostics sections ([27efb63](https://github.com/folke/trouble.nvim/commit/27efb6326d8bb9019d2b69e737c1c3741a3af568))\n* **diagnostics:** use main buffer for buffer-local diags ([259770d](https://github.com/folke/trouble.nvim/commit/259770dd860fd08007d13c116a5a7ee985e5f5bf))\n* **filter:** fix range filter to include col ([6cae8af](https://github.com/folke/trouble.nvim/commit/6cae8af72ccf1be72718d3f735a467bff23c9beb))\n* **filter:** range should also check that the buffer is the same ([fdd27d8](https://github.com/folke/trouble.nvim/commit/fdd27d8ac5276147b91cee2c2578b178a3dd7be2))\n* **format:** always pass a valid buffer to ftdetect even just the current onw ([b01b11e](https://github.com/folke/trouble.nvim/commit/b01b11efc901dc3bc5f89d164de5eb71d56cc257))\n* **help:** sort keymaps case incensitive ([5d81927](https://github.com/folke/trouble.nvim/commit/5d81927bc7eb9de085d9ea6cf8b977616694f771))\n* **highlights:** reset statusline hl groups when colorscheme changes ([a665272](https://github.com/folke/trouble.nvim/commit/a665272b1e1d4b06b1b6824cc3f853874b06c0a1))\n* **item:** clamp pos ([6c0204c](https://github.com/folke/trouble.nvim/commit/6c0204cb7d758d70edc5b88919f19a76de853aa7))\n* **jump:** save current main cursor to jump list before jumping. Fixes [#385](https://github.com/folke/trouble.nvim/issues/385) ([bd8bfc8](https://github.com/folke/trouble.nvim/commit/bd8bfc8abedbf992a6d8c0db941780e1a79d3b41))\n* **lsp:** check if buf is still valid after receiving document symbols ([33ec71c](https://github.com/folke/trouble.nvim/commit/33ec71cf377c518d8b8c022e2b46511f85c3a47d))\n* **lsp:** handle invalid positions. Fixes [#434](https://github.com/folke/trouble.nvim/issues/434) ([371cf26](https://github.com/folke/trouble.nvim/commit/371cf26bcbddb39e2e91d69dae90a480f29c3fc0))\n* **lsp:** refresh on LspAttach ([17afc44](https://github.com/folke/trouble.nvim/commit/17afc44fc317449128f1804ea6f316ce457295cc))\n* **main:** always return a main window, even when no main. Fixes [#426](https://github.com/folke/trouble.nvim/issues/426) ([bda72a5](https://github.com/folke/trouble.nvim/commit/bda72a548e4eb9cab3bcf567ca965b21a136263d))\n* make focus the default ([ba1ae49](https://github.com/folke/trouble.nvim/commit/ba1ae497e1899af0e57adb815ab30e37b73d0b76))\n* **parser:** handle empty args ([e667da7](https://github.com/folke/trouble.nvim/commit/e667da705c64509347829326d19668e3276355e9))\n* **preview:** better preview for multiline items ([60c9fdc](https://github.com/folke/trouble.nvim/commit/60c9fdcad7003fe6ed6f4a225bf709acd19068df))\n* **preview:** center preview location and open folds. See [#408](https://github.com/folke/trouble.nvim/issues/408) ([a87fa2a](https://github.com/folke/trouble.nvim/commit/a87fa2ae521d058ad67dc7610efdd234b792d6ef))\n* **preview:** clear highlights of preview buffer ([d590491](https://github.com/folke/trouble.nvim/commit/d590491de9515caf5ec3a3a0bd0fdb3047b1fda3))\n* **preview:** dont show preview for directories. Fixes [#410](https://github.com/folke/trouble.nvim/issues/410) ([769ee0f](https://github.com/folke/trouble.nvim/commit/769ee0f632ea3b6ffc5716590a711db340e80caf))\n* **preview:** fixup for directory check ([eed25b2](https://github.com/folke/trouble.nvim/commit/eed25b2bcea6e59e5f5c92c184ac08be4590b6de))\n* **preview:** pass valid buffer so that ftdetect works for ts files. See [#435](https://github.com/folke/trouble.nvim/issues/435) ([65f2430](https://github.com/folke/trouble.nvim/commit/65f2430f6d6276832ec9500b5eafabb01e17d01a))\n* **preview:** set correct winhighlight for preview window in main. See [#408](https://github.com/folke/trouble.nvim/issues/408) ([8c3c1db](https://github.com/folke/trouble.nvim/commit/8c3c1db742e74f0e48134cd4e84c976c2706e6b6))\n* **preview:** unload preview buffer again when closing and when it wasnt loaded before ([8cc680a](https://github.com/folke/trouble.nvim/commit/8cc680a25f30e63c9e92005155c7916b9a000ac9))\n* proper deprecated fix ffs... Fixes [#438](https://github.com/folke/trouble.nvim/issues/438) ([a8264a6](https://github.com/folke/trouble.nvim/commit/a8264a65a0b894832ea642844f5b7c30112c458f))\n* properly deal with multiline treesitter segments ([31681a9](https://github.com/folke/trouble.nvim/commit/31681a92e2e7fa0537284ad1516561b46cdb4a24))\n* **qf:** col/row offsets ([8ad817f](https://github.com/folke/trouble.nvim/commit/8ad817f12b4c9c4d6bd239c963cda3ac518d272a))\n* remove `buf = 0` sorting since it acts weirdly with next/prev ([2b589e9](https://github.com/folke/trouble.nvim/commit/2b589e938c4b5245d7f74b7f23293645e566cee3))\n* remove space from `zz zv` command ([ca2cd56](https://github.com/folke/trouble.nvim/commit/ca2cd56d14df8fc619ba44bebd5334d78b57d74c))\n* require Neovim &gt;= 0.9.2 ([2448521](https://github.com/folke/trouble.nvim/commit/24485219198a1ab10731e45229f2736ec3242231))\n* **section:** dont trigger on invalid buffers ([29ee890](https://github.com/folke/trouble.nvim/commit/29ee890b28280b0a8a504571596a0508244122e1))\n* **setup:** add check for NEovim 0.10.0 or markdown parsers. Fixes [#413](https://github.com/folke/trouble.nvim/issues/413) ([6267ef1](https://github.com/folke/trouble.nvim/commit/6267ef15e98bd8df29be7a8d6f47b0e724ceaaaf))\n* **sources:** always load sources when not registered yet. Fixes [#393](https://github.com/folke/trouble.nvim/issues/393) ([1470302](https://github.com/folke/trouble.nvim/commit/1470302bd7eef110aef3710dfc8808bc3c3a2179))\n* specs and tests ([315f624](https://github.com/folke/trouble.nvim/commit/315f624492c54f9893631459fc79e6c0b33b7cad))\n* **statusline:** double escape `#`. Fixes [#424](https://github.com/folke/trouble.nvim/issues/424) ([9ddfd47](https://github.com/folke/trouble.nvim/commit/9ddfd47eec3a1bd43f5e7dd34eb1084f6793eaba))\n* **statusline:** make sure max_items is honored ([da8ba7d](https://github.com/folke/trouble.nvim/commit/da8ba7dfba2341c5ea679e6cff85c01943777380))\n* **statusline:** schedule statusline refresh ([f000daa](https://github.com/folke/trouble.nvim/commit/f000daadd6d49b30eebacb2a6dd7c4d9758f2de6))\n* **telescope:** close telescope after sending results to trouble ([2753932](https://github.com/folke/trouble.nvim/commit/2753932bed13cff73be80e2565043dccce899983))\n* **telescope:** deprecation warning for old telescope provider ([0d7cdeb](https://github.com/folke/trouble.nvim/commit/0d7cdeba2d139f26314d53e2e06507b6a7b72e3b))\n* **throttle:** fixed throttling so that it now only gets scheduled when there are pending args ([0db2084](https://github.com/folke/trouble.nvim/commit/0db20847636e6715cb2d1c542fee34d349f47ee3))\n* **tree:** fixed tree item count. Fixes [#419](https://github.com/folke/trouble.nvim/issues/419) ([cb59440](https://github.com/folke/trouble.nvim/commit/cb594402cf4407bbf54111c20ef39d42d9b017f6))\n* **tree:** make sure qf items always have a unique id. Fixes [#367](https://github.com/folke/trouble.nvim/issues/367) ([c0755d5](https://github.com/folke/trouble.nvim/commit/c0755d59731869994187b04adcb89ebce75c27eb))\n* **treesitter:** show warning for missing treesitter parsers ([4253652](https://github.com/folke/trouble.nvim/commit/425365272136c731eea9da4c337f7b1dfe4d44d6))\n* **tree:** use format as node id for group without fields ([a29c293](https://github.com/folke/trouble.nvim/commit/a29c29382da0dcb8554da25e40a4d2495dff771f))\n* **ui:** better deal with invalid items positions and extmarks. Fixes [#404](https://github.com/folke/trouble.nvim/issues/404) ([bc0a194](https://github.com/folke/trouble.nvim/commit/bc0a19482ee8f68eb427939df2e28cca800b9cbb))\n* **util:** deprecation warnings for tbl_islist ([7577f3a](https://github.com/folke/trouble.nvim/commit/7577f3a82ff60ef7425451b1f40dd83ffee12307))\n* **util:** typo ([37f6266](https://github.com/folke/trouble.nvim/commit/37f62665dfc8db002f7fe62ae6b467c566329ec3))\n* **util:** use xpcall in throttle for better stack traces ([6d9a0ba](https://github.com/folke/trouble.nvim/commit/6d9a0baeb226548b967329d7c045c38ff16a19e8))\n* **view:** check if trouble win is still valid in OptionSet. Fixes [#400](https://github.com/folke/trouble.nvim/issues/400) ([e9fae8c](https://github.com/folke/trouble.nvim/commit/e9fae8c453eac69aa33dc4899c46b76417a04e3e))\n* **view:** check that window is open before checking active item ([5b5446d](https://github.com/folke/trouble.nvim/commit/5b5446ddf2d6c50b555d28459ce7632ca39d6ac0))\n* **view:** do `norm! zz zv` after jump. See [#408](https://github.com/folke/trouble.nvim/issues/408) ([74e31e7](https://github.com/folke/trouble.nvim/commit/74e31e732fa6888a72f6b73b3115b8ea84ef47f5))\n* **view:** dont refresh items when calling `open` and already open ([7485aa7](https://github.com/folke/trouble.nvim/commit/7485aa70e0341c55bd25e2287b29e991c61297d1))\n* **view:** dont trigger follow when moving. Fixes [#3359](https://github.com/folke/trouble.nvim/issues/3359) ([7cc4df2](https://github.com/folke/trouble.nvim/commit/7cc4df259b208a1f19a67a171da7d5f7e917e7c4))\n* **view:** store restore locations when moving the cursor. See [#367](https://github.com/folke/trouble.nvim/issues/367) ([d8265a6](https://github.com/folke/trouble.nvim/commit/d8265a6f0eea8fff249decf3a1304991f850715b))\n* **window:** main window float should have regular winhighlight ([6ccd579](https://github.com/folke/trouble.nvim/commit/6ccd579a177dc81d81706194131e23b24e93e5fa))\n* **window:** properly deal with alien buffers opening in trouble windows ([92832c4](https://github.com/folke/trouble.nvim/commit/92832c4676e079c4fe824fd5c9a5ba3259d2e506))\n* **windows:** Corrected regex matching path with backslash. ([#396](https://github.com/folke/trouble.nvim/issues/396)) ([a784506](https://github.com/folke/trouble.nvim/commit/a784506b4e5f649e568ec6763aa90d7a7ec4c0b3))\n* **window:** set cursorlineopt for the trouble window. Fixes [#356](https://github.com/folke/trouble.nvim/issues/356) ([4c07228](https://github.com/folke/trouble.nvim/commit/4c07228dec2663d9dda9b12f68785a9f9ec9fd72))\n\n\n### Performance Improvements\n\n* better throttle ([c10d53d](https://github.com/folke/trouble.nvim/commit/c10d53d3d7a48dc8090e44f375d2a12ca7ef0fb6))\n* only trigger refresh when event happens in main for some sources ([6eac568](https://github.com/folke/trouble.nvim/commit/6eac5689fe59fbb8038eebb150c66dbb2a3960a0))\n* use weak references to prevent memory leaks ([4d31d77](https://github.com/folke/trouble.nvim/commit/4d31d77561860cbe582239ebb970db1c75872018))\n* **util:** get_lines can now use a buf or filename or both ([e987642](https://github.com/folke/trouble.nvim/commit/e9876428329f2a91e5dd8b29bd854d9b9ff7813a))\n\n## [2.10.0](https://github.com/folke/trouble.nvim/compare/v2.9.1...v2.10.0) (2023-10-18)\n\n\n### Features\n\n* `open({focus=false})` now works as intended ([600fe24](https://github.com/folke/trouble.nvim/commit/600fe24ad04f130030fa54f0c70949ff084810a3))\n\n\n### Bug Fixes\n\n* **auto_open:** dont steal focus on auto open. Fixes [#344](https://github.com/folke/trouble.nvim/issues/344) ([1f00b6f](https://github.com/folke/trouble.nvim/commit/1f00b6f730c5ef6bcfeb829a5659ed3780778087))\n\n## [2.9.1](https://github.com/folke/trouble.nvim/compare/v2.9.0...v2.9.1) (2023-10-09)\n\n\n### Bug Fixes\n\n* **preview:** skip non-existing. Fixes [#87](https://github.com/folke/trouble.nvim/issues/87). Fixes [#188](https://github.com/folke/trouble.nvim/issues/188). Fixes [#336](https://github.com/folke/trouble.nvim/issues/336). ([#338](https://github.com/folke/trouble.nvim/issues/338)) ([5e78824](https://github.com/folke/trouble.nvim/commit/5e7882429ee2e235148ab759a6159950afd8021a))\n\n## [2.9.0](https://github.com/folke/trouble.nvim/compare/v2.8.0...v2.9.0) (2023-10-07)\n\n\n### Features\n\n* Make floating window configuration customizable ([#310](https://github.com/folke/trouble.nvim/issues/310)) ([ef0336a](https://github.com/folke/trouble.nvim/commit/ef0336a818e562439e25638b866cb4638a0fdc26))\n\n\n### Bug Fixes\n\n* check that view is valid before render and focus ([#319](https://github.com/folke/trouble.nvim/issues/319)) ([81e1643](https://github.com/folke/trouble.nvim/commit/81e1643a7c6b426535cf23ebdb28baec4ab7428e))\n* only filter msg if sev is hardcoded ([#328](https://github.com/folke/trouble.nvim/issues/328)) ([0ccc43d](https://github.com/folke/trouble.nvim/commit/0ccc43d61e0f9278056a8eeefbe022ce71707a85))\n* **qf:** properly deal with invalid qf entries. Fixes [#87](https://github.com/folke/trouble.nvim/issues/87). Fixes [#188](https://github.com/folke/trouble.nvim/issues/188). Fixes [#336](https://github.com/folke/trouble.nvim/issues/336) ([46b60e9](https://github.com/folke/trouble.nvim/commit/46b60e9fb942d60740c647f61fd779f05e7b9392))\n\n## [2.8.0](https://github.com/folke/trouble.nvim/compare/v2.7.0...v2.8.0) (2023-07-25)\n\n\n### Features\n\n* Create Configuration for IncludeDeclaration ([#312](https://github.com/folke/trouble.nvim/issues/312)) ([7691d93](https://github.com/folke/trouble.nvim/commit/7691d93131be9c4ef7788892a9c52374642beb89))\n\n## [2.7.0](https://github.com/folke/trouble.nvim/compare/v2.6.0...v2.7.0) (2023-07-25)\n\n\n### Features\n\n* Expose help action ([#311](https://github.com/folke/trouble.nvim/issues/311)) ([467dc20](https://github.com/folke/trouble.nvim/commit/467dc204af863a9f11bc3444b8f89af286fbf6b2))\n* Use code descriptions for opening URIs with extra information ([#309](https://github.com/folke/trouble.nvim/issues/309)) ([d2b0f1d](https://github.com/folke/trouble.nvim/commit/d2b0f1de1fe6f013d38234f7557c7935a9f97655))\n\n## [2.6.0](https://github.com/folke/trouble.nvim/compare/v2.5.0...v2.6.0) (2023-07-22)\n\n\n### Features\n\n* make multiline the default ([1f2eb71](https://github.com/folke/trouble.nvim/commit/1f2eb71948b8d08cd8fe0947f9dae95c441baf6d))\n\n## [2.5.0](https://github.com/folke/trouble.nvim/compare/v2.4.0...v2.5.0) (2023-07-22)\n\n\n### Features\n\n* add multiline diagnostic support ([#305](https://github.com/folke/trouble.nvim/issues/305)) ([7a6abd7](https://github.com/folke/trouble.nvim/commit/7a6abd7ed811def9494316d4217d1dcc80b05048))\n* Map double click to jump action ([#158](https://github.com/folke/trouble.nvim/issues/158)) ([ef53b9a](https://github.com/folke/trouble.nvim/commit/ef53b9a1401919a9a3ae5b2949068c456ce23085))\n* use markdown to render hover ([835b87d](https://github.com/folke/trouble.nvim/commit/835b87d93537a3cc403b961c084ca8c2998758cd))\n* **util:** trigger TroubleJump on jump. Closes [#248](https://github.com/folke/trouble.nvim/issues/248) ([d91f3b3](https://github.com/folke/trouble.nvim/commit/d91f3b3d588b0259060780c73dd4c93a8f158f38))\n\n## [2.4.0](https://github.com/folke/trouble.nvim/compare/v2.3.0...v2.4.0) (2023-07-16)\n\n\n### Features\n\n* add option to control cycling of result list ([#302](https://github.com/folke/trouble.nvim/issues/302)) ([e7805dc](https://github.com/folke/trouble.nvim/commit/e7805dc3448f28599e022dc7a0e58060dfdeeb9a))\n* rendering messages from provider ([#304](https://github.com/folke/trouble.nvim/issues/304)) ([a66a78b](https://github.com/folke/trouble.nvim/commit/a66a78b8878780e3b3154e9812ff040ec9b0f1d6))\n\n\n### Bug Fixes\n\n* Check parent window is valid before setting active ([#291](https://github.com/folke/trouble.nvim/issues/291)) ([c14786d](https://github.com/folke/trouble.nvim/commit/c14786d5e88f3e66360c70bab56694abd0e60af6))\n* move end of doc pos to last line. Fixes [#151](https://github.com/folke/trouble.nvim/issues/151) ([cb4da04](https://github.com/folke/trouble.nvim/commit/cb4da0401abe7ae6f368bf79d2ed6c2571b1e7ba))\n\n## [2.3.0](https://github.com/folke/trouble.nvim/compare/v2.2.3...v2.3.0) (2023-05-25)\n\n\n### Features\n\n* filter diagnostics by severity level ([#285](https://github.com/folke/trouble.nvim/issues/285)) ([b1f607f](https://github.com/folke/trouble.nvim/commit/b1f607ff0f2c107faf8b0c26d09877028b549d63))\n\n## [2.2.3](https://github.com/folke/trouble.nvim/compare/v2.2.2...v2.2.3) (2023-05-22)\n\n\n### Bug Fixes\n\n* set window options locally ([#282](https://github.com/folke/trouble.nvim/issues/282)) ([a5649c9](https://github.com/folke/trouble.nvim/commit/a5649c9a60d7c5aa2fed1781057af3f29b10f167))\n\n## [2.2.2](https://github.com/folke/trouble.nvim/compare/v2.2.1...v2.2.2) (2023-04-17)\n\n\n### Bug Fixes\n\n* **util:** auto_jump when trouble is open. Fixes [#144](https://github.com/folke/trouble.nvim/issues/144) ([e4f1623](https://github.com/folke/trouble.nvim/commit/e4f1623b51e18eb4e2835446e50886062c339f80))\n* **util:** save position in jump list before jump. Fixes [#143](https://github.com/folke/trouble.nvim/issues/143) Fixes [#235](https://github.com/folke/trouble.nvim/issues/235) ([f0477b0](https://github.com/folke/trouble.nvim/commit/f0477b0e78d9a16ff326e356235876ff3f87882d))\n\n## [2.2.1](https://github.com/folke/trouble.nvim/compare/v2.2.0...v2.2.1) (2023-03-26)\n\n\n### Bug Fixes\n\n* **icons:** fixed deprecated icons with nerdfix ([39db399](https://github.com/folke/trouble.nvim/commit/39db3994c8de87b0b5ca7a4d3d415926f201f1fc))\n\n## [2.2.0](https://github.com/folke/trouble.nvim/compare/v2.1.1...v2.2.0) (2023-02-28)\n\n\n### Features\n\n* enable looping during next/prev ([#232](https://github.com/folke/trouble.nvim/issues/232)) ([fc4c0f8](https://github.com/folke/trouble.nvim/commit/fc4c0f82c9181f3c27a4cbdd5db97c110fd78ee9))\n* expose renderer.signs. Fixes [#252](https://github.com/folke/trouble.nvim/issues/252) ([5581e73](https://github.com/folke/trouble.nvim/commit/5581e736c8afc8b227ad958ded1929c8a39f049e))\n\n## [2.1.1](https://github.com/folke/trouble.nvim/compare/v2.1.0...v2.1.1) (2023-02-19)\n\n\n### Bug Fixes\n\n* ensure that the diagnostic parameters are complete ([#179](https://github.com/folke/trouble.nvim/issues/179)) ([210969f](https://github.com/folke/trouble.nvim/commit/210969fce79e7d11554c61bca263d7e1ac77bde0))\n* icorrect row/line in diagnostics. Fixes [#264](https://github.com/folke/trouble.nvim/issues/264) ([32fa4ed](https://github.com/folke/trouble.nvim/commit/32fa4ed742fc91f3075c98edd3c131b716b9d782))\n\n## [2.1.0](https://github.com/folke/trouble.nvim/compare/v2.0.1...v2.1.0) (2023-02-18)\n\n\n### Features\n\n* expose `require(\"trouble\").is_open()` ([2eb27b3](https://github.com/folke/trouble.nvim/commit/2eb27b34442894e903fdc6e01edea6d7c476be63))\n\n## [2.0.1](https://github.com/folke/trouble.nvim/compare/v2.0.0...v2.0.1) (2023-02-16)\n\n\n### Bug Fixes\n\n* **init:** version check ([73eea32](https://github.com/folke/trouble.nvim/commit/73eea32efec2056cdce7593787390fc9aadf9c0c))\n\n## [2.0.0](https://github.com/folke/trouble.nvim/compare/v1.0.2...v2.0.0) (2023-02-16)\n\n\n### ⚠ BREAKING CHANGES\n\n* Trouble now requires Neovim >= 0.7.2\n\n### Features\n\n* Trouble now requires Neovim &gt;= 0.7.2 ([ef93259](https://github.com/folke/trouble.nvim/commit/ef9325970b341d436f43c50ce876aa0a665d3cf0))\n\n\n### Bug Fixes\n\n* Focus parent before closing ([#259](https://github.com/folke/trouble.nvim/issues/259)) ([66b057b](https://github.com/folke/trouble.nvim/commit/66b057b2b07881bceb969624f4c3b5727703c2c8))\n* **preview:** properly load buffer when showing preview ([949199a](https://github.com/folke/trouble.nvim/commit/949199a9ac60ce784a417f90388b8f173ef53819))\n* **util:** properly load a buffer when jumping to it ([bf0eeea](https://github.com/folke/trouble.nvim/commit/bf0eeead88d59d51003f4da1b649b4977ed90e2b))\n\n\n### Performance Improvements\n\n* dont load buffers when processing items. Get line with luv instead ([82c9a9a](https://github.com/folke/trouble.nvim/commit/82c9a9a9cd2cd2cdb05e05a3e6538529e2473e14))\n\n## [1.0.2](https://github.com/folke/trouble.nvim/compare/v1.0.1...v1.0.2) (2023-02-10)\n\n\n### Bug Fixes\n\n* **telescope:** properly fix issue with relative filenames in telescope. See [#250](https://github.com/folke/trouble.nvim/issues/250) ([7da0821](https://github.com/folke/trouble.nvim/commit/7da0821d20342751a7eedecd28cf16040146cbf7))\n\n## [1.0.1](https://github.com/folke/trouble.nvim/compare/v1.0.0...v1.0.1) (2023-01-23)\n\n\n### Bug Fixes\n\n* ensure first line is selected when padding is false ([#233](https://github.com/folke/trouble.nvim/issues/233)) ([b2d6ac8](https://github.com/folke/trouble.nvim/commit/b2d6ac8607e1ab612a85c1ec563aaff3a60f0603))\n* **telescope:** correctly use cwd for files. Fixes [#250](https://github.com/folke/trouble.nvim/issues/250) ([3174767](https://github.com/folke/trouble.nvim/commit/3174767c61b3786e65d78f539c60c6f70d26cdbe))\n\n## 1.0.0 (2023-01-04)\n\n\n### ⚠ BREAKING CHANGES\n\n* renamed use_lsp_diagnostic_signs to use_diagnostic_signs\n* removed deprecated commands\n\n### Features\n\n* added \"hover\" action that defaults to \"K\" to show the full multiline text [#11](https://github.com/folke/trouble.nvim/issues/11) ([9111a5e](https://github.com/folke/trouble.nvim/commit/9111a5eb7881a84cd66107077118614e218fba61))\n* added actions for opening in new tab, split and vsplit. Fixes [#36](https://github.com/folke/trouble.nvim/issues/36) ([c94cc59](https://github.com/folke/trouble.nvim/commit/c94cc599badb7086878559653ec705ed68579682))\n* added mapping for jump & close (defaults to \"o\") [#15](https://github.com/folke/trouble.nvim/issues/15) ([09de784](https://github.com/folke/trouble.nvim/commit/09de78495bad194b2d0d85498a1c1a7996182a71))\n* added support for vim.diagnostics and Neovim 0.7 ([735dcd5](https://github.com/folke/trouble.nvim/commit/735dcd599871179a835d1e0ebd777d4db24c2c72))\n* allow proper passing of plugin options ([79513ed](https://github.com/folke/trouble.nvim/commit/79513ed42a273a1bc80d82c7e1117d3a2e0f2c79))\n* Api to go to first and last items ([#157](https://github.com/folke/trouble.nvim/issues/157)) ([0649811](https://github.com/folke/trouble.nvim/commit/0649811e69a11dea4708a19deee9ab0b1e90313e))\n* better preview and mode ([160fa6c](https://github.com/folke/trouble.nvim/commit/160fa6cb213db6c7a421450b67adc495ae69cef0))\n* command complete ([9923b01](https://github.com/folke/trouble.nvim/commit/9923b01692a238535420d58e440b139a89c3de46))\n* comments to open/toggle workspace or ducument mode directly ([f7db1c2](https://github.com/folke/trouble.nvim/commit/f7db1c29d7eb76cb3310e0aa56a4d546420e7814))\n* config for auto_preview ([0ad97fb](https://github.com/folke/trouble.nvim/commit/0ad97fb67b21579729090214cbb3bce78fd153b7))\n* define multiple keybindings for the same action (better for defaults) ([bf8e8ee](https://github.com/folke/trouble.nvim/commit/bf8e8ee63c38103fb42de0b889810b584e378962))\n* expose items ([#41](https://github.com/folke/trouble.nvim/issues/41)) ([4f84ca4](https://github.com/folke/trouble.nvim/commit/4f84ca4530829b9448c6f13530c26df6d7020fd0))\n* indent lines ([f9e6930](https://github.com/folke/trouble.nvim/commit/f9e6930b5188593b9e6408d8937093d04198e90a))\n* inital version ([980fb07](https://github.com/folke/trouble.nvim/commit/980fb07fd33ea0f72b274e1ad3c8626bf8a14ac9))\n* Lsp implementation ([#50](https://github.com/folke/trouble.nvim/issues/50)) ([069cdae](https://github.com/folke/trouble.nvim/commit/069cdae61d58d2477b150af91692ace636000d47))\n* lsp references, loclist and quickfix lists! ([0b852c8](https://github.com/folke/trouble.nvim/commit/0b852c8418d65191983b2c9b8f90ad6d7f45ff51))\n* made it easier to integrate with trouble ([1dd72c2](https://github.com/folke/trouble.nvim/commit/1dd72c22403519c160b0c694762091971bcf191e))\n* make file grouping and padding configurable ([#66](https://github.com/folke/trouble.nvim/issues/66)) ([ff40475](https://github.com/folke/trouble.nvim/commit/ff40475143ecd40c86f13054935f3afc5653c469))\n* make position of the trouble list configurable (top, bottom, left or right) [#27](https://github.com/folke/trouble.nvim/issues/27) ([0c9ca5e](https://github.com/folke/trouble.nvim/commit/0c9ca5e10c2e5dd8d8479e864e12383b1d614273))\n* make signs configurable ([ff9fd51](https://github.com/folke/trouble.nvim/commit/ff9fd51ab05398c83c2a0b384999d49269d95572))\n* make sorting keys configurable ([#190](https://github.com/folke/trouble.nvim/issues/190)) ([68d3dc5](https://github.com/folke/trouble.nvim/commit/68d3dc52fe49375fe556af69d1e91e0a88b67935))\n* next/previous API. Implements [#44](https://github.com/folke/trouble.nvim/issues/44) ([a2a7dbf](https://github.com/folke/trouble.nvim/commit/a2a7dbfefc5ebdf1a9c1d37e9df1d26a3b13c1cd))\n* option to automatically jump when there is only one result (fixes [#57](https://github.com/folke/trouble.nvim/issues/57)) ([#79](https://github.com/folke/trouble.nvim/issues/79)) ([09fafb2](https://github.com/folke/trouble.nvim/commit/09fafb2e01fbaa4fe6ecede10a7e7a738464deba))\n* **providers.lsp:** Add definitions support  ([#20](https://github.com/folke/trouble.nvim/issues/20)) ([a951198](https://github.com/folke/trouble.nvim/commit/a95119893c8dfd4b4bed42da97d601c25c7a495f))\n* sort files by current directory and prefer non-hidden ([ea9a5e3](https://github.com/folke/trouble.nvim/commit/ea9a5e331b70cf4011081c951015033f0079a0cc))\n* sort items by severity / filename / lnum / col ([4a45782](https://github.com/folke/trouble.nvim/commit/4a45782db943f95500b61ffce187bf4cada954ae))\n* sort results by row and column isntead of just row ([#118](https://github.com/folke/trouble.nvim/issues/118)) ([5897b09](https://github.com/folke/trouble.nvim/commit/5897b09933731298382e86a5cf4d1a4861630873))\n* **telescope provider:** (Smart) multiselect ([#39](https://github.com/folke/trouble.nvim/issues/39)) ([45ff198](https://github.com/folke/trouble.nvim/commit/45ff198f4d436d256f02b14db9c817024c7fc85c))\n* Telescope support ([9c81e16](https://github.com/folke/trouble.nvim/commit/9c81e16adec697ffd0b694eb86e14cfee453917d))\n* use vim.notify for logging ([293118e](https://github.com/folke/trouble.nvim/commit/293118e195639c373a6a744621b9341e5e18f6e4))\n\n\n### Bug Fixes\n\n* Add nowait option to keymap ([#30](https://github.com/folke/trouble.nvim/issues/30)) ([4375f1f](https://github.com/folke/trouble.nvim/commit/4375f1f0b2457fcbb91d32de457e6e3b3bb7eba7))\n* added additional space between message and code ([aae12e7](https://github.com/folke/trouble.nvim/commit/aae12e7b23b3a2b8337ec5b1d6b7b4317aa3929b))\n* added compatibility to retrieve signs from vim.diagnostic ([dab82ef](https://github.com/folke/trouble.nvim/commit/dab82ef0f39893f50908881fdc5e96bfb1578ba1))\n* added suport for vim.diagnostic hl groups ([d25a8e6](https://github.com/folke/trouble.nvim/commit/d25a8e6779462127fb227397fa92b07bced8a6fe))\n* added support for new handler signatures (backward compatible with 0.5) ([87cae94](https://github.com/folke/trouble.nvim/commit/87cae946aee4798bee621ea6108224c08c218d69))\n* auto_open was broken. Fixed now [#29](https://github.com/folke/trouble.nvim/issues/29) ([a2f2b92](https://github.com/folke/trouble.nvim/commit/a2f2b9248bed41522d8caa3a7e9932981c4087ec))\n* better detection of the parent window ([4c5fd8a](https://github.com/folke/trouble.nvim/commit/4c5fd8abaf6058312ebe52f662ca002bf0aa9f77))\n* default to current window in jump_to_item ([#175](https://github.com/folke/trouble.nvim/issues/175)) ([ec24219](https://github.com/folke/trouble.nvim/commit/ec242197b1f72cabe17dfd61119c896f58bda672))\n* don't \"edit\" en existing buffer. Use \"buffer\" instead. ([#5](https://github.com/folke/trouble.nvim/issues/5), [#6](https://github.com/folke/trouble.nvim/issues/6)) ([abef115](https://github.com/folke/trouble.nvim/commit/abef1158c0ff236333f67f9f091e5d9ae67d6a89))\n* don't steal focus on auto_open. Fixes [#48](https://github.com/folke/trouble.nvim/issues/48) ([36b6813](https://github.com/folke/trouble.nvim/commit/36b6813a2103d85b469a61721b030903ddd8b3b3))\n* don't try to fetch sign for \"other\" ([5b50990](https://github.com/folke/trouble.nvim/commit/5b509904f8865bea7d09b7a686e139077a2484c6))\n* don't use file sorter for items without a valid filename ([20469be](https://github.com/folke/trouble.nvim/commit/20469be985143d024c460d95326ebeff9971d714))\n* dont advance two items at a time. Fixes https://github.com/folke/todo-comments.nvim/issues/39 ([7de8bc4](https://github.com/folke/trouble.nvim/commit/7de8bc46164ec1f787dee34b6843b61251b1ea91))\n* files without col/row should be set to col=1 and row=1 [#22](https://github.com/folke/trouble.nvim/issues/22) ([fcd5f1f](https://github.com/folke/trouble.nvim/commit/fcd5f1fc035ee3d9832c63a307247c09f25c9cd1))\n* filetype set too early ([#230](https://github.com/folke/trouble.nvim/issues/230)) ([c4da921](https://github.com/folke/trouble.nvim/commit/c4da921ba613aa6d6659dc18edc204c37e4b8833))\n* fixed auto_open swicth_to_parent. Fixes [#7](https://github.com/folke/trouble.nvim/issues/7) ([7cf1aa1](https://github.com/folke/trouble.nvim/commit/7cf1aa1195245d3098097bc3a2510dc358c87363))\n* give focus back to correct window when closing ([#72](https://github.com/folke/trouble.nvim/issues/72)) ([a736b8d](https://github.com/folke/trouble.nvim/commit/a736b8db9f49b8b49ac96fbab7f8e396032cfa37))\n* handle normal api calls to trouble as it should [#42](https://github.com/folke/trouble.nvim/issues/42) ([52b875d](https://github.com/folke/trouble.nvim/commit/52b875d1aaf88f32e9f070a0119190c3e65b51a5))\n* if grouping is off, decrease indent ([#140](https://github.com/folke/trouble.nvim/issues/140)) ([ed65f84](https://github.com/folke/trouble.nvim/commit/ed65f84abc4a1e5d8f368d7e02601fc0357ea15e))\n* lazy include telescope when needed ([7e3d4f9](https://github.com/folke/trouble.nvim/commit/7e3d4f9efc157bbfeb3e37837f8ded9289c48f25))\n* lsp diag creates ugly buffers for unopened files in the workspace. Fixed now ([91d1139](https://github.com/folke/trouble.nvim/commit/91d1139d85407b99bd4d2f6850200a793631679b))\n* lsp diagnostics codes ([dbbd523](https://github.com/folke/trouble.nvim/commit/dbbd523d91fe51e8421909147bf069b1ec780720))\n* lsp handler error log ([#95](https://github.com/folke/trouble.nvim/issues/95)) ([063aefd](https://github.com/folke/trouble.nvim/commit/063aefd69a8146e27cde860c9ddd807891e5a119))\n* **lsp:** avoid overwriting uri of result ([#60](https://github.com/folke/trouble.nvim/issues/60)) ([655391c](https://github.com/folke/trouble.nvim/commit/655391c2f592ef61943b6325030333dfacc54757))\n* only use old hl groups when they exist (Fixes [#49](https://github.com/folke/trouble.nvim/issues/49)) ([d4ce76f](https://github.com/folke/trouble.nvim/commit/d4ce76fa82cdbd12dcf9dbfa682dae89b2a143ac))\n* possible vim.NIL on diagnostics code ([1faa347](https://github.com/folke/trouble.nvim/commit/1faa347a93748531b5e418d84276c93da21b86a7))\n* prevent segfault on closing ([756f09d](https://github.com/folke/trouble.nvim/commit/756f09de113a775ab16ba6d26c090616b40a999d))\n* properly close trouble window on close ([d10ee4b](https://github.com/folke/trouble.nvim/commit/d10ee4bc99b8e2bb842c2274316db400b197cca9))\n* properly exit when trouble is the last window. Fixes [#24](https://github.com/folke/trouble.nvim/issues/24) ([2b27b96](https://github.com/folke/trouble.nvim/commit/2b27b96c7893ac534ba0cbfc95d52c6c609a0b20))\n* remove useless \"no results\" notification ([#164](https://github.com/folke/trouble.nvim/issues/164)) ([da61737](https://github.com/folke/trouble.nvim/commit/da61737d860ddc12f78e638152834487eabf0ee5)), closes [#154](https://github.com/folke/trouble.nvim/issues/154)\n* removed space betweend rendering of source + code ([b676029](https://github.com/folke/trouble.nvim/commit/b6760291874d078668f4ff04d78acc0670536ca9))\n* removed unused plenary require. Fixes [#1](https://github.com/folke/trouble.nvim/issues/1) ([1ff45e2](https://github.com/folke/trouble.nvim/commit/1ff45e274de32e816b891b1ca12f73f73b58a604))\n* replace possible newlines in rendered text ([08d068f](https://github.com/folke/trouble.nvim/commit/08d068fb1668b7f898af721cbc8a1ae72ddf6565))\n* restore item indentation ([7c93271](https://github.com/folke/trouble.nvim/commit/7c93271e7a6a147b8f4342f5b377fa863419846f))\n* set buftype before filetype ([#67](https://github.com/folke/trouble.nvim/issues/67)) ([169b2ec](https://github.com/folke/trouble.nvim/commit/169b2ec3a4d0cac01f22cc8f7332f1d0a11f1fa4))\n* set EndOfBuffer to LspTroubleNormal and hide ~ [#23](https://github.com/folke/trouble.nvim/issues/23) ([7d67f34](https://github.com/folke/trouble.nvim/commit/7d67f34d92b3b52ca63c84f929751d98b3f56b63))\n* set nowrap for the trouble window. Fixes [#69](https://github.com/folke/trouble.nvim/issues/69) ([51dd917](https://github.com/folke/trouble.nvim/commit/51dd9175eb506b026189c70f81823dfa77defe86))\n* set the filetype lastly so autocmd's can override options from it ([#126](https://github.com/folke/trouble.nvim/issues/126)) ([b5353dd](https://github.com/folke/trouble.nvim/commit/b5353ddcd09bd7e93d6f934149d25792d455a8fb))\n* show warning when icons=true but devicons is not installed ([7aabea5](https://github.com/folke/trouble.nvim/commit/7aabea5cca2d51ba5432c988fe84ff9d3644637a))\n* support LocationLink ([#94](https://github.com/folke/trouble.nvim/issues/94)) ([7f3761b](https://github.com/folke/trouble.nvim/commit/7f3761b6dbadd682a20bd1ff4cb588985c14c9a0))\n* typos ([#55](https://github.com/folke/trouble.nvim/issues/55)) ([059ea2b](https://github.com/folke/trouble.nvim/commit/059ea2b999171f50019291ee776dd496799fdf3a))\n* use deprecated vim.lsp.diagnostics for now ([afb300f](https://github.com/folke/trouble.nvim/commit/afb300f18c09f7b474783aa12eb680ea59785b46))\n* use new DiagnosticChanged event ([#127](https://github.com/folke/trouble.nvim/issues/127)) ([4d0a711](https://github.com/folke/trouble.nvim/commit/4d0a711e7432eed022611ce385f3a7714e81f63b)), closes [#122](https://github.com/folke/trouble.nvim/issues/122)\n* use vim.diagnostic instead of vim.lsp.diagnostic when available ([a2e2e7b](https://github.com/folke/trouble.nvim/commit/a2e2e7b53f389f84477a1a11c086c9a379af702e))\n* workspace and document diagnostics were switched around ([1fa8469](https://github.com/folke/trouble.nvim/commit/1fa84691236d16a2d1c12707c1fbc54060c910f7))\n\n\n### Performance Improvements\n\n* debounce auto refresh when diagnostics get updated ([068476d](https://github.com/folke/trouble.nvim/commit/068476db8576e5b32acf20df040e7fca032cd11d))\n* much faster async preview ([2c9b319](https://github.com/folke/trouble.nvim/commit/2c9b3195a7fa8cfc19a368666c9f83fd7a20a482))\n* only fetch line when needed. Fixes [#26](https://github.com/folke/trouble.nvim/issues/26) ([52f18fd](https://github.com/folke/trouble.nvim/commit/52f18fd6bea57af54265247a3ec39f19a31adce3))\n* only update diagnostics once when window changes ([d965d22](https://github.com/folke/trouble.nvim/commit/d965d22ee37e50be0ab32f6a5987a8cd88206f10))\n* prevent nested loading of preview [#2](https://github.com/folke/trouble.nvim/issues/2) ([b20a784](https://github.com/folke/trouble.nvim/commit/b20a7844a035cf6795270db575ad8c4db2a774c9))\n* use vim.lsp.util.get_line to get line instad of bufload ([607b1d5](https://github.com/folke/trouble.nvim/commit/607b1d5bbfdbd19242659415746b5e62f5ddfb94))\n\n\n### Code Refactoring\n\n* removed deprecated commands ([dd89ad9](https://github.com/folke/trouble.nvim/commit/dd89ad9ebb63e131098ff04857f8598eb88d8d79))\n* renamed use_lsp_diagnostic_signs to use_diagnostic_signs ([9db77e1](https://github.com/folke/trouble.nvim/commit/9db77e194d848744139673aa246efa00fbcba982))\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "# 🚦 Trouble\n\nA pretty list for showing diagnostics, references, telescope results, quickfix and location lists to help you solve all the trouble your code is causing.\n\n![image](https://github.com/folke/trouble.nvim/assets/292349/481bc1f7-cb93-432d-8ab6-f54044334b96)\n\n## ✨ Features\n\n- Diagnostics\n- LSP references\n- LSP implementations\n- LSP definitions\n- LSP type definitions\n- LSP Document Symbols\n- LSP Incoming/Outgoing calls\n- quickfix list\n- location list\n- [Telescope](https://github.com/nvim-telescope/telescope.nvim) search results\n- [fzf-lua](https://github.com/ibhagwan/fzf-lua) results\n\n## 📰 What's new?\n\nThis is a full rewrite of the original **trouble.nvim**.\n\nThe new version is much more flexible and powerful,\nwith a lot of new features and improvements:\n\n- multiple trouble windows at the same time\n- LSP document symbols\n- LSP incoming/outgoing calls\n- lots of options to configure trouble windows (floats or splits)\n- `focus` option to focus the trouble window when opened (or not)\n- `follow` option to follow the item under the cursor\n- `pinned` option to pin the buffer as the source for the opened trouble window\n- full tree views of anything\n- highly configurable views with custom formatters, filters, and sorters\n- show multiple sections in the same view\n- multi-line messages\n- prettier and configurable indent guides\n- tree view that follows the natural hierarchy of the items (like document symbols, or file structure)\n- expansive API and `Trouble` command\n- trouble `modes` to define custom views\n- statusline component (useful with document symbols)\n\n## ⚡️ Requirements\n\n- Neovim >= 0.9.2\n- Neovim >= 0.10.0 **OR** the `markdown` and `markdown_inline` [nvim-treesitter](https://github.com/nvim-treesitter/nvim-treesitter) parsers\n- Properly configured Neovim LSP client\n- [nvim-web-devicons](https://github.com/nvim-tree/nvim-web-devicons) is optional to enable file icons\n- a theme with properly configured highlight groups for Neovim Diagnostics\n- a [patched font](https://www.nerdfonts.com/) for the default severity and fold icons\n\n## 📦 Installation\n\nInstall the plugin with your preferred package manager:\n\n### [lazy.nvim](https://github.com/folke/lazy.nvim)\n\n```lua\n{\n  \"folke/trouble.nvim\",\n  opts = {}, -- for default options, refer to the configuration section for custom setup.\n  cmd = \"Trouble\",\n  keys = {\n    {\n      \"<leader>xx\",\n      \"<cmd>Trouble diagnostics toggle<cr>\",\n      desc = \"Diagnostics (Trouble)\",\n    },\n    {\n      \"<leader>xX\",\n      \"<cmd>Trouble diagnostics toggle filter.buf=0<cr>\",\n      desc = \"Buffer Diagnostics (Trouble)\",\n    },\n    {\n      \"<leader>cs\",\n      \"<cmd>Trouble symbols toggle focus=false<cr>\",\n      desc = \"Symbols (Trouble)\",\n    },\n    {\n      \"<leader>cl\",\n      \"<cmd>Trouble lsp toggle focus=false win.position=right<cr>\",\n      desc = \"LSP Definitions / references / ... (Trouble)\",\n    },\n    {\n      \"<leader>xL\",\n      \"<cmd>Trouble loclist toggle<cr>\",\n      desc = \"Location List (Trouble)\",\n    },\n    {\n      \"<leader>xQ\",\n      \"<cmd>Trouble qflist toggle<cr>\",\n      desc = \"Quickfix List (Trouble)\",\n    },\n  },\n}\n```\n\n## ⚙️ Configuration\n\n### Setup\n\n**Trouble** is highly configurable. Please refer to the default settings below.\n\n<details><summary>Default Settings</summary>\n\n<!-- config:start -->\n\n```lua\n---@class trouble.Mode: trouble.Config,trouble.Section.spec\n---@field desc? string\n---@field sections? string[]\n\n---@class trouble.Config\n---@field mode? string\n---@field config? fun(opts:trouble.Config)\n---@field formatters? table<string,trouble.Formatter> custom formatters\n---@field filters? table<string, trouble.FilterFn> custom filters\n---@field sorters? table<string, trouble.SorterFn> custom sorters\nlocal defaults = {\n  auto_close = false, -- auto close when there are no items\n  auto_open = false, -- auto open when there are items\n  auto_preview = true, -- automatically open preview when on an item\n  auto_refresh = true, -- auto refresh when open\n  auto_jump = false, -- auto jump to the item when there's only one\n  focus = false, -- Focus the window when opened\n  restore = true, -- restores the last location in the list when opening\n  follow = true, -- Follow the current item\n  indent_guides = true, -- show indent guides\n  max_items = 200, -- limit number of items that can be displayed per section\n  multiline = true, -- render multi-line messages\n  pinned = false, -- When pinned, the opened trouble window will be bound to the current buffer\n  warn_no_results = true, -- show a warning when there are no results\n  open_no_results = false, -- open the trouble window when there are no results\n  ---@type trouble.Window.opts\n  win = {}, -- window options for the results window. Can be a split or a floating window.\n  -- Window options for the preview window. Can be a split, floating window,\n  -- or `main` to show the preview in the main editor window.\n  ---@type trouble.Window.opts\n  preview = {\n    type = \"main\",\n    -- when a buffer is not yet loaded, the preview window will be created\n    -- in a scratch buffer with only syntax highlighting enabled.\n    -- Set to false, if you want the preview to always be a real loaded buffer.\n    scratch = true,\n  },\n  -- Throttle/Debounce settings. Should usually not be changed.\n  ---@type table<string, number|{ms:number, debounce?:boolean}>\n  throttle = {\n    refresh = 20, -- fetches new data when needed\n    update = 10, -- updates the window\n    render = 10, -- renders the window\n    follow = 100, -- follows the current item\n    preview = { ms = 100, debounce = true }, -- shows the preview for the current item\n  },\n  -- Key mappings can be set to the name of a builtin action,\n  -- or you can define your own custom action.\n  ---@type table<string, trouble.Action.spec|false>\n  keys = {\n    [\"?\"] = \"help\",\n    r = \"refresh\",\n    R = \"toggle_refresh\",\n    q = \"close\",\n    o = \"jump_close\",\n    [\"<esc>\"] = \"cancel\",\n    [\"<cr>\"] = \"jump\",\n    [\"<2-leftmouse>\"] = \"jump\",\n    [\"<c-s>\"] = \"jump_split\",\n    [\"<c-v>\"] = \"jump_vsplit\",\n    -- go down to next item (accepts count)\n    -- j = \"next\",\n    [\"}\"] = \"next\",\n    [\"]]\"] = \"next\",\n    -- go up to prev item (accepts count)\n    -- k = \"prev\",\n    [\"{\"] = \"prev\",\n    [\"[[\"] = \"prev\",\n    dd = \"delete\",\n    d = { action = \"delete\", mode = \"v\" },\n    i = \"inspect\",\n    p = \"preview\",\n    P = \"toggle_preview\",\n    zo = \"fold_open\",\n    zO = \"fold_open_recursive\",\n    zc = \"fold_close\",\n    zC = \"fold_close_recursive\",\n    za = \"fold_toggle\",\n    zA = \"fold_toggle_recursive\",\n    zm = \"fold_more\",\n    zM = \"fold_close_all\",\n    zr = \"fold_reduce\",\n    zR = \"fold_open_all\",\n    zx = \"fold_update\",\n    zX = \"fold_update_all\",\n    zn = \"fold_disable\",\n    zN = \"fold_enable\",\n    zi = \"fold_toggle_enable\",\n    gb = { -- example of a custom action that toggles the active view filter\n      action = function(view)\n        view:filter({ buf = 0 }, { toggle = true })\n      end,\n      desc = \"Toggle Current Buffer Filter\",\n    },\n    s = { -- example of a custom action that toggles the severity\n      action = function(view)\n        local f = view:get_filter(\"severity\")\n        local severity = ((f and f.filter.severity or 0) + 1) % 5\n        view:filter({ severity = severity }, {\n          id = \"severity\",\n          template = \"{hl:Title}Filter:{hl} {severity}\",\n          del = severity == 0,\n        })\n      end,\n      desc = \"Toggle Severity Filter\",\n    },\n  },\n  ---@type table<string, trouble.Mode>\n  modes = {\n    -- sources define their own modes, which you can use directly,\n    -- or override like in the example below\n    lsp_references = {\n      -- some modes are configurable, see the source code for more details\n      params = {\n        include_declaration = true,\n      },\n    },\n    -- The LSP base mode for:\n    -- * lsp_definitions, lsp_references, lsp_implementations\n    -- * lsp_type_definitions, lsp_declarations, lsp_command\n    lsp_base = {\n      params = {\n        -- don't include the current location in the results\n        include_current = false,\n      },\n    },\n    -- more advanced example that extends the lsp_document_symbols\n    symbols = {\n      desc = \"document symbols\",\n      mode = \"lsp_document_symbols\",\n      focus = false,\n      win = { position = \"right\" },\n      filter = {\n        -- remove Package since luals uses it for control flow structures\n        [\"not\"] = { ft = \"lua\", kind = \"Package\" },\n        any = {\n          -- all symbol kinds for help / markdown files\n          ft = { \"help\", \"markdown\" },\n          -- default set of symbol kinds\n          kind = {\n            \"Class\",\n            \"Constructor\",\n            \"Enum\",\n            \"Field\",\n            \"Function\",\n            \"Interface\",\n            \"Method\",\n            \"Module\",\n            \"Namespace\",\n            \"Package\",\n            \"Property\",\n            \"Struct\",\n            \"Trait\",\n          },\n        },\n      },\n    },\n  },\n  icons = {\n    ---@type trouble.Indent.symbols\n    indent = {\n      top           = \"│ \",\n      middle        = \"├╴\",\n      last          = \"└╴\",\n      -- last          = \"-╴\",\n      -- last       = \"╰╴\", -- rounded\n      fold_open     = \" \",\n      fold_closed   = \" \",\n      ws            = \"  \",\n    },\n    folder_closed   = \" \",\n    folder_open     = \" \",\n    kinds = {\n      Array         = \" \",\n      Boolean       = \"󰨙 \",\n      Class         = \" \",\n      Constant      = \"󰏿 \",\n      Constructor   = \" \",\n      Enum          = \" \",\n      EnumMember    = \" \",\n      Event         = \" \",\n      Field         = \" \",\n      File          = \" \",\n      Function      = \"󰊕 \",\n      Interface     = \" \",\n      Key           = \" \",\n      Method        = \"󰊕 \",\n      Module        = \" \",\n      Namespace     = \"󰦮 \",\n      Null          = \" \",\n      Number        = \"󰎠 \",\n      Object        = \" \",\n      Operator      = \" \",\n      Package       = \" \",\n      Property      = \" \",\n      String        = \" \",\n      Struct        = \"󰆼 \",\n      TypeParameter = \" \",\n      Variable      = \"󰀫 \",\n    },\n  },\n}\n```\n\n<!-- config:end -->\n\n</details>\n\nMake sure to check the [Examples](/docs/examples.md)!\n\n## 🚀 Usage\n\n### Commands\n\nThe **Trouble** command is a wrapper around the **Trouble** API.\nIt can do anything the regular API can do.\n\n- `Trouble [mode] [action] [options]`\n\nSome examples:\n\n- Toggle diagnostics for the current buffer and stay in the current window:\n  - `Trouble diagnostics toggle focus=false filter.buf=0`\n- Show document symbols on the right of the current window.\n  Keep the document symbols in sync with the buffer you started the command in.\n  - `Trouble symbols toggle pinned=true win.relative=win win.position=right`\n- You can use **lua** code in the options for the `Trouble` command.\n  The examples below all do the same thing.\n  - `Trouble diagnostics filter.severity=vim.diagnostic.severity.ERROR`\n  - `Trouble diagnostics filter.severity = vim.diagnostic.severity.ERROR`\n  - `Trouble diagnostics filter = { severity=vim.diagnostic.severity.ERROR }`\n- Merging of nested options, with or without quoting strings:\n  - `Trouble diagnostics win.type = split win.position=right`\n  - `Trouble diagnostics win = { type = split, position=right}`\n  - `Trouble diagnostics win = { type = \"split\", position='right'}`\n\nPlease refer to the API section for more information on the available actions and options.\n\nModes:\n\n<!-- modes:start -->\n\n- **diagnostics**: diagnostics\n- **fzf**: FzfLua results previously opened with `require('trouble.sources.fzf').open()`.\n- **fzf_files**: FzfLua results previously opened with `require('trouble.sources.fzf').open()`.\n- **loclist**: Location List\n- **lsp**: LSP definitions, references, implementations, type definitions, and declarations\n- **lsp_command**: command\n- **lsp_declarations**: declarations\n- **lsp_definitions**: definitions\n- **lsp_document_symbols**: document symbols\n- **lsp_implementations**: implementations\n- **lsp_incoming_calls**: Incoming Calls\n- **lsp_outgoing_calls**: Outgoing Calls\n- **lsp_references**: references\n- **lsp_type_definitions**: type definitions\n- **qflist**: Quickfix List\n- **quickfix**: Quickfix List\n- **snacks**: Snacks results previously opened with `require('trouble.sources.snacks').open()`.\n- **snacks_files**: Snacks results previously opened with `require('trouble.sources.snacks').open()`.\n- **symbols**: document symbols\n- **telescope**: Telescope results previously opened with `require('trouble.sources.telescope').open()`.\n- **telescope_files**: Telescope results previously opened with `require('trouble.sources.telescope').open()`.\n\n<!-- modes:end -->\n\n### Filters\n\nPlease refer to the [filter docs](docs/filter.md) for more information examples on filters.\n\n### API\n\nYou can use the following functions in your keybindings:\n\n<details><summary>API</summary>\n\n<!-- api:start -->\n\n```lua\n-- Opens trouble with the given mode.\n-- If a view is already open with the same mode,\n-- it will be focused unless `opts.focus = false`.\n-- When a view is already open and `opts.new = true`,\n-- a new view will be created.\n---@param opts? trouble.Mode | { new?: boolean, refresh?: boolean } | string\n---@return trouble.View?\nrequire(\"trouble\").open(opts)\n\n-- Closes the last open view matching the filter.\n---@param opts? trouble.Mode|string\n---@return trouble.View?\nrequire(\"trouble\").close(opts)\n\n-- Toggle the view with the given mode.\n---@param opts? trouble.Mode|string\n---@return trouble.View?\nrequire(\"trouble\").toggle(opts)\n\n-- Returns true if there is an open view matching the mode.\n---@param opts? trouble.Mode|string\nrequire(\"trouble\").is_open(opts)\n\n-- Refresh all open views. Normally this is done automatically,\n-- unless you disabled auto refresh.\n---@param opts? trouble.Mode|string\nrequire(\"trouble\").refresh(opts)\n\n-- Get all items from the active view for a given mode.\n---@param opts? trouble.Mode|string\nrequire(\"trouble\").get_items(opts)\n\n-- Renders a trouble list as a statusline component.\n-- Check the docs for examples.\n---@param opts? trouble.Mode|string|{hl_group?:string}\n---@return {get: (fun():string), has: (fun():boolean)}\nrequire(\"trouble\").statusline(opts)\n\n-- Closes the preview and goes to the main window.\n-- The Trouble window is not closed.\n---@param opts? trouble.Mode | { new? : boolean } | string\n---@return trouble.View\nrequire(\"trouble\").cancel(opts)\n\n-- Open the preview\n---@param opts? trouble.Mode | { new? : boolean } | string\n---@return trouble.View\nrequire(\"trouble\").delete(opts)\n\n-- filter\n---@param opts? trouble.Mode | { new? : boolean } | string\n---@return trouble.View\nrequire(\"trouble\").filter(opts)\n\n-- Go to the first item\n---@param opts? trouble.Mode | { new? : boolean } | string\n---@return trouble.View\nrequire(\"trouble\").first(opts)\n\n-- Focus the trouble window\n---@param opts? trouble.Mode | { new? : boolean } | string\n---@return trouble.View\nrequire(\"trouble\").focus(opts)\n\n-- Fold close \n---@param opts? trouble.Mode | { new? : boolean } | string\n---@return trouble.View\nrequire(\"trouble\").fold_close(opts)\n\n-- fold close all\n---@param opts? trouble.Mode | { new? : boolean } | string\n---@return trouble.View\nrequire(\"trouble\").fold_close_all(opts)\n\n-- Fold close recursive\n---@param opts? trouble.Mode | { new? : boolean } | string\n---@return trouble.View\nrequire(\"trouble\").fold_close_recursive(opts)\n\n-- fold disable\n---@param opts? trouble.Mode | { new? : boolean } | string\n---@return trouble.View\nrequire(\"trouble\").fold_disable(opts)\n\n-- fold enable\n---@param opts? trouble.Mode | { new? : boolean } | string\n---@return trouble.View\nrequire(\"trouble\").fold_enable(opts)\n\n-- fold more\n---@param opts? trouble.Mode | { new? : boolean } | string\n---@return trouble.View\nrequire(\"trouble\").fold_more(opts)\n\n-- Fold open \n---@param opts? trouble.Mode | { new? : boolean } | string\n---@return trouble.View\nrequire(\"trouble\").fold_open(opts)\n\n-- fold open all\n---@param opts? trouble.Mode | { new? : boolean } | string\n---@return trouble.View\nrequire(\"trouble\").fold_open_all(opts)\n\n-- Fold open recursive\n---@param opts? trouble.Mode | { new? : boolean } | string\n---@return trouble.View\nrequire(\"trouble\").fold_open_recursive(opts)\n\n-- fold reduce\n---@param opts? trouble.Mode | { new? : boolean } | string\n---@return trouble.View\nrequire(\"trouble\").fold_reduce(opts)\n\n-- Fold toggle \n---@param opts? trouble.Mode | { new? : boolean } | string\n---@return trouble.View\nrequire(\"trouble\").fold_toggle(opts)\n\n-- fold toggle enable\n---@param opts? trouble.Mode | { new? : boolean } | string\n---@return trouble.View\nrequire(\"trouble\").fold_toggle_enable(opts)\n\n-- Fold toggle recursive\n---@param opts? trouble.Mode | { new? : boolean } | string\n---@return trouble.View\nrequire(\"trouble\").fold_toggle_recursive(opts)\n\n-- fold update\n---@param opts? trouble.Mode | { new? : boolean } | string\n---@return trouble.View\nrequire(\"trouble\").fold_update(opts)\n\n-- fold update all\n---@param opts? trouble.Mode | { new? : boolean } | string\n---@return trouble.View\nrequire(\"trouble\").fold_update_all(opts)\n\n-- Show the help\n---@param opts? trouble.Mode | { new? : boolean } | string\n---@return trouble.View\nrequire(\"trouble\").help(opts)\n\n-- Dump the item to the console\n---@param opts? trouble.Mode | { new? : boolean } | string\n---@return trouble.View\nrequire(\"trouble\").inspect(opts)\n\n-- Jump to the item if on an item, otherwise fold the node\n---@param opts? trouble.Mode | { new? : boolean } | string\n---@return trouble.View\nrequire(\"trouble\").jump(opts)\n\n-- Jump to the item and close the trouble window\n---@param opts? trouble.Mode | { new? : boolean } | string\n---@return trouble.View\nrequire(\"trouble\").jump_close(opts)\n\n-- Jump to the item if on an item, otherwise do nothing\n---@param opts? trouble.Mode | { new? : boolean } | string\n---@return trouble.View\nrequire(\"trouble\").jump_only(opts)\n\n-- Open the item in a split\n---@param opts? trouble.Mode | { new? : boolean } | string\n---@return trouble.View\nrequire(\"trouble\").jump_split(opts)\n\n-- Open the item in a split and close the trouble window\n---@param opts? trouble.Mode | { new? : boolean } | string\n---@return trouble.View\nrequire(\"trouble\").jump_split_close(opts)\n\n-- Open the item in a vsplit\n---@param opts? trouble.Mode | { new? : boolean } | string\n---@return trouble.View\nrequire(\"trouble\").jump_vsplit(opts)\n\n-- Open the item in a vsplit and close the trouble window\n---@param opts? trouble.Mode | { new? : boolean } | string\n---@return trouble.View\nrequire(\"trouble\").jump_vsplit_close(opts)\n\n-- Go to the last item\n---@param opts? trouble.Mode | { new? : boolean } | string\n---@return trouble.View\nrequire(\"trouble\").last(opts)\n\n-- Go to the next item\n---@param opts? trouble.Mode | { new? : boolean } | string\n---@return trouble.View\nrequire(\"trouble\").next(opts)\n\n-- Go to the previous item\n---@param opts? trouble.Mode | { new? : boolean } | string\n---@return trouble.View\nrequire(\"trouble\").prev(opts)\n\n-- Open the preview\n---@param opts? trouble.Mode | { new? : boolean } | string\n---@return trouble.View\nrequire(\"trouble\").preview(opts)\n\n-- Refresh the trouble source\n---@param opts? trouble.Mode | { new? : boolean } | string\n---@return trouble.View\nrequire(\"trouble\").refresh(opts)\n\n-- Toggle the preview\n---@param opts? trouble.Mode | { new? : boolean } | string\n---@return trouble.View\nrequire(\"trouble\").toggle_preview(opts)\n\n-- Toggle the auto refresh\n---@param opts? trouble.Mode | { new? : boolean } | string\n---@return trouble.View\nrequire(\"trouble\").toggle_refresh(opts)\n```\n\n<!-- api:end -->\n\n</details>\n\n### Telescope\n\nYou can easily open any search results in **Trouble**, by defining a custom action:\n\n```lua\nlocal actions = require(\"telescope.actions\")\nlocal open_with_trouble = require(\"trouble.sources.telescope\").open\n\n-- Use this to add more results without clearing the trouble list\nlocal add_to_trouble = require(\"trouble.sources.telescope\").add\n\nlocal telescope = require(\"telescope\")\n\ntelescope.setup({\n  defaults = {\n    mappings = {\n      i = { [\"<c-t>\"] = open_with_trouble },\n      n = { [\"<c-t>\"] = open_with_trouble },\n    },\n  },\n})\n```\n\nWhen you open telescope, you can now hit `<c-t>` to open the results in **Trouble**\n\n### fzf-lua\n\nYou can easily open any search results in **Trouble**, by defining a custom action:\n\n```lua\nlocal config = require(\"fzf-lua.config\")\nlocal actions = require(\"trouble.sources.fzf\").actions\nconfig.defaults.actions.files[\"ctrl-t\"] = actions.open\n```\n\nWhen you open fzf-lua, you can now hit `<c-t>` to open the results in **Trouble**\n\n### Statusline Component\n\nExample for [lualine.nvim](https://github.com/nvim-lualine/lualine.nvim):\n\n```lua\n{\n  \"nvim-lualine/lualine.nvim\",\n  opts = function(_, opts)\n    local trouble = require(\"trouble\")\n    local symbols = trouble.statusline({\n      mode = \"lsp_document_symbols\",\n      groups = {},\n      title = false,\n      filter = { range = true },\n      format = \"{kind_icon}{symbol.name:Normal}\",\n      -- The following line is needed to fix the background color\n      -- Set it to the lualine section you want to use\n      hl_group = \"lualine_c_normal\",\n    })\n    table.insert(opts.sections.lualine_c, {\n      symbols.get,\n      cond = symbols.has,\n    })\n  end,\n}\n```\n\n## 🎨 Colors\n\nThe table below shows all the highlight groups defined for Trouble.\n\n<details><summary>Highlight Groups</summary>\n\n<!-- colors:start -->\n\n| Highlight Group | Default Group | Description |\n| --- | --- | --- |\n| **TroubleBasename** | ***TroubleFilename*** |  |\n| **TroubleCode** | ***Special*** |  |\n| **TroubleCount** | ***TabLineSel*** |  |\n| **TroubleDirectory** | ***Directory*** |  |\n| **TroubleFilename** | ***Directory*** |  |\n| **TroubleIconArray** | ***@punctuation.bracket*** |  |\n| **TroubleIconBoolean** | ***@boolean*** |  |\n| **TroubleIconClass** | ***@type*** |  |\n| **TroubleIconConstant** | ***@constant*** |  |\n| **TroubleIconConstructor** | ***@constructor*** |  |\n| **TroubleIconDirectory** | ***Special*** |  |\n| **TroubleIconEnum** | ***@lsp.type.enum*** |  |\n| **TroubleIconEnumMember** | ***@lsp.type.enumMember*** |  |\n| **TroubleIconEvent** | ***Special*** |  |\n| **TroubleIconField** | ***@variable.member*** |  |\n| **TroubleIconFile** | ***Normal*** |  |\n| **TroubleIconFunction** | ***@function*** |  |\n| **TroubleIconInterface** | ***@lsp.type.interface*** |  |\n| **TroubleIconKey** | ***@lsp.type.keyword*** |  |\n| **TroubleIconMethod** | ***@function.method*** |  |\n| **TroubleIconModule** | ***@module*** |  |\n| **TroubleIconNamespace** | ***@module*** |  |\n| **TroubleIconNull** | ***@constant.builtin*** |  |\n| **TroubleIconNumber** | ***@number*** |  |\n| **TroubleIconObject** | ***@constant*** |  |\n| **TroubleIconOperator** | ***@operator*** |  |\n| **TroubleIconPackage** | ***@module*** |  |\n| **TroubleIconProperty** | ***@property*** |  |\n| **TroubleIconString** | ***@string*** |  |\n| **TroubleIconStruct** | ***@lsp.type.struct*** |  |\n| **TroubleIconTypeParameter** | ***@lsp.type.typeParameter*** |  |\n| **TroubleIconVariable** | ***@variable*** |  |\n| **TroubleIndent** | ***LineNr*** |  |\n| **TroubleIndentFoldClosed** | ***CursorLineNr*** |  |\n| **TroubleIndentFoldOpen** | ***TroubleIndent*** |  |\n| **TroubleIndentLast** | ***TroubleIndent*** |  |\n| **TroubleIndentMiddle** | ***TroubleIndent*** |  |\n| **TroubleIndentTop** | ***TroubleIndent*** |  |\n| **TroubleIndentWs** | ***TroubleIndent*** |  |\n| **TroubleNormal** | ***NormalFloat*** |  |\n| **TroubleNormalNC** | ***NormalFloat*** |  |\n| **TroublePos** | ***LineNr*** |  |\n| **TroublePreview** | ***Visual*** |  |\n| **TroubleSource** | ***Comment*** |  |\n| **TroubleText** | ***Normal*** |  |\n\n<!-- colors:end -->\n\n</details>\n"
  },
  {
    "path": "doc/trouble.nvim-examples.txt",
    "content": "*trouble.nvim-examples.txt*                         trouble.nvim examples docs\n\n==============================================================================\nTable of Contents                    *trouble.nvim-examples-table-of-contents*\n\n1. Window Options                       |trouble.nvim-examples-window-options|\n  - Preview in small float top right|trouble.nvim-examples-window-options-preview-in-small-float-top-right|\n  - Preview in a split to the right of the trouble list|trouble.nvim-examples-window-options-preview-in-a-split-to-the-right-of-the-trouble-list|\n  - Make Trouble look like v2|trouble.nvim-examples-window-options-make-trouble-look-like-v2|\n2. Filtering                                 |trouble.nvim-examples-filtering|\n  - Diagnostics for the current buffer only|trouble.nvim-examples-filtering-diagnostics-for-the-current-buffer-only|\n  - Diagnostics for the current buffer and errors from the current project|trouble.nvim-examples-filtering-diagnostics-for-the-current-buffer-and-errors-from-the-current-project|\n  - Diagnostics Cascade  |trouble.nvim-examples-filtering-diagnostics-cascade|\n3. Other                                         |trouble.nvim-examples-other|\n  - Automatically Open Trouble Quickfix|trouble.nvim-examples-other-automatically-open-trouble-quickfix|\n  - Open Trouble Quickfix when the qf list opens|trouble.nvim-examples-other-open-trouble-quickfix-when-the-qf-list-opens|\n4. Links                                         |trouble.nvim-examples-links|\n\n==============================================================================\n1. Window Options                       *trouble.nvim-examples-window-options*\n\n\nPREVIEW IN SMALL FLOAT TOP RIGHT*trouble.nvim-examples-window-options-preview-in-small-float-top-right*\n\n>lua\n    {\n      modes = {\n        preview_float = {\n          mode = \"diagnostics\",\n          preview = {\n            type = \"float\",\n            relative = \"editor\",\n            border = \"rounded\",\n            title = \"Preview\",\n            title_pos = \"center\",\n            position = { 0, -2 },\n            size = { width = 0.3, height = 0.3 },\n            zindex = 200,\n          },\n        },\n      },\n    }\n<\n\n\nPREVIEW IN A SPLIT TO THE RIGHT OF THE TROUBLE LIST*trouble.nvim-examples-window-options-preview-in-a-split-to-the-right-of-the-trouble-list*\n\n>lua\n    {\n      modes = {\n        test = {\n          mode = \"diagnostics\",\n          preview = {\n            type = \"split\",\n            relative = \"win\",\n            position = \"right\",\n            size = 0.3,\n          },\n        },\n      },\n    }\n<\n\n\nMAKE TROUBLE LOOK LIKE V2*trouble.nvim-examples-window-options-make-trouble-look-like-v2*\n\n>lua\n    {\n      icons = {\n        indent = {\n          middle = \" \",\n          last = \" \",\n          top = \" \",\n          ws = \"│  \",\n        },\n      },\n      modes = {\n        diagnostics = {\n          groups = {\n            { \"filename\", format = \"{file_icon} {basename:Title} {count}\" },\n          },\n        },\n      },\n    }\n<\n\n\n==============================================================================\n2. Filtering                                 *trouble.nvim-examples-filtering*\n\n\nDIAGNOSTICS FOR THE CURRENT BUFFER ONLY*trouble.nvim-examples-filtering-diagnostics-for-the-current-buffer-only*\n\n>lua\n    {\n      modes = {\n        diagnostics_buffer = {\n          mode = \"diagnostics\", -- inherit from diagnostics mode\n          filter = { buf = 0 }, -- filter diagnostics to the current buffer\n        },\n      }\n    }\n<\n\n\nDIAGNOSTICS FOR THE CURRENT BUFFER AND ERRORS FROM THE CURRENT PROJECT*trouble.nvim-examples-filtering-diagnostics-for-the-current-buffer-and-errors-from-the-current-project*\n\n>lua\n    {\n      modes = {\n        mydiags = {\n          mode = \"diagnostics\", -- inherit from diagnostics mode\n          filter = {\n            any = {\n              buf = 0, -- current buffer\n              {\n                severity = vim.diagnostic.severity.ERROR, -- errors only\n                -- limit to files in the current project\n                function(item)\n                  return item.filename:find((vim.loop or vim.uv).cwd(), 1, true)\n                end,\n              },\n            },\n          },\n        }\n      }\n    }\n<\n\n\nDIAGNOSTICS CASCADE      *trouble.nvim-examples-filtering-diagnostics-cascade*\n\nThe following example shows how to create a new mode that shows only the most\nsevere diagnostics.\n\nOnce those are resolved, less severe diagnostics will be shown.\n\n>lua\n    {\n      modes = {\n        cascade = {\n          mode = \"diagnostics\", -- inherit from diagnostics mode\n          filter = function(items)\n            local severity = vim.diagnostic.severity.HINT\n            for _, item in ipairs(items) do\n              severity = math.min(severity, item.severity)\n            end\n            return vim.tbl_filter(function(item)\n              return item.severity == severity\n            end, items)\n          end,\n        },\n      },\n    }\n<\n\n\n==============================================================================\n3. Other                                         *trouble.nvim-examples-other*\n\n\nAUTOMATICALLY OPEN TROUBLE QUICKFIX*trouble.nvim-examples-other-automatically-open-trouble-quickfix*\n\n>lua\n    vim.api.nvim_create_autocmd(\"QuickFixCmdPost\", {\n      callback = function()\n        vim.cmd([[Trouble qflist open]])\n      end,\n    })\n<\n\nTest with something like `:silent grep vim %`\n\n\nOPEN TROUBLE QUICKFIX WHEN THE QF LIST OPENS*trouble.nvim-examples-other-open-trouble-quickfix-when-the-qf-list-opens*\n\n\n  This is **NOT** recommended, since you won’t be able to use the quickfix list\n  for other things.\n>lua\n    \n    vim.api.nvim_create_autocmd(\"BufRead\", {\n      callback = function(ev)\n        if vim.bo[ev.buf].buftype == \"quickfix\" then\n          vim.schedule(function()\n            vim.cmd([[cclose]])\n            vim.cmd([[Trouble qflist open]])\n          end)\n        end\n      end,\n    })\n<\n\n==============================================================================\n4. Links                                         *trouble.nvim-examples-links*\n\n1. *image*: https://github.com/folke/trouble.nvim/assets/292349/f422b8fd-579e-427b-87d3-62daab85d2e0\n2. *image*: https://github.com/folke/trouble.nvim/assets/292349/adfa02df-b3dd-4c90-af3c-41683c0b5356\n\nGenerated by panvimdoc <https://github.com/kdheepak/panvimdoc>\n\nvim:tw=78:ts=8:noet:ft=help:norl:\n"
  },
  {
    "path": "doc/trouble.nvim-filter.txt",
    "content": "*trouble.nvim-filter.txt*                             trouble.nvim filter docs\n\n==============================================================================\nTable of Contents                      *trouble.nvim-filter-table-of-contents*\n\n1. Examples                                     |trouble.nvim-filter-examples|\n2. Advanced examples                   |trouble.nvim-filter-advanced-examples|\n  - The not filter      |trouble.nvim-filter-advanced-examples-the-not-filter|\n  - The any filter      |trouble.nvim-filter-advanced-examples-the-any-filter|\n3. Item attributes                       |trouble.nvim-filter-item-attributes|\n\n==============================================================================\n1. Examples                                     *trouble.nvim-filter-examples*\n\nA simple filter is a table whose keys are item attributes. The following filter\nkeeps items with attribute `buf = 0` **and** `ft = 'lua'`, i.e., diagnostics\nwith severity error to the current buffer when its filetype is `lua`.\n\n>lua\n    {\n      modes = {\n        my_diagnostics = {\n          mode = 'diagnostics',\n          filter = { buf = 0, ft = 'lua' },\n        },\n      },\n    }\n<\n\nA filter may be a function that takes `items` as parameter. The following\nfilter keeps items with severity `HINT`\n\n>lua\n    {\n      modes = {\n        my_diagnostics = {\n          mode = 'diagnostics',\n          filter = function(items)\n            return vim.tbl_filter(function(item)\n              return item.severity == vim.diagnostic.severity.HINT\n            end, items)\n          end,\n        },\n      },\n    }\n<\n\n\n==============================================================================\n2. Advanced examples                   *trouble.nvim-filter-advanced-examples*\n\n\nTHE NOT FILTER          *trouble.nvim-filter-advanced-examples-the-not-filter*\n\nThe `not` negates filter results. The following filter **removes** diagnostics\nwith severity `INFO`\n\n>lua\n    {\n      modes = {\n        my_diagnostics = {\n          mode = 'diagnostics',\n          filter = {\n            ['not'] = { severity = vim.diagnostic.severity.INFO },\n          },\n        },\n      },\n    }\n<\n\n\nTHE ANY FILTER          *trouble.nvim-filter-advanced-examples-the-any-filter*\n\nThe `any` filter provides logical disjunction. The following filter **keeps**\ndiagnostics for the current buffer **or** diagnostics with severity `ERROR` for\nthe current project.\n\n>lua\n    {\n      modes = {\n        my_diagnostics = {\n          mode = 'diagnostics',\n          filter = {\n            any = {\n              buf = 0,\n              {\n                severity = vim.diagnostic.severity.ERROR,\n                function(item)\n                  return item.filename:find((vim.loop or vim.uv).cwd(), 1, true)\n                end,\n              },\n            },\n          },\n        },\n      },\n    }\n<\n\n\n==============================================================================\n3. Item attributes                       *trouble.nvim-filter-item-attributes*\n\nItem attributes are documented in `lua/trouble/item.lua`\n\n  ------------------------------------------------------------------------------\n  Name       Type                       Description\n  ---------- -------------------------- ----------------------------------------\n  any        Logical or                 Filter result disjunction\n\n  basename   string                     File name.\n\n  buf        number                     Buffer id.\n\n  dirname    string                     Directory path.\n\n  filename   string                     Full file path.\n\n  ft         string or string[]         File types.\n\n  kind       string                     Symbol kind. See :h symbol.\n\n  not        Logical not                Filter result negation.\n\n  pos        {[1]:number, [2]:number}   Item position.\n\n  severity   number                     Diagnostic severity. See\n                                        :h diagnostic-severity.\n\n  source     string                     Item source.\n  ------------------------------------------------------------------------------\n\nGenerated by panvimdoc <https://github.com/kdheepak/panvimdoc>\n\nvim:tw=78:ts=8:noet:ft=help:norl:\n"
  },
  {
    "path": "doc/trouble.nvim.txt",
    "content": "*trouble.nvim.txt*                                           trouble.nvim docs\n\n==============================================================================\nTable of Contents                             *trouble.nvim-table-of-contents*\n\n1. Trouble                                              |trouble.nvim-trouble|\n  - Features                                   |trouble.nvim-trouble-features|\n  - What’s new?                         |trouble.nvim-trouble-what’s-new?|\n  - Requirements                           |trouble.nvim-trouble-requirements|\n  - Installation                           |trouble.nvim-trouble-installation|\n  - Configuration                         |trouble.nvim-trouble-configuration|\n  - Usage                                         |trouble.nvim-trouble-usage|\n  - Colors                                       |trouble.nvim-trouble-colors|\n2. Links                                                  |trouble.nvim-links|\n\n==============================================================================\n1. Trouble                                              *trouble.nvim-trouble*\n\nA pretty list for showing diagnostics, references, telescope results, quickfix\nand location lists to help you solve all the trouble your code is causing.\n\n\nFEATURES                                       *trouble.nvim-trouble-features*\n\n- Diagnostics\n- LSP references\n- LSP implementations\n- LSP definitions\n- LSP type definitions\n- LSP Document Symbols\n- LSP Incoming/Outgoing calls\n- quickfix list\n- location list\n- Telescope <https://github.com/nvim-telescope/telescope.nvim> search results\n- fzf-lua <https://github.com/ibhagwan/fzf-lua> results\n\n\nWHAT’S NEW?                             *trouble.nvim-trouble-what’s-new?*\n\nThis is a full rewrite of the original **trouble.nvim**.\n\nThe new version is much more flexible and powerful, with a lot of new features\nand improvements:\n\n- multiple trouble windows at the same time\n- LSP document symbols\n- LSP incoming/outgoing calls\n- lots of options to configure trouble windows (floats or splits)\n- `focus` option to focus the trouble window when opened (or not)\n- `follow` option to follow the item under the cursor\n- `pinned` option to pin the buffer as the source for the opened trouble window\n- full tree views of anything\n- highly configurable views with custom formatters, filters, and sorters\n- show multiple sections in the same view\n- multi-line messages\n- prettier and configurable indent guides\n- tree view that follows the natural hierarchy of the items (like document symbols, or file structure)\n- expansive API and `Trouble` command\n- trouble `modes` to define custom views\n- statusline component (useful with document symbols)\n\n\nREQUIREMENTS                               *trouble.nvim-trouble-requirements*\n\n- Neovim >= 0.9.2\n- Neovim >= 0.10.0 **OR** the `markdown` and `markdown_inline` nvim-treesitter <https://github.com/nvim-treesitter/nvim-treesitter> parsers\n- Properly configured Neovim LSP client\n- nvim-web-devicons <https://github.com/nvim-tree/nvim-web-devicons> is optional to enable file icons\n- a theme with properly configured highlight groups for Neovim Diagnostics\n- a patched font <https://www.nerdfonts.com/> for the default severity and fold icons\n\n\nINSTALLATION                               *trouble.nvim-trouble-installation*\n\nInstall the plugin with your preferred package manager:\n\n\nLAZY.NVIM ~\n\n>lua\n    {\n      \"folke/trouble.nvim\",\n      opts = {}, -- for default options, refer to the configuration section for custom setup.\n      cmd = \"Trouble\",\n      keys = {\n        {\n          \"<leader>xx\",\n          \"<cmd>Trouble diagnostics toggle<cr>\",\n          desc = \"Diagnostics (Trouble)\",\n        },\n        {\n          \"<leader>xX\",\n          \"<cmd>Trouble diagnostics toggle filter.buf=0<cr>\",\n          desc = \"Buffer Diagnostics (Trouble)\",\n        },\n        {\n          \"<leader>cs\",\n          \"<cmd>Trouble symbols toggle focus=false<cr>\",\n          desc = \"Symbols (Trouble)\",\n        },\n        {\n          \"<leader>cl\",\n          \"<cmd>Trouble lsp toggle focus=false win.position=right<cr>\",\n          desc = \"LSP Definitions / references / ... (Trouble)\",\n        },\n        {\n          \"<leader>xL\",\n          \"<cmd>Trouble loclist toggle<cr>\",\n          desc = \"Location List (Trouble)\",\n        },\n        {\n          \"<leader>xQ\",\n          \"<cmd>Trouble qflist toggle<cr>\",\n          desc = \"Quickfix List (Trouble)\",\n        },\n      },\n    }\n<\n\n\nCONFIGURATION                             *trouble.nvim-trouble-configuration*\n\n\nSETUP ~\n\n**Trouble** is highly configurable. Please refer to the default settings below.\n\nDefault Settings ~\n\n>lua\n    ---@class trouble.Mode: trouble.Config,trouble.Section.spec\n    ---@field desc? string\n    ---@field sections? string[]\n    \n    ---@class trouble.Config\n    ---@field mode? string\n    ---@field config? fun(opts:trouble.Config)\n    ---@field formatters? table<string,trouble.Formatter> custom formatters\n    ---@field filters? table<string, trouble.FilterFn> custom filters\n    ---@field sorters? table<string, trouble.SorterFn> custom sorters\n    local defaults = {\n      auto_close = false, -- auto close when there are no items\n      auto_open = false, -- auto open when there are items\n      auto_preview = true, -- automatically open preview when on an item\n      auto_refresh = true, -- auto refresh when open\n      auto_jump = false, -- auto jump to the item when there's only one\n      focus = false, -- Focus the window when opened\n      restore = true, -- restores the last location in the list when opening\n      follow = true, -- Follow the current item\n      indent_guides = true, -- show indent guides\n      max_items = 200, -- limit number of items that can be displayed per section\n      multiline = true, -- render multi-line messages\n      pinned = false, -- When pinned, the opened trouble window will be bound to the current buffer\n      warn_no_results = true, -- show a warning when there are no results\n      open_no_results = false, -- open the trouble window when there are no results\n      ---@type trouble.Window.opts\n      win = {}, -- window options for the results window. Can be a split or a floating window.\n      -- Window options for the preview window. Can be a split, floating window,\n      -- or `main` to show the preview in the main editor window.\n      ---@type trouble.Window.opts\n      preview = {\n        type = \"main\",\n        -- when a buffer is not yet loaded, the preview window will be created\n        -- in a scratch buffer with only syntax highlighting enabled.\n        -- Set to false, if you want the preview to always be a real loaded buffer.\n        scratch = true,\n      },\n      -- Throttle/Debounce settings. Should usually not be changed.\n      ---@type table<string, number|{ms:number, debounce?:boolean}>\n      throttle = {\n        refresh = 20, -- fetches new data when needed\n        update = 10, -- updates the window\n        render = 10, -- renders the window\n        follow = 100, -- follows the current item\n        preview = { ms = 100, debounce = true }, -- shows the preview for the current item\n      },\n      -- Key mappings can be set to the name of a builtin action,\n      -- or you can define your own custom action.\n      ---@type table<string, trouble.Action.spec|false>\n      keys = {\n        [\"?\"] = \"help\",\n        r = \"refresh\",\n        R = \"toggle_refresh\",\n        q = \"close\",\n        o = \"jump_close\",\n        [\"<esc>\"] = \"cancel\",\n        [\"<cr>\"] = \"jump\",\n        [\"<2-leftmouse>\"] = \"jump\",\n        [\"<c-s>\"] = \"jump_split\",\n        [\"<c-v>\"] = \"jump_vsplit\",\n        -- go down to next item (accepts count)\n        -- j = \"next\",\n        [\"}\"] = \"next\",\n        [\"]]\"] = \"next\",\n        -- go up to prev item (accepts count)\n        -- k = \"prev\",\n        [\"{\"] = \"prev\",\n        [\"[[\"] = \"prev\",\n        dd = \"delete\",\n        d = { action = \"delete\", mode = \"v\" },\n        i = \"inspect\",\n        p = \"preview\",\n        P = \"toggle_preview\",\n        zo = \"fold_open\",\n        zO = \"fold_open_recursive\",\n        zc = \"fold_close\",\n        zC = \"fold_close_recursive\",\n        za = \"fold_toggle\",\n        zA = \"fold_toggle_recursive\",\n        zm = \"fold_more\",\n        zM = \"fold_close_all\",\n        zr = \"fold_reduce\",\n        zR = \"fold_open_all\",\n        zx = \"fold_update\",\n        zX = \"fold_update_all\",\n        zn = \"fold_disable\",\n        zN = \"fold_enable\",\n        zi = \"fold_toggle_enable\",\n        gb = { -- example of a custom action that toggles the active view filter\n          action = function(view)\n            view:filter({ buf = 0 }, { toggle = true })\n          end,\n          desc = \"Toggle Current Buffer Filter\",\n        },\n        s = { -- example of a custom action that toggles the severity\n          action = function(view)\n            local f = view:get_filter(\"severity\")\n            local severity = ((f and f.filter.severity or 0) + 1) % 5\n            view:filter({ severity = severity }, {\n              id = \"severity\",\n              template = \"{hl:Title}Filter:{hl} {severity}\",\n              del = severity == 0,\n            })\n          end,\n          desc = \"Toggle Severity Filter\",\n        },\n      },\n      ---@type table<string, trouble.Mode>\n      modes = {\n        -- sources define their own modes, which you can use directly,\n        -- or override like in the example below\n        lsp_references = {\n          -- some modes are configurable, see the source code for more details\n          params = {\n            include_declaration = true,\n          },\n        },\n        -- The LSP base mode for:\n        -- * lsp_definitions, lsp_references, lsp_implementations\n        -- * lsp_type_definitions, lsp_declarations, lsp_command\n        lsp_base = {\n          params = {\n            -- don't include the current location in the results\n            include_current = false,\n          },\n        },\n        -- more advanced example that extends the lsp_document_symbols\n        symbols = {\n          desc = \"document symbols\",\n          mode = \"lsp_document_symbols\",\n          focus = false,\n          win = { position = \"right\" },\n          filter = {\n            -- remove Package since luals uses it for control flow structures\n            [\"not\"] = { ft = \"lua\", kind = \"Package\" },\n            any = {\n              -- all symbol kinds for help / markdown files\n              ft = { \"help\", \"markdown\" },\n              -- default set of symbol kinds\n              kind = {\n                \"Class\",\n                \"Constructor\",\n                \"Enum\",\n                \"Field\",\n                \"Function\",\n                \"Interface\",\n                \"Method\",\n                \"Module\",\n                \"Namespace\",\n                \"Package\",\n                \"Property\",\n                \"Struct\",\n                \"Trait\",\n              },\n            },\n          },\n        },\n      },\n      icons = {\n        ---@type trouble.Indent.symbols\n        indent = {\n          top           = \"│ \",\n          middle        = \"├╴\",\n          last          = \"└╴\",\n          -- last          = \"-╴\",\n          -- last       = \"╰╴\", -- rounded\n          fold_open     = \" \",\n          fold_closed   = \" \",\n          ws            = \"  \",\n        },\n        folder_closed   = \" \",\n        folder_open     = \" \",\n        kinds = {\n          Array         = \" \",\n          Boolean       = \"󰨙 \",\n          Class         = \" \",\n          Constant      = \"󰏿 \",\n          Constructor   = \" \",\n          Enum          = \" \",\n          EnumMember    = \" \",\n          Event         = \" \",\n          Field         = \" \",\n          File          = \" \",\n          Function      = \"󰊕 \",\n          Interface     = \" \",\n          Key           = \" \",\n          Method        = \"󰊕 \",\n          Module        = \" \",\n          Namespace     = \"󰦮 \",\n          Null          = \" \",\n          Number        = \"󰎠 \",\n          Object        = \" \",\n          Operator      = \" \",\n          Package       = \" \",\n          Property      = \" \",\n          String        = \" \",\n          Struct        = \"󰆼 \",\n          TypeParameter = \" \",\n          Variable      = \"󰀫 \",\n        },\n      },\n    }\n<\n\nMake sure to check the Examples </docs/examples.md>!\n\n\nUSAGE                                             *trouble.nvim-trouble-usage*\n\n\nCOMMANDS ~\n\nThe **Trouble** command is a wrapper around the **Trouble** API. It can do\nanything the regular API can do.\n\n- `Trouble [mode] [action] [options]`\n\nSome examples:\n\n- Toggle diagnostics for the current buffer and stay in the current window:\n    - `Trouble diagnostics toggle focus=false filter.buf=0`\n- Show document symbols on the right of the current window.\n    Keep the document symbols in sync with the buffer you started the command in.\n    - `Trouble symbols toggle pinned=true win.relative=win win.position=right`\n- You can use **lua** code in the options for the `Trouble` command.\n    The examples below all do the same thing.\n    - `Trouble diagnostics filter.severity=vim.diagnostic.severity.ERROR`\n    - `Trouble diagnostics filter.severity = vim.diagnostic.severity.ERROR`\n    - `Trouble diagnostics filter = { severity=vim.diagnostic.severity.ERROR }`\n- Merging of nested options, with or without quoting strings:\n    - `Trouble diagnostics win.type = split win.position=right`\n    - `Trouble diagnostics win = { type = split, position=right}`\n    - `Trouble diagnostics win = { type = \"split\", position='right'}`\n\nPlease refer to the API section for more information on the available actions\nand options.\n\nModes:\n\n- **diagnostics**: diagnostics\n- **fzf**: FzfLua results previously opened with `require('trouble.sources.fzf').open()`.\n- **fzf_files**: FzfLua results previously opened with `require('trouble.sources.fzf').open()`.\n- **loclist**: Location List\n- **lsp**: LSP definitions, references, implementations, type definitions, and declarations\n- **lsp_command**: command\n- **lsp_declarations**: declarations\n- **lsp_definitions**: definitions\n- **lsp_document_symbols**: document symbols\n- **lsp_implementations**: implementations\n- **lsp_incoming_calls**: Incoming Calls\n- **lsp_outgoing_calls**: Outgoing Calls\n- **lsp_references**: references\n- **lsp_type_definitions**: type definitions\n- **qflist**: Quickfix List\n- **quickfix**: Quickfix List\n- **snacks**: Snacks results previously opened with `require('trouble.sources.snacks').open()`.\n- **snacks_files**: Snacks results previously opened with `require('trouble.sources.snacks').open()`.\n- **symbols**: document symbols\n- **telescope**: Telescope results previously opened with `require('trouble.sources.telescope').open()`.\n- **telescope_files**: Telescope results previously opened with `require('trouble.sources.telescope').open()`.\n\n\nFILTERS ~\n\nPlease refer to the filter docs <docs/filter.md> for more information examples\non filters.\n\n\nAPI ~\n\nYou can use the following functions in your keybindings:\n\nAPI ~\n\n>lua\n    -- Opens trouble with the given mode.\n    -- If a view is already open with the same mode,\n    -- it will be focused unless `opts.focus = false`.\n    -- When a view is already open and `opts.new = true`,\n    -- a new view will be created.\n    ---@param opts? trouble.Mode | { new?: boolean, refresh?: boolean } | string\n    ---@return trouble.View?\n    require(\"trouble\").open(opts)\n    \n    -- Closes the last open view matching the filter.\n    ---@param opts? trouble.Mode|string\n    ---@return trouble.View?\n    require(\"trouble\").close(opts)\n    \n    -- Toggle the view with the given mode.\n    ---@param opts? trouble.Mode|string\n    ---@return trouble.View?\n    require(\"trouble\").toggle(opts)\n    \n    -- Returns true if there is an open view matching the mode.\n    ---@param opts? trouble.Mode|string\n    require(\"trouble\").is_open(opts)\n    \n    -- Refresh all open views. Normally this is done automatically,\n    -- unless you disabled auto refresh.\n    ---@param opts? trouble.Mode|string\n    require(\"trouble\").refresh(opts)\n    \n    -- Get all items from the active view for a given mode.\n    ---@param opts? trouble.Mode|string\n    require(\"trouble\").get_items(opts)\n    \n    -- Renders a trouble list as a statusline component.\n    -- Check the docs for examples.\n    ---@param opts? trouble.Mode|string|{hl_group?:string}\n    ---@return {get: (fun():string), has: (fun():boolean)}\n    require(\"trouble\").statusline(opts)\n    \n    -- Closes the preview and goes to the main window.\n    -- The Trouble window is not closed.\n    ---@param opts? trouble.Mode | { new? : boolean } | string\n    ---@return trouble.View\n    require(\"trouble\").cancel(opts)\n    \n    -- Open the preview\n    ---@param opts? trouble.Mode | { new? : boolean } | string\n    ---@return trouble.View\n    require(\"trouble\").delete(opts)\n    \n    -- filter\n    ---@param opts? trouble.Mode | { new? : boolean } | string\n    ---@return trouble.View\n    require(\"trouble\").filter(opts)\n    \n    -- Go to the first item\n    ---@param opts? trouble.Mode | { new? : boolean } | string\n    ---@return trouble.View\n    require(\"trouble\").first(opts)\n    \n    -- Focus the trouble window\n    ---@param opts? trouble.Mode | { new? : boolean } | string\n    ---@return trouble.View\n    require(\"trouble\").focus(opts)\n    \n    -- Fold close \n    ---@param opts? trouble.Mode | { new? : boolean } | string\n    ---@return trouble.View\n    require(\"trouble\").fold_close(opts)\n    \n    -- fold close all\n    ---@param opts? trouble.Mode | { new? : boolean } | string\n    ---@return trouble.View\n    require(\"trouble\").fold_close_all(opts)\n    \n    -- Fold close recursive\n    ---@param opts? trouble.Mode | { new? : boolean } | string\n    ---@return trouble.View\n    require(\"trouble\").fold_close_recursive(opts)\n    \n    -- fold disable\n    ---@param opts? trouble.Mode | { new? : boolean } | string\n    ---@return trouble.View\n    require(\"trouble\").fold_disable(opts)\n    \n    -- fold enable\n    ---@param opts? trouble.Mode | { new? : boolean } | string\n    ---@return trouble.View\n    require(\"trouble\").fold_enable(opts)\n    \n    -- fold more\n    ---@param opts? trouble.Mode | { new? : boolean } | string\n    ---@return trouble.View\n    require(\"trouble\").fold_more(opts)\n    \n    -- Fold open \n    ---@param opts? trouble.Mode | { new? : boolean } | string\n    ---@return trouble.View\n    require(\"trouble\").fold_open(opts)\n    \n    -- fold open all\n    ---@param opts? trouble.Mode | { new? : boolean } | string\n    ---@return trouble.View\n    require(\"trouble\").fold_open_all(opts)\n    \n    -- Fold open recursive\n    ---@param opts? trouble.Mode | { new? : boolean } | string\n    ---@return trouble.View\n    require(\"trouble\").fold_open_recursive(opts)\n    \n    -- fold reduce\n    ---@param opts? trouble.Mode | { new? : boolean } | string\n    ---@return trouble.View\n    require(\"trouble\").fold_reduce(opts)\n    \n    -- Fold toggle \n    ---@param opts? trouble.Mode | { new? : boolean } | string\n    ---@return trouble.View\n    require(\"trouble\").fold_toggle(opts)\n    \n    -- fold toggle enable\n    ---@param opts? trouble.Mode | { new? : boolean } | string\n    ---@return trouble.View\n    require(\"trouble\").fold_toggle_enable(opts)\n    \n    -- Fold toggle recursive\n    ---@param opts? trouble.Mode | { new? : boolean } | string\n    ---@return trouble.View\n    require(\"trouble\").fold_toggle_recursive(opts)\n    \n    -- fold update\n    ---@param opts? trouble.Mode | { new? : boolean } | string\n    ---@return trouble.View\n    require(\"trouble\").fold_update(opts)\n    \n    -- fold update all\n    ---@param opts? trouble.Mode | { new? : boolean } | string\n    ---@return trouble.View\n    require(\"trouble\").fold_update_all(opts)\n    \n    -- Show the help\n    ---@param opts? trouble.Mode | { new? : boolean } | string\n    ---@return trouble.View\n    require(\"trouble\").help(opts)\n    \n    -- Dump the item to the console\n    ---@param opts? trouble.Mode | { new? : boolean } | string\n    ---@return trouble.View\n    require(\"trouble\").inspect(opts)\n    \n    -- Jump to the item if on an item, otherwise fold the node\n    ---@param opts? trouble.Mode | { new? : boolean } | string\n    ---@return trouble.View\n    require(\"trouble\").jump(opts)\n    \n    -- Jump to the item and close the trouble window\n    ---@param opts? trouble.Mode | { new? : boolean } | string\n    ---@return trouble.View\n    require(\"trouble\").jump_close(opts)\n    \n    -- Jump to the item if on an item, otherwise do nothing\n    ---@param opts? trouble.Mode | { new? : boolean } | string\n    ---@return trouble.View\n    require(\"trouble\").jump_only(opts)\n    \n    -- Open the item in a split\n    ---@param opts? trouble.Mode | { new? : boolean } | string\n    ---@return trouble.View\n    require(\"trouble\").jump_split(opts)\n    \n    -- Open the item in a split and close the trouble window\n    ---@param opts? trouble.Mode | { new? : boolean } | string\n    ---@return trouble.View\n    require(\"trouble\").jump_split_close(opts)\n    \n    -- Open the item in a vsplit\n    ---@param opts? trouble.Mode | { new? : boolean } | string\n    ---@return trouble.View\n    require(\"trouble\").jump_vsplit(opts)\n    \n    -- Open the item in a vsplit and close the trouble window\n    ---@param opts? trouble.Mode | { new? : boolean } | string\n    ---@return trouble.View\n    require(\"trouble\").jump_vsplit_close(opts)\n    \n    -- Go to the last item\n    ---@param opts? trouble.Mode | { new? : boolean } | string\n    ---@return trouble.View\n    require(\"trouble\").last(opts)\n    \n    -- Go to the next item\n    ---@param opts? trouble.Mode | { new? : boolean } | string\n    ---@return trouble.View\n    require(\"trouble\").next(opts)\n    \n    -- Go to the previous item\n    ---@param opts? trouble.Mode | { new? : boolean } | string\n    ---@return trouble.View\n    require(\"trouble\").prev(opts)\n    \n    -- Open the preview\n    ---@param opts? trouble.Mode | { new? : boolean } | string\n    ---@return trouble.View\n    require(\"trouble\").preview(opts)\n    \n    -- Refresh the trouble source\n    ---@param opts? trouble.Mode | { new? : boolean } | string\n    ---@return trouble.View\n    require(\"trouble\").refresh(opts)\n    \n    -- Toggle the preview\n    ---@param opts? trouble.Mode | { new? : boolean } | string\n    ---@return trouble.View\n    require(\"trouble\").toggle_preview(opts)\n    \n    -- Toggle the auto refresh\n    ---@param opts? trouble.Mode | { new? : boolean } | string\n    ---@return trouble.View\n    require(\"trouble\").toggle_refresh(opts)\n<\n\n\nTELESCOPE ~\n\nYou can easily open any search results in **Trouble**, by defining a custom\naction:\n\n>lua\n    local actions = require(\"telescope.actions\")\n    local open_with_trouble = require(\"trouble.sources.telescope\").open\n    \n    -- Use this to add more results without clearing the trouble list\n    local add_to_trouble = require(\"trouble.sources.telescope\").add\n    \n    local telescope = require(\"telescope\")\n    \n    telescope.setup({\n      defaults = {\n        mappings = {\n          i = { [\"<c-t>\"] = open_with_trouble },\n          n = { [\"<c-t>\"] = open_with_trouble },\n        },\n      },\n    })\n<\n\nWhen you open telescope, you can now hit `<c-t>` to open the results in\n**Trouble**\n\n\nFZF-LUA ~\n\nYou can easily open any search results in **Trouble**, by defining a custom\naction:\n\n>lua\n    local config = require(\"fzf-lua.config\")\n    local actions = require(\"trouble.sources.fzf\").actions\n    config.defaults.actions.files[\"ctrl-t\"] = actions.open\n<\n\nWhen you open fzf-lua, you can now hit `<c-t>` to open the results in\n**Trouble**\n\n\nSTATUSLINE COMPONENT ~\n\nExample for lualine.nvim <https://github.com/nvim-lualine/lualine.nvim>:\n\n>lua\n    {\n      \"nvim-lualine/lualine.nvim\",\n      opts = function(_, opts)\n        local trouble = require(\"trouble\")\n        local symbols = trouble.statusline({\n          mode = \"lsp_document_symbols\",\n          groups = {},\n          title = false,\n          filter = { range = true },\n          format = \"{kind_icon}{symbol.name:Normal}\",\n          -- The following line is needed to fix the background color\n          -- Set it to the lualine section you want to use\n          hl_group = \"lualine_c_normal\",\n        })\n        table.insert(opts.sections.lualine_c, {\n          symbols.get,\n          cond = symbols.has,\n        })\n      end,\n    }\n<\n\n\nCOLORS                                           *trouble.nvim-trouble-colors*\n\nThe table below shows all the highlight groups defined for Trouble.\n\nHighlight Groups ~\n\n  Highlight Group            Default Group             Description\n  -------------------------- ------------------------- -------------\n  TroubleBasename            TroubleFilename           \n  TroubleCode                Special                   \n  TroubleCount               TabLineSel                \n  TroubleDirectory           Directory                 \n  TroubleFilename            Directory                 \n  TroubleIconArray           @punctuation.bracket      \n  TroubleIconBoolean         @boolean                  \n  TroubleIconClass           @type                     \n  TroubleIconConstant        @constant                 \n  TroubleIconConstructor     @constructor              \n  TroubleIconDirectory       Special                   \n  TroubleIconEnum            @lsp.type.enum            \n  TroubleIconEnumMember      @lsp.type.enumMember      \n  TroubleIconEvent           Special                   \n  TroubleIconField           @variable.member          \n  TroubleIconFile            Normal                    \n  TroubleIconFunction        @function                 \n  TroubleIconInterface       @lsp.type.interface       \n  TroubleIconKey             @lsp.type.keyword         \n  TroubleIconMethod          @function.method          \n  TroubleIconModule          @module                   \n  TroubleIconNamespace       @module                   \n  TroubleIconNull            @constant.builtin         \n  TroubleIconNumber          @number                   \n  TroubleIconObject          @constant                 \n  TroubleIconOperator        @operator                 \n  TroubleIconPackage         @module                   \n  TroubleIconProperty        @property                 \n  TroubleIconString          @string                   \n  TroubleIconStruct          @lsp.type.struct          \n  TroubleIconTypeParameter   @lsp.type.typeParameter   \n  TroubleIconVariable        @variable                 \n  TroubleIndent              LineNr                    \n  TroubleIndentFoldClosed    CursorLineNr              \n  TroubleIndentFoldOpen      TroubleIndent             \n  TroubleIndentLast          TroubleIndent             \n  TroubleIndentMiddle        TroubleIndent             \n  TroubleIndentTop           TroubleIndent             \n  TroubleIndentWs            TroubleIndent             \n  TroubleNormal              NormalFloat               \n  TroubleNormalNC            NormalFloat               \n  TroublePos                 LineNr                    \n  TroublePreview             Visual                    \n  TroubleSource              Comment                   \n  TroubleText                Normal                    \n==============================================================================\n2. Links                                                  *trouble.nvim-links*\n\n1. *image*: https://github.com/folke/trouble.nvim/assets/292349/481bc1f7-cb93-432d-8ab6-f54044334b96\n\nGenerated by panvimdoc <https://github.com/kdheepak/panvimdoc>\n\nvim:tw=78:ts=8:noet:ft=help:norl:\n"
  },
  {
    "path": "docs/examples.md",
    "content": "# Examples\n\n## Window Options\n\n### Preview in small float top right\n\n![image](https://github.com/folke/trouble.nvim/assets/292349/f422b8fd-579e-427b-87d3-62daab85d2e0)\n\n```lua\n{\n  modes = {\n    preview_float = {\n      mode = \"diagnostics\",\n      preview = {\n        type = \"float\",\n        relative = \"editor\",\n        border = \"rounded\",\n        title = \"Preview\",\n        title_pos = \"center\",\n        position = { 0, -2 },\n        size = { width = 0.3, height = 0.3 },\n        zindex = 200,\n      },\n    },\n  },\n}\n```\n\n### Preview in a split to the right of the trouble list\n\n![image](https://github.com/folke/trouble.nvim/assets/292349/adfa02df-b3dd-4c90-af3c-41683c0b5356)\n\n```lua\n{\n  modes = {\n    test = {\n      mode = \"diagnostics\",\n      preview = {\n        type = \"split\",\n        relative = \"win\",\n        position = \"right\",\n        size = 0.3,\n      },\n    },\n  },\n}\n```\n\n### Make Trouble look like v2\n\n```lua\n{\n  icons = {\n    indent = {\n      middle = \" \",\n      last = \" \",\n      top = \" \",\n      ws = \"│  \",\n    },\n  },\n  modes = {\n    diagnostics = {\n      groups = {\n        { \"filename\", format = \"{file_icon} {basename:Title} {count}\" },\n      },\n    },\n  },\n}\n```\n\n## Filtering\n\n### Diagnostics for the current buffer only\n\n```lua\n{\n  modes = {\n    diagnostics_buffer = {\n      mode = \"diagnostics\", -- inherit from diagnostics mode\n      filter = { buf = 0 }, -- filter diagnostics to the current buffer\n    },\n  }\n}\n```\n\n### Diagnostics for the current buffer and errors from the current project\n\n```lua\n{\n  modes = {\n    mydiags = {\n      mode = \"diagnostics\", -- inherit from diagnostics mode\n      filter = {\n        any = {\n          buf = 0, -- current buffer\n          {\n            severity = vim.diagnostic.severity.ERROR, -- errors only\n            -- limit to files in the current project\n            function(item)\n              return item.filename:find((vim.loop or vim.uv).cwd(), 1, true)\n            end,\n          },\n        },\n      },\n    }\n  }\n}\n```\n\n### Diagnostics Cascade\n\nThe following example shows how to create a new mode that\nshows only the most severe diagnostics.\n\nOnce those are resolved, less severe diagnostics will be shown.\n\n```lua\n{\n  modes = {\n    cascade = {\n      mode = \"diagnostics\", -- inherit from diagnostics mode\n      filter = function(items)\n        local severity = vim.diagnostic.severity.HINT\n        for _, item in ipairs(items) do\n          severity = math.min(severity, item.severity)\n        end\n        return vim.tbl_filter(function(item)\n          return item.severity == severity\n        end, items)\n      end,\n    },\n  },\n}\n```\n\n## Other\n\n### Automatically Open Trouble Quickfix\n\n```lua\nvim.api.nvim_create_autocmd(\"QuickFixCmdPost\", {\n  callback = function()\n    vim.cmd([[Trouble qflist open]])\n  end,\n})\n```\n\nTest with something like `:silent grep vim %`\n\n### Open Trouble Quickfix when the qf list opens\n\n> This is **NOT** recommended, since you won't be able to use the quickfix list for other things.\n\n```lua\n\nvim.api.nvim_create_autocmd(\"BufRead\", {\n  callback = function(ev)\n    if vim.bo[ev.buf].buftype == \"quickfix\" then\n      vim.schedule(function()\n        vim.cmd([[cclose]])\n        vim.cmd([[Trouble qflist open]])\n      end)\n    end\n  end,\n})\n```\n"
  },
  {
    "path": "docs/filter.md",
    "content": "# Filter\n\n## Examples\n\nA simple filter is a table whose keys are item attributes.\nThe following filter keeps items with attribute `buf = 0` **and** `ft = 'lua'`,\ni.e., diagnostics with severity error to the current buffer when its filetype is `lua`.\n\n```lua\n{\n  modes = {\n    my_diagnostics = {\n      mode = 'diagnostics',\n      filter = { buf = 0, ft = 'lua' },\n    },\n  },\n}\n```\n\nA filter may be a function that takes `items` as parameter.\nThe following filter keeps items with severity `HINT`\n```lua\n{\n  modes = {\n    my_diagnostics = {\n      mode = 'diagnostics',\n      filter = function(items)\n        return vim.tbl_filter(function(item)\n          return item.severity == vim.diagnostic.severity.HINT\n        end, items)\n      end,\n    },\n  },\n}\n```\n\n## Advanced examples\n\n### The `not` filter\n\nThe `not` negates filter results.\nThe following filter **removes** diagnostics with severity `INFO`\n```lua\n{\n  modes = {\n    my_diagnostics = {\n      mode = 'diagnostics',\n      filter = {\n        ['not'] = { severity = vim.diagnostic.severity.INFO },\n      },\n    },\n  },\n}\n```\n\n### The `any` filter\n\nThe `any` filter provides logical disjunction.\nThe following filter **keeps** diagnostics for the current buffer **or** diagnostics with severity `ERROR` for the current project.\n\n```lua\n{\n  modes = {\n    my_diagnostics = {\n      mode = 'diagnostics',\n      filter = {\n        any = {\n          buf = 0,\n          {\n            severity = vim.diagnostic.severity.ERROR,\n            function(item)\n              return item.filename:find((vim.loop or vim.uv).cwd(), 1, true)\n            end,\n          },\n        },\n      },\n    },\n  },\n}\n```\n\n## Item attributes\n\nItem attributes are documented in `lua/trouble/item.lua`\n\n|     Name     |            Type            |                    Description                     |\n| ------------ | -------------------------- | -------------------------------------------------- |\n| **any**      | Logical `or`               | Filter result disjunction                          |\n| **basename** | `string`                   | File name.                                         |\n| **buf**      | `number`                   | Buffer id.                                         |\n| **dirname**  | `string`                   | Directory path.                                    |\n| **filename** | `string`                   | Full file path.                                    |\n| **ft**       | `string` or `string[]`     | File types.                                        |\n| **kind**     | `string`                   | Symbol kind. See `:h symbol`.                      |\n| **not**      | Logical `not`              | Filter result negation.                            |\n| **pos**      | `{[1]:number, [2]:number}` | Item position.                                     |\n| **severity** | `number`                   | Diagnostic severity. See `:h diagnostic-severity`. |\n| **source**   | `string`                   | Item source.                                       |"
  },
  {
    "path": "lua/trouble/api.lua",
    "content": "local Actions = require(\"trouble.config.actions\")\nlocal Config = require(\"trouble.config\")\nlocal Util = require(\"trouble.util\")\nlocal View = require(\"trouble.view\")\n\n---@alias trouble.ApiFn fun(opts?: trouble.Config|string): trouble.View\n\n---@class trouble.api: trouble.actions\nlocal M = {}\nM.last_mode = nil ---@type string?\n\n--- Finds all open views matching the filter.\n---@param opts? trouble.Config|string\n---@param filter? trouble.View.filter\n---@return trouble.View[], trouble.Config\nfunction M._find(opts, filter)\n  opts = Config.get(opts)\n  if opts.mode == \"last\" then\n    opts.mode = M.last_mode\n    opts = Config.get(opts)\n  end\n  M.last_mode = opts.mode or M.last_mode\n  filter = filter or { open = true, mode = opts.mode }\n  return vim.tbl_map(function(v)\n    return v.view\n  end, View.get(filter)), opts\nend\n\n--- Finds the last open view matching the filter.\n---@param opts? trouble.Mode|string\n---@param filter? trouble.View.filter\n---@return trouble.View?, trouble.Mode\nfunction M._find_last(opts, filter)\n  local views, _opts = M._find(opts, filter)\n  ---@cast _opts trouble.Mode\n  return views[#views], _opts\nend\n\n-- Opens trouble with the given mode.\n-- If a view is already open with the same mode,\n-- it will be focused unless `opts.focus = false`.\n-- When a view is already open and `opts.new = true`,\n-- a new view will be created.\n---@param opts? trouble.Mode | { new?: boolean, refresh?: boolean } | string\n---@return trouble.View?\nfunction M.open(opts)\n  opts = opts or {}\n  local view, _opts = M._find_last(opts)\n  if not view or _opts.new then\n    if not _opts.mode then\n      return Util.error(\"No mode specified\")\n    elseif not vim.tbl_contains(Config.modes(), _opts.mode) then\n      return Util.error(\"Invalid mode `\" .. _opts.mode .. \"`\")\n    end\n    view = View.new(_opts)\n  end\n  if view then\n    if view:is_open() then\n      if opts.refresh ~= false then\n        view:refresh()\n      end\n    else\n      view:open()\n    end\n    if _opts.focus ~= false then\n      view:wait(function()\n        view.win:focus()\n      end)\n    end\n  end\n  return view\nend\n\n-- Closes the last open view matching the filter.\n---@param opts? trouble.Mode|string\n---@return trouble.View?\nfunction M.close(opts)\n  local view = M._find_last(opts)\n  if view then\n    view:close()\n    return view\n  end\nend\n\n-- Toggle the view with the given mode.\n---@param opts? trouble.Mode|string\n---@return trouble.View?\nfunction M.toggle(opts)\n  if M.is_open(opts) then\n    ---@diagnostic disable-next-line: return-type-mismatch\n    return M.close(opts)\n  else\n    return M.open(opts)\n  end\nend\n\n-- Returns true if there is an open view matching the mode.\n---@param opts? trouble.Mode|string\nfunction M.is_open(opts)\n  return M._find_last(opts) ~= nil\nend\n\n-- Refresh all open views. Normally this is done automatically,\n-- unless you disabled auto refresh.\n---@param opts? trouble.Mode|string\nfunction M.refresh(opts)\n  for _, view in ipairs(M._find(opts)) do\n    view:refresh()\n  end\nend\n\n-- Proxy to last view's action.\n---@param action trouble.Action.spec\nfunction M._action(action)\n  return function(opts)\n    opts = opts or {}\n    if type(opts) == \"string\" then\n      opts = { mode = opts }\n    end\n    opts = vim.tbl_deep_extend(\"force\", {\n      refresh = false,\n    }, opts)\n    local view = M.open(opts)\n    if view then\n      view:action(action, opts)\n    end\n    return view\n  end\nend\n\n-- Get all items from the active view for a given mode.\n---@param opts? trouble.Mode|string\nfunction M.get_items(opts)\n  local view = M._find_last(opts)\n  local ret = {} ---@type trouble.Item[]\n  if view then\n    for _, source in pairs(view.sections) do\n      vim.list_extend(ret, source.items or {})\n    end\n  end\n  return ret\nend\n\n-- Renders a trouble list as a statusline component.\n-- Check the docs for examples.\n---@param opts? trouble.Mode|string|{hl_group?:string}\n---@return {get: (fun():string), has: (fun():boolean)}\nfunction M.statusline(opts)\n  local Spec = require(\"trouble.spec\")\n  local Section = require(\"trouble.view.section\")\n  local Render = require(\"trouble.view.render\")\n  opts = Config.get(opts)\n  opts.indent_guides = false\n  opts.icons.indent.ws = \"\"\n  local renderer = Render.new(opts, {\n    multiline = false,\n    indent = false,\n  })\n  local status = nil ---@type string?\n  ---@cast opts trouble.Mode\n\n  local s = Spec.section(opts)\n  s.max_items = s.max_items or opts.max_items\n  local section = Section.new(s, opts)\n  section.on_update = function()\n    status = nil\n    if package.loaded[\"lualine\"] then\n      vim.schedule(function()\n        require(\"lualine\").refresh()\n      end)\n    else\n      vim.cmd.redrawstatus()\n    end\n  end\n  section:listen()\n  section:refresh()\n  return {\n    has = function()\n      return section.node and section.node:count() > 0\n    end,\n    get = function()\n      if status then\n        return status\n      end\n      renderer:clear()\n      renderer:sections({ section })\n      status = renderer:statusline()\n      if opts.hl_group then\n        status = require(\"trouble.config.highlights\").fix_statusline(status, opts.hl_group)\n      end\n      return status\n    end,\n  }\nend\n\nreturn setmetatable(M, {\n  __index = function(_, k)\n    if k == \"last_mode\" then\n      return nil\n    end\n    return M._action(k)\n  end,\n})\n"
  },
  {
    "path": "lua/trouble/async.lua",
    "content": "local M = {}\nlocal uv = vim.loop or vim.uv\n\nM.budget = 1\nlocal Scheduler = {}\nScheduler._queue = {}\nScheduler._executor = assert(uv.new_check())\n\nfunction Scheduler.step()\n  local budget = M.budget * 1e6\n  local start = uv.hrtime()\n  while #Scheduler._queue > 0 and uv.hrtime() - start < budget do\n    local a = table.remove(Scheduler._queue, 1)\n    a:_step()\n    if a.running then\n      table.insert(Scheduler._queue, a)\n    end\n  end\n  if #Scheduler._queue == 0 then\n    return Scheduler._executor:stop()\n  end\nend\n\n---@param a Async\nfunction Scheduler.add(a)\n  table.insert(Scheduler._queue, a)\n  if not Scheduler._executor:is_active() then\n    Scheduler._executor:start(vim.schedule_wrap(Scheduler.step))\n  end\nend\n\n--- @alias AsyncCallback fun(result?:any, error?:string)\n\n--- @class Async\n--- @field running boolean\n--- @field result? any\n--- @field error? string\n--- @field callbacks AsyncCallback[]\n--- @field thread thread\nlocal Async = {}\nAsync.__index = Async\n\nfunction Async.new(fn)\n  local self = setmetatable({}, Async)\n  self.callbacks = {}\n  self.running = true\n  self.thread = coroutine.create(fn)\n  Scheduler.add(self)\n  return self\nend\n\n---@param result? any\n---@param error? string\nfunction Async:_done(result, error)\n  if self.running then\n    self.running = false\n    self.result = result\n    self.error = error\n  end\n  for _, callback in ipairs(self.callbacks) do\n    callback(result, error)\n  end\n  -- only run each callback once.\n  -- _done can possibly be called multiple times.\n  -- so we need to clear callbacks after executing them.\n  self.callbacks = {}\nend\n\nfunction Async:_step()\n  local ok, res = coroutine.resume(self.thread)\n  if not ok then\n    return self:_done(nil, res)\n  elseif res == \"abort\" then\n    return self:_done(nil, \"abort\")\n  elseif coroutine.status(self.thread) == \"dead\" then\n    return self:_done(res)\n  end\nend\n\nfunction Async:cancel()\n  self:_done(nil, \"abort\")\nend\n\n---@param cb AsyncCallback\nfunction Async:await(cb)\n  if not cb then\n    error(\"callback is required\")\n  end\n  if self.running then\n    table.insert(self.callbacks, cb)\n  else\n    cb(self.result, self.error)\n  end\nend\n\nfunction Async:sync()\n  while self.running do\n    vim.wait(10)\n  end\n  return self.error and error(self.error) or self.result\nend\n\n--- @return boolean\nfunction M.is_async(obj)\n  return obj and type(obj) == \"table\" and getmetatable(obj) == Async\nend\n\n---@generic F\n---@param fn F\n---@return F|fun(...): Async\nfunction M.wrap(fn)\n  return function(...)\n    local args = { ... }\n    return Async.new(function()\n      return fn(unpack(args))\n    end)\n  end\nend\n\n-- This will yield when called from a coroutine\n---@async\nfunction M.yield(...)\n  if coroutine.running() == nil then\n    error(\"Trying to yield from a non-yieldable context\")\n    return ...\n  end\n  return coroutine.yield(...)\nend\n\n---@async\nfunction M.abort()\n  return M.yield(\"abort\")\nend\n\nreturn M\n"
  },
  {
    "path": "lua/trouble/cache.lua",
    "content": "---@class trouble.CacheM: {[string]: trouble.Cache}\nlocal M = {}\n\n---@type table<string, {name:string, hit: number, miss: number, ratio?:number}>\nM.stats = {}\n\n---@class trouble.Cache: {[string]: any}\n---@field data table<string, any>\n---@field name string\n---@field size number\nlocal C = {}\n\nfunction C:__index(key)\n  local ret = C[key]\n  if ret then\n    return ret\n  end\n  ret = self.data[key]\n  M.stats[self.name] = M.stats[self.name] or { name = self.name, hit = 0, miss = 0 }\n  local stats = M.stats[self.name]\n  if ret ~= nil then\n    stats.hit = stats.hit + 1\n  else\n    stats.miss = stats.miss + 1\n  end\n  return ret\nend\n\nfunction C:__newindex(key, value)\n  if self.data[key] ~= nil and value == nil then\n    self.size = self.size - 1\n  elseif self.data[key] == nil and value ~= nil then\n    self.size = self.size + 1\n  end\n  self.data[key] = value\nend\n\nfunction C:clear()\n  self.data = {}\n  self.size = 0\nend\n\nfunction M.new(name)\n  return setmetatable({ data = {}, name = name, size = 0 }, C)\nend\n\nfunction M.report()\n  for _, v in pairs(M.stats) do\n    v.ratio = math.ceil(v.hit / (v.hit + v.miss) * 100)\n  end\n  return M.stats\nend\n\nfunction M.__index(_, k)\n  M[k] = M.new(k)\n  return M[k]\nend\n\nlocal ret = setmetatable(M, M)\nreturn ret\n"
  },
  {
    "path": "lua/trouble/command.lua",
    "content": "local Config = require(\"trouble.config\")\nlocal Parser = require(\"trouble.config.parser\")\nlocal Util = require(\"trouble.util\")\n\nlocal M = {}\n\n---@param prefix string\n---@param line string\n---@param col number\nfunction M.complete(prefix, line, col)\n  line = line:sub(1, col):match(\"Trouble%s*(.*)$\")\n  local parsed = M.parse(line)\n  local candidates = {} ---@type string[]\n  if vim.tbl_isempty(parsed.opts) then\n    if not parsed.mode then\n      vim.list_extend(candidates, Config.modes())\n    else\n      if not parsed.action then\n        vim.list_extend(candidates, M.actions())\n      end\n      vim.list_extend(candidates, M.complete_opts())\n    end\n  else\n    vim.list_extend(candidates, M.complete_opts())\n  end\n  candidates = vim.tbl_filter(function(x)\n    return tostring(x):find(prefix, 1, true) == 1\n  end, candidates)\n  table.sort(candidates)\n  return candidates\nend\n\nfunction M.complete_opts()\n  local candidates = {} ---@type string[]\n  local stack = { { k = \"\", t = Config.get() } }\n  while #stack > 0 do\n    local top = table.remove(stack)\n    for k, v in pairs(top.t) do\n      if type(k) == \"number\" then\n        k = \"[\" .. k .. \"]\"\n      elseif k:match(\"^[a-z_]+$\") then\n        k = \".\" .. k\n      else\n        k = (\"[%q]\"):format(k)\n      end\n      local kk = top.k .. k\n      candidates[#candidates + 1] = kk:gsub(\"^%.\", \"\") .. \"=\"\n      if type(v) == \"table\" and not Util.islist(v) then\n        table.insert(stack, { k = kk, t = v })\n      end\n    end\n  end\n  vim.list_extend(candidates, {\n    \"new=true\",\n  })\n  for _, w in ipairs({ \"win\", \"preview\" }) do\n    local winopts = {\n      \"type=float\",\n      \"type=split\",\n      \"position=top\",\n      \"position=bottom\",\n      \"position=left\",\n      \"position=right\",\n      \"relative=editor\",\n      \"relative=win\",\n    }\n    vim.list_extend(\n      candidates,\n      vim.tbl_map(function(x)\n        return w .. \".\" .. x\n      end, winopts)\n    )\n  end\n  return candidates\nend\n\nfunction M.actions()\n  local actions = vim.tbl_keys(require(\"trouble.api\"))\n  vim.list_extend(actions, vim.tbl_keys(require(\"trouble.config.actions\")))\n  return actions\nend\n\n---@param input string\nfunction M.parse(input)\n  ---@type {mode: string, action: string, opts: trouble.Config, errors: string[], args: string[]}\n  local ret = Parser.parse(input)\n  local modes = Config.modes()\n  local actions = M.actions()\n\n  -- Args can be mode and/or action\n  for _, a in ipairs(ret.args) do\n    if vim.tbl_contains(modes, a) then\n      ret.mode = a\n    elseif vim.tbl_contains(actions, a) then\n      ret.action = a\n    else\n      table.insert(ret.errors, \"Unknown argument: \" .. a)\n    end\n  end\n\n  return ret\nend\n\nfunction M.execute(input)\n  if input.args:match(\"^%s*$\") then\n    ---@type {name: string, desc: string}[]\n    local modes = vim.tbl_map(function(x)\n      local m = Config.get(x)\n      local desc = m.desc or x:gsub(\"^%l\", string.upper)\n      desc = Util.camel(desc, \" \")\n      return { name = x, desc = desc }\n    end, Config.modes())\n\n    vim.ui.select(modes, {\n      prompt = \"Select Trouble Mode:\",\n      format_item = function(x)\n        return x.desc and (x.desc .. \" (\" .. x.name .. \")\") or x.name\n      end,\n    }, function(mode)\n      if mode then\n        require(\"trouble\").open({ mode = mode.name })\n      end\n    end)\n  else\n    local ret = M.parse(input.args)\n    ret.action = ret.action or \"open\"\n    ret.opts.mode = ret.opts.mode or ret.mode\n    if #ret.errors > 0 then\n      Util.error(\"Error parsing command:\\n- input: `\" .. input.args .. \"`\\nErrors:\\n\" .. table.concat(ret.errors, \"\\n\"))\n      return\n    end\n    require(\"trouble\")[ret.action](ret.opts)\n  end\nend\n\nreturn M\n"
  },
  {
    "path": "lua/trouble/config/actions.lua",
    "content": "local Util = require(\"trouble.util\")\n\n---@alias trouble.Action.ctx {item?: trouble.Item, node?: trouble.Node, opts?: table}\n---@alias trouble.ActionFn fun(view:trouble.View, ctx:trouble.Action.ctx)\n---@alias trouble.Action {action: trouble.ActionFn, desc?: string, mode?: string}\n---@alias trouble.Action.spec string|trouble.ActionFn|trouble.Action|{action: string}\n\n---@class trouble.actions: {[string]: trouble.ActionFn}\nlocal M = {\n  -- Refresh the trouble source\n  refresh = function(self)\n    self:refresh()\n  end,\n  -- Close the trouble window\n  close = function(self)\n    self:close()\n  end,\n  -- Closes the preview and goes to the main window.\n  -- The Trouble window is not closed.\n  cancel = function(self)\n    self:goto_main()\n  end,\n  -- Focus the trouble window\n  focus = function(self)\n    self.win:focus()\n  end,\n  -- Open the preview\n  preview = function(self, ctx)\n    local Preview = require(\"trouble.view.preview\")\n    if Preview.is_open() then\n      Preview.close()\n    else\n      self:preview(ctx.item)\n    end\n  end,\n  -- Open the preview\n  delete = function(self)\n    local enabled = self.opts.auto_refresh\n    self:delete()\n    if enabled and not self.opts.auto_refresh then\n      Util.warn(\"Auto refresh **disabled**\", { id = \"toggle_refresh\" })\n    end\n  end,\n  -- Toggle the preview\n  toggle_preview = function(self, ctx)\n    self.opts.auto_preview = not self.opts.auto_preview\n    local enabled = self.opts.auto_preview and \"enabled\" or \"disabled\"\n    local notify = (enabled == \"enabled\") and Util.info or Util.warn\n    notify(\"Auto preview **\" .. enabled .. \"**\", { id = \"toggle_preview\" })\n    local Preview = require(\"trouble.view.preview\")\n    if self.opts.auto_preview then\n      if ctx.item then\n        self:preview()\n      end\n    else\n      Preview.close()\n    end\n  end,\n  -- Toggle the auto refresh\n  toggle_refresh = function(self)\n    self.opts.auto_refresh = not self.opts.auto_refresh\n    local enabled = self.opts.auto_refresh and \"enabled\" or \"disabled\"\n    local notify = (enabled == \"enabled\") and Util.info or Util.warn\n    notify(\"Auto refresh **\" .. enabled .. \"**\", { id = \"toggle_refresh\" })\n  end,\n\n  filter = function(self, ctx)\n    self:filter(ctx.opts.filter)\n  end,\n  -- Show the help\n  help = function(self)\n    self:help()\n  end,\n  -- Go to the next item\n  next = function(self, ctx)\n    self:move({ down = vim.v.count1, jump = ctx.opts.jump })\n  end,\n  -- Go to the previous item\n  prev = function(self, ctx)\n    self:move({ up = vim.v.count1, jump = ctx.opts.jump })\n  end,\n  -- Go to the first item\n  first = function(self, ctx)\n    self:move({ idx = vim.v.count1, jump = ctx.opts.jump })\n  end,\n  -- Go to the last item\n  last = function(self, ctx)\n    self:move({ idx = -vim.v.count1, jump = ctx.opts.jump })\n  end,\n  -- Jump to the item if on an item, otherwise do nothing\n  jump_only = function(self, ctx)\n    if ctx.item then\n      self:jump(ctx.item)\n    end\n  end,\n  -- Jump to the item if on an item, otherwise fold the node\n  jump = function(self, ctx)\n    if ctx.item then\n      self:jump(ctx.item)\n    elseif ctx.node then\n      self:fold(ctx.node)\n    end\n  end,\n  -- Jump to the item and close the trouble window\n  jump_close = function(self, ctx)\n    if ctx.item then\n      self:jump(ctx.item)\n      self:close()\n    end\n  end,\n  -- Open the item in a split\n  jump_split = function(self, ctx)\n    if ctx.item then\n      self:jump(ctx.item, { split = true })\n    end\n  end,\n  -- Open the item in a split and close the trouble window\n  jump_split_close = function(self, ctx)\n    if ctx.item then\n      self:jump(ctx.item, { split = true })\n      self:close()\n    end\n  end,\n  -- Open the item in a vsplit\n  jump_vsplit = function(self, ctx)\n    if ctx.item then\n      self:jump(ctx.item, { vsplit = true })\n    end\n  end,\n  -- Open the item in a vsplit and close the trouble window\n  jump_vsplit_close = function(self, ctx)\n    if ctx.item then\n      self:jump(ctx.item, { vsplit = true })\n      self:close()\n    end\n  end,\n  -- Dump the item to the console\n  inspect = function(_, ctx)\n    vim.print(ctx.item or (ctx.node and ctx.node.item))\n  end,\n  fold_reduce = function(self)\n    self:fold_level({ add = vim.v.count1 })\n  end,\n  fold_open_all = function(self)\n    self:fold_level({ level = 1000 })\n  end,\n  fold_more = function(self)\n    self:fold_level({ add = -vim.v.count1 })\n  end,\n  fold_close_all = function(self)\n    self:fold_level({ level = 0 })\n  end,\n  fold_update = function(self, ctx)\n    self:fold_level({})\n    self:fold(ctx.node, { action = \"open\" })\n  end,\n  fold_update_all = function(self)\n    self:fold_level({})\n  end,\n  fold_disable = function(self)\n    self.renderer.foldenable = false\n    self:render()\n  end,\n  fold_enable = function(self)\n    self.renderer.foldenable = true\n    self:render()\n  end,\n  fold_toggle_enable = function(self)\n    self.renderer.foldenable = not self.renderer.foldenable\n    self:render()\n  end,\n}\n\nfor _, fold_action in ipairs({ \"toggle\", \"open\", \"close\" }) do\n  for _, recursive in ipairs({ true, false }) do\n    local desc = \"Fold \" .. fold_action .. \" \" .. (recursive and \"recursive\" or \"\")\n    local name = \"fold_\" .. fold_action .. (recursive and \"_recursive\" or \"\")\n    M[name] = {\n      action = function(self, ctx)\n        self:fold(ctx.node, { action = fold_action, recursive = recursive })\n      end,\n      desc = desc,\n    }\n  end\nend\n\nreturn setmetatable(M, {\n  __index = function(_, k)\n    if k == \"previous\" then\n      Util.warn(\"`previous` is deprecated, use `prev` instead\")\n    else\n      Util.error(\"Action not found: \" .. k)\n    end\n  end,\n})\n"
  },
  {
    "path": "lua/trouble/config/highlights.lua",
    "content": "local Util = require(\"trouble.util\")\n\nlocal M = {}\n\n-- stylua: ignore\nM.colors = {\n  -- General\n  Normal            = \"NormalFloat\",\n  NormalNC          = \"NormalFloat\",\n  Text              = \"Normal\",\n  Preview           = \"Visual\",\n\n  -- Item\n  Filename          = \"Directory\",\n  Basename          = \"TroubleFilename\",\n  Directory         = \"Directory\",\n  IconDirectory     = \"Special\",\n  Source            = \"Comment\",\n  Code              = \"Special\",\n  Pos               = \"LineNr\",\n  Count             = \"TabLineSel\",\n\n  -- Indent Guides\n  Indent            = \"LineNr\",\n  IndentFoldClosed  = \"CursorLineNr\",\n  IndentFoldOpen    = \"TroubleIndent\",\n  IndentTop         = \"TroubleIndent\",\n  IndentMiddle      = \"TroubleIndent\",\n  IndentLast        = \"TroubleIndent\",\n  IndentWs          = \"TroubleIndent\",\n\n  -- LSP Symbol Kinds\n  IconArray         = \"@punctuation.bracket\",\n  IconBoolean       = \"@boolean\",\n  IconClass         = \"@type\",\n  IconConstant      = \"@constant\",\n  IconConstructor   = \"@constructor\",\n  IconEnum          = \"@lsp.type.enum\",\n  IconEnumMember    = \"@lsp.type.enumMember\",\n  IconEvent         = \"Special\",\n  IconField         = \"@variable.member\",\n  IconFile          = \"Normal\",\n  IconFunction      = \"@function\",\n  IconInterface     = \"@lsp.type.interface\",\n  IconKey           = \"@lsp.type.keyword\",\n  IconMethod        = \"@function.method\",\n  IconModule        = \"@module\",\n  IconNamespace     = \"@module\",\n  IconNull          = \"@constant.builtin\",\n  IconNumber        = \"@number\",\n  IconObject        = \"@constant\",\n  IconOperator      = \"@operator\",\n  IconPackage       = \"@module\",\n  IconProperty      = \"@property\",\n  IconString        = \"@string\",\n  IconStruct        = \"@lsp.type.struct\",\n  IconTypeParameter = \"@lsp.type.typeParameter\",\n  IconVariable      = \"@variable\",\n}\n\nfunction M.setup()\n  M.link(M.colors)\n  M.source(\"fs\")\n  vim.api.nvim_create_autocmd(\"ColorScheme\", {\n    group = vim.api.nvim_create_augroup(\"trouble.colorscheme\", { clear = true }),\n    callback = function()\n      M._fixed = {}\n    end,\n  })\nend\n\n---@param prefix? string\n---@param links table<string, string>\nfunction M.link(links, prefix)\n  for k, v in pairs(links) do\n    k = (prefix or \"Trouble\") .. k\n    vim.api.nvim_set_hl(0, k, { link = v, default = true })\n  end\nend\n\n---@param source string\n---@param links? table<string, string>\nfunction M.source(source, links)\n  ---@type table<string, string>\n  links = vim.tbl_extend(\"force\", {\n    Filename = \"TroubleFilename\",\n    Basename = \"TroubleFilename\",\n    Source = \"TroubleSource\",\n    Pos = \"TroublePos\",\n    Count = \"TroubleCount\",\n  }, links or {})\n  M.link(links, \"Trouble\" .. Util.camel(source))\nend\n\nM._fixed = {} ---@type table<string, string>\n---@param sl string\nfunction M.fix_statusline(sl, statusline_hl)\n  local bg = vim.api.nvim_get_hl(0, { name = statusline_hl, link = false })\n  bg = bg and bg.bg or nil\n\n  return sl:gsub(\"%%#(.-)#\", function(hl)\n    if not M._fixed[hl] then\n      local opts = vim.api.nvim_get_hl(0, { name = hl, link = false }) or {}\n      opts.bg = bg\n      local group = \"TroubleStatusline\" .. vim.tbl_count(M._fixed)\n      vim.api.nvim_set_hl(0, group, opts)\n      M._fixed[hl] = group\n    end\n    return \"%#\" .. M._fixed[hl] .. \"#\"\n  end)\nend\n\nreturn M\n"
  },
  {
    "path": "lua/trouble/config/init.lua",
    "content": "---@class trouble.Config.mod: trouble.Config\nlocal M = {}\n\n---@class trouble.Mode: trouble.Config,trouble.Section.spec\n---@field desc? string\n---@field sections? string[]\n\n---@class trouble.Config\n---@field mode? string\n---@field config? fun(opts:trouble.Config)\n---@field formatters? table<string,trouble.Formatter> custom formatters\n---@field filters? table<string, trouble.FilterFn> custom filters\n---@field sorters? table<string, trouble.SorterFn> custom sorters\nlocal defaults = {\n  debug = false,\n  auto_close = false, -- auto close when there are no items\n  auto_open = false, -- auto open when there are items\n  auto_preview = true, -- automatically open preview when on an item\n  auto_refresh = true, -- auto refresh when open\n  auto_jump = false, -- auto jump to the item when there's only one\n  focus = false, -- Focus the window when opened\n  restore = true, -- restores the last location in the list when opening\n  follow = true, -- Follow the current item\n  indent_guides = true, -- show indent guides\n  max_items = 200, -- limit number of items that can be displayed per section\n  multiline = true, -- render multi-line messages\n  pinned = false, -- When pinned, the opened trouble window will be bound to the current buffer\n  warn_no_results = true, -- show a warning when there are no results\n  open_no_results = false, -- open the trouble window when there are no results\n  ---@type trouble.Window.opts\n  win = {}, -- window options for the results window. Can be a split or a floating window.\n  -- Window options for the preview window. Can be a split, floating window,\n  -- or `main` to show the preview in the main editor window.\n  ---@type trouble.Window.opts\n  preview = {\n    type = \"main\",\n    -- when a buffer is not yet loaded, the preview window will be created\n    -- in a scratch buffer with only syntax highlighting enabled.\n    -- Set to false, if you want the preview to always be a real loaded buffer.\n    scratch = true,\n  },\n  -- Throttle/Debounce settings. Should usually not be changed.\n  ---@type table<string, number|{ms:number, debounce?:boolean}>\n  throttle = {\n    refresh = 20, -- fetches new data when needed\n    update = 10, -- updates the window\n    render = 10, -- renders the window\n    follow = 100, -- follows the current item\n    preview = { ms = 100, debounce = true }, -- shows the preview for the current item\n  },\n  -- Key mappings can be set to the name of a builtin action,\n  -- or you can define your own custom action.\n  ---@type table<string, trouble.Action.spec|false>\n  keys = {\n    [\"?\"] = \"help\",\n    r = \"refresh\",\n    R = \"toggle_refresh\",\n    q = \"close\",\n    o = \"jump_close\",\n    [\"<esc>\"] = \"cancel\",\n    [\"<cr>\"] = \"jump\",\n    [\"<2-leftmouse>\"] = \"jump\",\n    [\"<c-s>\"] = \"jump_split\",\n    [\"<c-v>\"] = \"jump_vsplit\",\n    -- go down to next item (accepts count)\n    -- j = \"next\",\n    [\"}\"] = \"next\",\n    [\"]]\"] = \"next\",\n    -- go up to prev item (accepts count)\n    -- k = \"prev\",\n    [\"{\"] = \"prev\",\n    [\"[[\"] = \"prev\",\n    dd = \"delete\",\n    d = { action = \"delete\", mode = \"v\" },\n    i = \"inspect\",\n    p = \"preview\",\n    P = \"toggle_preview\",\n    zo = \"fold_open\",\n    zO = \"fold_open_recursive\",\n    zc = \"fold_close\",\n    zC = \"fold_close_recursive\",\n    za = \"fold_toggle\",\n    zA = \"fold_toggle_recursive\",\n    zm = \"fold_more\",\n    zM = \"fold_close_all\",\n    zr = \"fold_reduce\",\n    zR = \"fold_open_all\",\n    zx = \"fold_update\",\n    zX = \"fold_update_all\",\n    zn = \"fold_disable\",\n    zN = \"fold_enable\",\n    zi = \"fold_toggle_enable\",\n    gb = { -- example of a custom action that toggles the active view filter\n      action = function(view)\n        view:filter({ buf = 0 }, { toggle = true })\n      end,\n      desc = \"Toggle Current Buffer Filter\",\n    },\n    s = { -- example of a custom action that toggles the severity\n      action = function(view)\n        local f = view:get_filter(\"severity\")\n        local severity = ((f and f.filter.severity or 0) + 1) % 5\n        view:filter({ severity = severity }, {\n          id = \"severity\",\n          template = \"{hl:Title}Filter:{hl} {severity}\",\n          del = severity == 0,\n        })\n      end,\n      desc = \"Toggle Severity Filter\",\n    },\n  },\n  ---@type table<string, trouble.Mode>\n  modes = {\n    -- sources define their own modes, which you can use directly,\n    -- or override like in the example below\n    lsp_references = {\n      -- some modes are configurable, see the source code for more details\n      params = {\n        include_declaration = true,\n      },\n    },\n    -- The LSP base mode for:\n    -- * lsp_definitions, lsp_references, lsp_implementations\n    -- * lsp_type_definitions, lsp_declarations, lsp_command\n    lsp_base = {\n      params = {\n        -- don't include the current location in the results\n        include_current = false,\n      },\n    },\n    -- more advanced example that extends the lsp_document_symbols\n    symbols = {\n      desc = \"document symbols\",\n      mode = \"lsp_document_symbols\",\n      focus = false,\n      win = { position = \"right\" },\n      filter = {\n        -- remove Package since luals uses it for control flow structures\n        [\"not\"] = { ft = \"lua\", kind = \"Package\" },\n        any = {\n          -- all symbol kinds for help / markdown files\n          ft = { \"help\", \"markdown\" },\n          -- default set of symbol kinds\n          kind = {\n            \"Class\",\n            \"Constructor\",\n            \"Enum\",\n            \"Field\",\n            \"Function\",\n            \"Interface\",\n            \"Method\",\n            \"Module\",\n            \"Namespace\",\n            \"Package\",\n            \"Property\",\n            \"Struct\",\n            \"Trait\",\n          },\n        },\n      },\n    },\n  },\n  -- stylua: ignore\n  icons = {\n    ---@type trouble.Indent.symbols\n    indent = {\n      top           = \"│ \",\n      middle        = \"├╴\",\n      last          = \"└╴\",\n      -- last          = \"-╴\",\n      -- last       = \"╰╴\", -- rounded\n      fold_open     = \" \",\n      fold_closed   = \" \",\n      ws            = \"  \",\n    },\n    folder_closed   = \" \",\n    folder_open     = \" \",\n    kinds = {\n      Array         = \" \",\n      Boolean       = \"󰨙 \",\n      Class         = \" \",\n      Constant      = \"󰏿 \",\n      Constructor   = \" \",\n      Enum          = \" \",\n      EnumMember    = \" \",\n      Event         = \" \",\n      Field         = \" \",\n      File          = \" \",\n      Function      = \"󰊕 \",\n      Interface     = \" \",\n      Key           = \" \",\n      Method        = \"󰊕 \",\n      Module        = \" \",\n      Namespace     = \"󰦮 \",\n      Null          = \" \",\n      Number        = \"󰎠 \",\n      Object        = \" \",\n      Operator      = \" \",\n      Package       = \" \",\n      Property      = \" \",\n      String        = \" \",\n      Struct        = \"󰆼 \",\n      TypeParameter = \" \",\n      Variable      = \"󰀫 \",\n    },\n  },\n}\n\n---@type trouble.Config\nlocal options\n\n---@param opts? trouble.Config\nfunction M.setup(opts)\n  if vim.fn.has(\"nvim-0.9.2\") == 0 then\n    local msg = \"trouble.nvim requires Neovim >= 0.9.2\"\n    vim.notify_once(msg, vim.log.levels.ERROR, { title = \"trouble.nvim\" })\n    error(msg)\n    return\n  end\n  opts = opts or {}\n\n  if opts.auto_open then\n    require(\"trouble.util\").warn({\n      \"You specified `auto_open = true` in your global config.\",\n      \"This is probably not what you want.\",\n      \"Add it to the mode you want to auto open instead.\",\n      \"```lua\",\n      \"opts = {\",\n      \"  modes = {\",\n      \"    diagnostics = { auto_open = true },\",\n      \"  }\",\n      \"}\",\n      \"```\",\n      \"Disabling global `auto_open`.\",\n    })\n    opts.auto_open = nil\n  end\n  opts.mode = nil\n  options = {}\n  options = M.get(opts)\n  require(\"trouble.config.highlights\").setup()\n  vim.api.nvim_create_user_command(\"Trouble\", function(input)\n    require(\"trouble.command\").execute(input)\n  end, {\n    nargs = \"*\",\n    complete = function(...)\n      return require(\"trouble.command\").complete(...)\n    end,\n    desc = \"Trouble\",\n  })\n  require(\"trouble.view.main\").setup()\n  vim.schedule(function()\n    for mode, mode_opts in pairs(options.modes) do\n      if mode_opts.auto_open then\n        require(\"trouble.view\").new(M.get(mode))\n      end\n    end\n  end)\n  return options\nend\n\n--- Update the default config.\n--- Should only be used by source to extend the default config.\n---@param config trouble.Config\nfunction M.defaults(config)\n  options = vim.tbl_deep_extend(\"force\", config, options)\nend\n\nfunction M.modes()\n  require(\"trouble.sources\").load()\n  local ret = {} ---@type string[]\n  for k, v in pairs(options.modes) do\n    if v.source or v.mode or v.sections then\n      ret[#ret + 1] = k\n    end\n  end\n  table.sort(ret)\n  return ret\nend\n\n---@param ...? trouble.Config|string\n---@return trouble.Config\nfunction M.get(...)\n  options = options or M.setup()\n\n  -- check if we need to load sources\n  for i = 1, select(\"#\", ...) do\n    ---@type trouble.Config?\n    local opts = select(i, ...)\n    if type(opts) == \"string\" or (type(opts) == \"table\" and opts.mode) then\n      M.modes() -- trigger loading of sources\n      break\n    end\n  end\n\n  ---@type trouble.Config[]\n  local all = { {}, defaults, options or {} }\n\n  ---@type table<string, boolean>\n  local modes = {}\n  local first_mode ---@type string?\n\n  for i = 1, select(\"#\", ...) do\n    ---@type trouble.Config?\n    local opts = select(i, ...)\n    if type(opts) == \"string\" then\n      opts = { mode = opts }\n    end\n    if opts then\n      table.insert(all, opts)\n      local idx = #all\n      while opts.mode and not modes[opts.mode] do\n        first_mode = first_mode or opts.mode\n        modes[opts.mode or \"\"] = true\n        opts = options.modes[opts.mode] or {}\n        table.insert(all, idx, opts)\n      end\n    end\n  end\n\n  local ret = vim.tbl_deep_extend(\"force\", unpack(all))\n\n  if type(ret.config) == \"function\" then\n    ret.config(ret)\n  end\n  ret.mode = first_mode\n\n  return ret\nend\n\nreturn setmetatable(M, {\n  __index = function(_, key)\n    options = options or M.setup()\n    assert(options, \"should be setup\")\n    return options[key]\n  end,\n})\n"
  },
  {
    "path": "lua/trouble/config/parser.lua",
    "content": "local M = {}\n\n---@param t table<any, any>\n---@param dotted_key string\n---@param value any\nfunction M.dotset(t, dotted_key, value)\n  local keys = vim.split(dotted_key, \".\", { plain = true })\n  for i = 1, #keys - 1 do\n    local key = keys[i]\n    t[key] = t[key] or {}\n    if type(t[key]) ~= \"table\" then\n      t[key] = {}\n    end\n    t = t[key]\n  end\n  ---@diagnostic disable-next-line: no-unknown\n  t[keys[#keys]] = value\nend\n\n---@return {args: string[], opts: table<string, any>, errors: string[]}\nfunction M.parse(input)\n  ---@type string?, string?\n  local positional, options = input:match(\"^%s*(.-)%s*([a-z%._]+%s*=.*)$\")\n  positional = positional or input\n  positional = vim.trim(positional)\n  local ret = {\n    args = positional == \"\" and {} or vim.split(positional, \"%s+\"),\n    opts = {},\n    errors = {},\n  }\n  if not options then\n    return ret\n  end\n  input = options\n  local parser = vim.treesitter.get_string_parser(input, \"lua\")\n  parser:parse()\n  local query = vim.treesitter.query.parse(\n    \"lua\",\n    [[\n      (ERROR) @error\n      (assignment_statement (variable_list name: (_)) @name)\n      (assignment_statement (expression_list value: (_)) @value)\n      (_ value: (identifier) @global (#has-ancestor? @global expression_list))\n    ]]\n  )\n  ---@type table<string, any>\n  local env = {\n    dotset = M.dotset,\n    opts = ret.opts,\n  }\n  local lines = {} ---@type string[]\n  local name = \"\"\n  ---@diagnostic disable-next-line: missing-parameter\n  for id, node in query:iter_captures(parser:trees()[1]:root(), input) do\n    local capture = query.captures[id]\n    local text = vim.treesitter.get_node_text(node, input)\n    if capture == \"name\" then\n      name = text\n    elseif capture == \"value\" then\n      table.insert(lines, (\"dotset(opts, %q, %s)\"):format(name, text))\n    elseif capture == \"global\" then\n      env[text] = text\n    elseif capture == \"error\" then\n      table.insert(ret.errors, text)\n    end\n  end\n  local ok, err = pcall(function()\n    local code = table.concat(lines, \"\\n\")\n    env.vim = vim\n    -- selene: allow(incorrect_standard_library_use)\n    local chunk = load(code, \"trouble\", \"t\", env)\n    chunk()\n  end)\n  if not ok then\n    table.insert(ret.errors, err)\n  end\n  return ret\nend\n\nreturn M\n"
  },
  {
    "path": "lua/trouble/docs.lua",
    "content": "local Config = require(\"trouble.config\")\nlocal Docs = require(\"lazy.docs\")\nlocal LazyUtil = require(\"lazy.util\")\n\nlocal M = {}\n\nfunction M.update()\n  local config = Docs.extract(\"lua/trouble/config/init.lua\", \"\\n(--@class trouble%.Mode.-\\n})\")\n  config = config:gsub(\"%s*debug = false.\\n\", \"\\n\")\n  Docs.save({\n    config = config,\n    colors = Docs.colors({\n      modname = \"trouble.config.highlights\",\n      path = \"lua/trouble/config/highlights.lua\",\n      name = \"Trouble\",\n    }),\n    modes = M.modes(),\n    api = M.api(),\n  })\nend\n\n---@return ReadmeBlock\nfunction M.modes()\n  ---@type string[]\n  local lines = {}\n\n  local exclude = { \"fs\", \"todo\" }\n  local modes = Config.modes()\n  for _, mode in ipairs(modes) do\n    if not vim.tbl_contains(exclude, mode) then\n      local m = Config.get(mode)\n      lines[#lines + 1] = (\"- **%s**: %s\"):format(mode, m.desc or \"\")\n    end\n  end\n\n  return { content = table.concat(lines, \"\\n\") }\nend\n\n---@return ReadmeBlock\nfunction M.api()\n  local lines = vim.split(LazyUtil.read_file(\"lua/trouble/api.lua\"), \"\\n\")\n\n  local funcs = {}\n\n  ---@type string[]\n  local f = {}\n\n  for _, line in ipairs(lines) do\n    if line:match(\"^%-%-\") then\n      f[#f + 1] = line\n    elseif line:match(\"^function\") and not line:match(\"^function M%._\") then\n      f[#f + 1] = line:gsub(\"^function M\", [[require(\"trouble\")]])\n      funcs[#funcs + 1] = table.concat(f, \"\\n\")\n      f = {}\n    else\n      f = {}\n    end\n  end\n\n  lines = vim.split(LazyUtil.read_file(\"lua/trouble/config/actions.lua\"), \"\\n\")\n  f = {}\n  ---@type table<string, string>\n  local comments = {}\n\n  for _, line in ipairs(lines) do\n    if line:match(\"^%s*%-%-\") then\n      f[#f + 1] = line:gsub(\"^%s*[%-]*%s*\", \"\")\n    elseif line:match(\"^%s*[%w_]+ = function\") then\n      local name = line:match(\"^%s*([%w_]+)\")\n      if not name:match(\"^_\") and #f > 0 then\n        comments[name] = table.concat(f, \"\\n\")\n      end\n      f = {}\n    else\n      f = {}\n    end\n  end\n  local Actions = require(\"trouble.config.actions\")\n  local names = vim.tbl_keys(Actions)\n  table.sort(names)\n\n  local exclude = { \"close\" }\n\n  for _, k in ipairs(names) do\n    local desc = comments[k] or k:gsub(\"_\", \" \")\n    local action = Actions[k]\n    if type(Actions[k]) == \"table\" then\n      desc = action.desc or desc\n      action = action.action\n    end\n    desc = table.concat(\n      vim.tbl_map(function(line)\n        return (\"-- %s\"):format(line)\n      end, vim.split(desc, \"\\n\")),\n      \"\\n\"\n    )\n    if type(action) == \"function\" and not vim.tbl_contains(exclude, k) then\n      funcs[#funcs + 1] = ([[\n%s\n---@param opts? trouble.Mode | { new? : boolean } | string\n---@return trouble.View\nrequire(\"trouble\").%s(opts)]]):format(desc, k)\n    end\n  end\n  return { content = table.concat(funcs, \"\\n\\n\"), lang = \"lua\" }\nend\n\nM.update()\nprint(\"Updated docs\")\n-- M.api()\n\nreturn M\n"
  },
  {
    "path": "lua/trouble/filter.lua",
    "content": "local Util = require(\"trouble.util\")\n\nlocal M = {}\n\n---@class trouble.ViewFilter.opts\n---@field id? string\n---@field template? string\n---@field data? table<string, any>\n---@field toggle? boolean\n---@field del? boolean\n\n---@class trouble.ViewFilter\n---@field id string\n---@field filter trouble.Filter\n---@field template? string\n---@field data? table<string, any>\n\n---@param opts? {lines:boolean}\n---@param range trouble.Range\n---@param pos trouble.Pos\nfunction M.overlaps(pos, range, opts)\n  if opts and opts.lines then\n    return pos[1] >= range.pos[1] and pos[1] <= range.end_pos[1]\n  else\n    return (pos[1] > range.pos[1] or (pos[1] == range.pos[1] and pos[2] >= range.pos[2]))\n      and (pos[1] < range.end_pos[1] or (pos[1] == range.end_pos[1] and pos[2] <= range.end_pos[2]))\n  end\nend\n\n---@alias trouble.Filter.ctx {opts:trouble.Config, main?:trouble.Main}\n---@alias trouble.FilterFn fun(item:trouble.Item, value: any, ctx:trouble.Filter.ctx): boolean\n---@class trouble.Filters: {[string]: trouble.FilterFn}\nM.filters = {\n  buf = function(item, buf, ctx)\n    if buf == 0 then\n      return ctx.main and ctx.main.filename == item.filename or false\n    end\n    return item.buf == buf\n  end,\n  ---@param fts string|string[]\n  ft = function(item, fts, _)\n    fts = type(fts) == \"table\" and fts or { fts }\n    local ft = item.buf and vim.bo[item.buf].filetype\n    return ft and vim.tbl_contains(fts, ft) or false\n  end,\n  range = function(item, buf, ctx)\n    local main = ctx.main\n    if not main or (main.buf ~= item.buf) then\n      return false\n    end\n    local range = item.range --[[@as trouble.Range]]\n    if range then\n      return M.overlaps(main.cursor, range, { lines = true })\n    else\n      return M.overlaps(main.cursor, item, { lines = true })\n    end\n  end,\n  [\"not\"] = function(item, filter, ctx)\n    ---@cast filter trouble.Filter\n    return not M.is(item, filter, ctx)\n  end,\n  any = function(item, any, ctx)\n    ---@cast any trouble.Filter[]\n    for k, f in pairs(any) do\n      if type(k) == \"string\" then\n        f = { [k] = f }\n      end\n      if M.is(item, f, ctx) then\n        return true\n      end\n    end\n    return false\n  end,\n}\n\n---@param item trouble.Item\n---@param filter trouble.Filter\n---@param ctx trouble.Filter.ctx\nfunction M.is(item, filter, ctx)\n  if type(filter) == \"table\" and Util.islist(filter) then\n    for _, f in ipairs(filter) do\n      if not M.is(item, f, ctx) then\n        return false\n      end\n    end\n    return true\n  end\n\n  filter = type(filter) == \"table\" and filter or { filter }\n  for k, v in pairs(filter) do\n    ---@type trouble.FilterFn?\n    local filter_fn = ctx.opts.filters and ctx.opts.filters[k] or M.filters[k]\n    if filter_fn then\n      if not filter_fn(item, v, ctx) then\n        return false\n      end\n    elseif type(k) == \"number\" then\n      if type(v) == \"function\" then\n        if not v(item) then\n          return false\n        end\n      elseif not item[v] then\n        return false\n      end\n    elseif type(v) == \"table\" then\n      if not vim.tbl_contains(v, item[k]) then\n        return false\n      end\n    elseif item[k] ~= v then\n      return false\n    end\n  end\n  return true\nend\n\n---@param items trouble.Item[]\n---@param filter? trouble.Filter\n---@param ctx trouble.Filter.ctx\nfunction M.filter(items, filter, ctx)\n  -- fast path for empty filter\n  if not filter or (type(filter) == \"table\" and vim.tbl_isempty(filter)) then\n    return items, {}\n  end\n  if type(filter) == \"function\" then\n    return filter(items)\n  end\n  local ret = {} ---@type trouble.Item[]\n  for _, item in ipairs(items) do\n    if M.is(item, filter, ctx) then\n      ret[#ret + 1] = item\n    end\n  end\n  return ret\nend\n\nreturn M\n"
  },
  {
    "path": "lua/trouble/format.lua",
    "content": "local Cache = require(\"trouble.cache\")\nlocal Util = require(\"trouble.util\")\n\nlocal M = {}\n\n---@alias trouble.spec.format string|trouble.Format|(string|trouble.Format)[]\n---@alias trouble.Format {text:string, hl?:string}\n\n---@alias trouble.Formatter fun(ctx: trouble.Formatter.ctx): trouble.spec.format?\n---@alias trouble.Formatter.ctx {item: trouble.Item, node:trouble.Node, field:string, value:string, opts:trouble.Config}\n\n---@param source string\n---@param field string\nfunction M.default_hl(source, field)\n  if not source then\n    return \"Trouble\" .. Util.camel(field)\n  end\n  local key = source .. field\n  local value = Cache.default_hl[key]\n  if value then\n    return value\n  end\n  local hl = \"Trouble\" .. Util.camel(source) .. Util.camel(field)\n  Cache.default_hl[key] = hl\n  return hl\nend\n\n---@type (fun(file: string, ext: string): string, string)[]\nlocal icons = {\n  function(file)\n    return require(\"mini.icons\").get(\"file\", file)\n  end,\n  function(file, ext)\n    return require(\"nvim-web-devicons\").get_icon(file, ext, { default = true })\n  end,\n}\nfunction M.get_icon(file, ext)\n  while #icons > 0 do\n    local ok, icon, hl = pcall(icons[1], file, ext)\n    if ok then\n      return icon, hl\n    end\n    table.remove(icons, 1)\n  end\nend\n\n---@param fn trouble.Formatter\n---@param field string\nfunction M.cached_formatter(fn, field)\n  local cache = {}\n  ---@param ctx trouble.Formatter.ctx\n  return function(ctx)\n    local key = ctx.item.source .. field .. (ctx.item[field] or \"\")\n    local result = cache[key]\n    if result then\n      return result\n    end\n    result = fn(ctx)\n    cache[key] = result\n    return result\n  end\nend\n\n---@type table<string, trouble.Formatter>\nM.formatters = {\n  pos = function(ctx)\n    return {\n      text = \"[\" .. ctx.item.pos[1] .. \", \" .. (ctx.item.pos[2] + 1) .. \"]\",\n    }\n  end,\n  code = function(ctx)\n    if not ctx.item.code or ctx.item.code == vim.NIL then\n      return\n    end\n    return {\n      text = \"(\" .. ctx.item.code .. \")\",\n      hl = \"TroubleCode\",\n    }\n  end,\n  severity = function(ctx)\n    local severity = ctx.item.severity or vim.diagnostic.severity.ERROR\n    local name = vim.diagnostic.severity[severity] or \"OTHER\"\n    return {\n      text = name,\n      hl = \"Diagnostic\" .. Util.camel(name:lower()),\n    }\n  end,\n  severity_icon = function(ctx)\n    local severity = ctx.item.severity or vim.diagnostic.severity.ERROR\n    if not vim.diagnostic.severity[severity] then\n      return\n    end\n    if type(severity) == \"string\" then\n      severity = vim.diagnostic.severity[severity:upper()] or vim.diagnostic.severity.ERROR\n    end\n    local name = Util.camel(vim.diagnostic.severity[severity]:lower())\n    local sign = vim.fn.sign_getdefined(\"DiagnosticSign\" .. name)[1]\n    if vim.fn.has(\"nvim-0.10.0\") == 1 then\n      local config = vim.diagnostic.config() or {}\n      if config.signs == nil or type(config.signs) == \"boolean\" then\n        return { text = sign and sign.text or name:sub(1, 1), hl = \"DiagnosticSign\" .. name }\n      end\n      local signs = config.signs or {}\n      if type(signs) == \"function\" then\n        signs = signs(0, 0) --[[@as vim.diagnostic.Opts.Signs]]\n      end\n      return {\n        text = type(signs) == \"table\" and signs.text and signs.text[severity] or sign and sign.text or name:sub(1, 1),\n        hl = \"DiagnosticSign\" .. name,\n      }\n    else\n      return sign and { text = sign.text, hl = sign.texthl } or { text = name } or nil\n    end\n  end,\n  file_icon = function(ctx)\n    local item = ctx.item --[[@as Diagnostic|trouble.Item]]\n    local file = vim.fn.fnamemodify(item.filename, \":t\")\n    local ext = vim.fn.fnamemodify(item.filename, \":e\")\n    local icon, color = M.get_icon(file, ext)\n    return icon and { text = icon .. \" \", hl = color } or \"\"\n  end,\n  count = function(ctx)\n    return {\n      text = (\" %d \"):format(ctx.node:count()),\n    }\n  end,\n  filename = function(ctx)\n    return {\n      text = vim.fn.fnamemodify(ctx.item.filename, \":p:~:.\"),\n    }\n  end,\n  dirname = function(ctx)\n    return {\n      text = vim.fn.fnamemodify(ctx.item.dirname, \":p:~:.\"),\n    }\n  end,\n  filter = function(ctx)\n    return {\n      text = vim.inspect(ctx.item.filter):gsub(\"%s+\", \" \"),\n      hl = \"ts.lua\",\n    }\n  end,\n  kind_icon = function(ctx)\n    if not ctx.item.kind then\n      return\n    end\n    local icon = ctx.opts.icons.kinds[ctx.item.kind]\n    if icon then\n      return {\n        text = icon,\n        hl = \"TroubleIcon\" .. ctx.item.kind,\n      }\n    end\n  end,\n  directory = function(ctx)\n    if ctx.node:source() == \"fs\" then\n      local directory = ctx.item.directory or \"\"\n      local parent = ctx.node:parent_item()\n      if parent and parent.directory then\n        directory = directory:sub(#parent.directory + 1)\n        return { text = directory, hl = \"TroubleDirectory\" }\n      end\n      return { text = vim.fn.fnamemodify(directory, \":~\"), hl = \"TroubleDirectory\" }\n    end\n  end,\n  directory_icon = function(ctx)\n    if ctx.node:source() == \"fs\" then\n      local text = ctx.node.folded and ctx.opts.icons.folder_closed or ctx.opts.icons.folder_open\n      return { text = text, hl = \"TroubleIconDirectory\" }\n    end\n  end,\n}\nM.formatters.severity_icon = M.cached_formatter(M.formatters.severity_icon, \"severity\")\nM.formatters.severity = M.cached_formatter(M.formatters.severity, \"severity\")\n\n---@param ctx trouble.Formatter.ctx\nfunction M.field(ctx)\n  ---@type trouble.Format[]\n  local format = { { fi = ctx.field, text = vim.trim(tostring(ctx.item[ctx.field] or \"\")) } }\n\n  local opts = ctx.opts\n\n  local formatter = opts.formatters and opts.formatters[ctx.field] or M.formatters[ctx.field]\n\n  if formatter then\n    local result = formatter(ctx)\n    if not result then\n      return\n    end\n    result = type(result) == \"table\" and Util.islist(result) and result or { result }\n    format = {}\n    ---@cast result (string|trouble.Format)[]\n    for _, f in ipairs(result) do\n      ---@diagnostic disable-next-line: assign-type-mismatch\n      format[#format + 1] = type(f) == \"string\" and { text = f } or f\n    end\n  end\n  for _, f in ipairs(format) do\n    f.hl = f.hl or M.default_hl(ctx.item.source, ctx.field)\n  end\n  return format\nend\n\n---@param format string\n---@param ctx {item: trouble.Item, node:trouble.Node, opts:trouble.Config}\nfunction M.format(format, ctx)\n  ---@type trouble.Format[]\n  local ret = {}\n  local hl ---@type string?\n  while true do\n    ---@type string?,string,string\n    local before, fields, after = format:match(\"^(.-){(.-)}(.*)$\")\n    if not before then\n      break\n    end\n    format = after\n    if #before > 0 then\n      ret[#ret + 1] = { text = before, hl = hl }\n    end\n\n    for _, field in Util.split(fields, \"|\") do\n      ---@type string,string\n      local field_name, field_hl = field:match(\"^(.-):(.+)$\")\n      if field_name then\n        field = field_name\n      end\n      if field == \"hl\" then\n        hl = field_hl\n      else\n        ---@cast ctx trouble.Formatter.ctx\n        ctx.field = field\n        ctx.value = ctx.item[field]\n        local ff = M.field(ctx)\n        if ff then\n          for _, f in ipairs(ff) do\n            if hl or field_hl then\n              f.hl = field_hl or hl\n            end\n            ret[#ret + 1] = f\n          end\n          -- only render the first field\n          break\n        end\n      end\n    end\n  end\n  if #format > 0 then\n    ret[#ret + 1] = { text = format, hl = hl }\n  end\n  return ret\nend\n\nreturn M\n"
  },
  {
    "path": "lua/trouble/init.lua",
    "content": "---@class trouble: trouble.api\nlocal M = {}\n\n---@param opts? trouble.Config\nfunction M.setup(opts)\n  require(\"trouble.config\").setup(opts)\nend\n\nreturn setmetatable(M, {\n  __index = function(_, k)\n    return require(\"trouble.api\")[k]\n  end,\n})\n"
  },
  {
    "path": "lua/trouble/item.lua",
    "content": "local Cache = require(\"trouble.cache\")\nlocal Util = require(\"trouble.util\")\n\n---@alias trouble.Pos {[1]:number, [2]:number}\n---@class trouble.Range\n---@field pos trouble.Pos\n---@field end_pos trouble.Pos\n\n---@class trouble.Item: {[string]: any}\n---@field id? string\n---@field parent? trouble.Item\n---@field buf? number\n---@field filename string\n---@field pos trouble.Pos (1,0)-indexed\n---@field end_pos? trouble.Pos (1,0)-indexed\n---@field item table<string,any>\n---@field source string\n---@field cache table<string,any>\n---@field range? trouble.Range\nlocal M = {}\n\n---@param opts trouble.Item | {filename?:string}\nfunction M.new(opts)\n  local self = opts\n  assert(self.source, \"source is required\")\n  self.pos = self.pos or { 1, 0 }\n  self.pos[1] = math.max(self.pos[1] or 1, 1)\n  self.pos[2] = math.max(self.pos[2] or 0, 0)\n  self.end_pos = self.end_pos or self.pos\n  self.item = self.item or {}\n  if self.buf and not self.filename then\n    self.filename = vim.api.nvim_buf_get_name(self.buf)\n    if self.filename == \"\" then\n      self.filename = \"[buffer:\" .. self.buf .. \"]\"\n    end\n  end\n  assert(self.filename, \"filename is required\")\n  if self.filename then\n    self.filename = vim.fs.normalize(self.filename)\n    local parts = vim.split(self.filename, \"/\", { plain = true })\n    self.basename = table.remove(parts)\n    self.dirname = table.concat(parts, \"/\")\n  end\n  self.cache = Cache.new(\"item\")\n  return setmetatable(self, M)\nend\n\n---@param items trouble.Item[]\n---@param fields? string[]\nfunction M.add_id(items, fields)\n  for _, item in ipairs(items) do\n    if not item.id then\n      local id = {\n        item.source,\n        item.filename,\n        item.pos[1] or \"\",\n        item.pos[2] or \"\",\n        item.end_pos[1] or \"\",\n        item.end_pos[2] or \"\",\n      }\n      for _, field in ipairs(fields or {}) do\n        table.insert(id, item[field] or \"\")\n      end\n      item.id = table.concat(id, \":\")\n    end\n  end\nend\n\n---@return string?\nfunction M:get_ft(buf)\n  if self.buf and vim.api.nvim_buf_is_loaded(self.buf) then\n    return vim.bo[self.buf].filetype\n  end\n  if not self.filename then\n    return\n  end\n  local ft = Cache.ft[self.filename]\n  if ft == nil then\n    -- HACK: make sure we always pass a valid buf,\n    -- otherwise some detectors will fail hard (like ts)\n    ft = vim.filetype.match({ filename = self.filename, buf = buf or 0 })\n    Cache.ft[self.filename] = ft or false -- cache misses too\n  end\n  return ft\nend\n\nfunction M:get_lang(buf)\n  local ft = self:get_ft(buf)\n  return ft and ft ~= \"\" and vim.treesitter.language.get_lang(ft) or nil\nend\n\nfunction M:__index(k)\n  if type(k) ~= \"string\" then\n    return\n  end\n  if M[k] then\n    return M[k]\n  end\n  local item = rawget(self, \"item\")\n  ---@cast k string\n  if item and item[k] ~= nil then\n    return item[k]\n  end\n\n  local obj = self\n\n  local start = 1\n  while type(obj) == \"table\" do\n    local dot = k:find(\".\", start, true)\n    if not dot then\n      if start == 1 then\n        return\n      end\n      local ret = obj[k:sub(start)]\n      rawset(self, k, ret)\n      return ret\n    end\n    local key = k:sub(start, dot - 1)\n    obj = obj[key]\n    start = dot + 1\n  end\nend\n\n---@param item trouble.Item\nfunction M:add_child(item)\n  item.parent = self\nend\n\n---@param items trouble.Item[]\n---@param opts? {mode?:\"range\"|\"full\"|\"after\", multiline?:boolean}\nfunction M.add_text(items, opts)\n  opts = opts or {}\n  opts.mode = opts.mode or \"range\"\n  local todo = {} ---@type table<string, {buf?:number, rows:number[]}>\n\n  for _, item in ipairs(items) do\n    if not item.item.text and item.filename then\n      -- schedule to get the lines\n      todo[item.filename] = todo[item.filename] or { rows = {} }\n      todo[item.filename].buf = todo[item.filename].buf or item.buf\n      for r = item.pos[1], item.end_pos and item.end_pos[1] or item.pos[1] do\n        table.insert(todo[item.filename].rows, r)\n        if not opts.multiline then\n          break\n        end\n      end\n    end\n  end\n\n  -- get the lines and range text\n  local buf_lines = {} ---@type table<string, table<number, string>>\n  for path, t in pairs(todo) do\n    buf_lines[path] = Util.get_lines({\n      rows = t.rows,\n      buf = t.buf,\n      path = path,\n    }) or {}\n  end\n  for _, item in ipairs(items) do\n    if not item.item.text and item.filename then\n      local lines = {} ---@type string[]\n      for row = item.pos[1], item.end_pos[1] do\n        local line = buf_lines[item.filename][row] or \"\"\n        if row == item.pos[1] and row == item.end_pos[1] then\n          if opts.mode == \"after\" then\n            line = line:sub(item.pos[2] + 1)\n          elseif opts.mode == \"range\" then\n            line = line:sub(item.pos[2] + 1, item.end_pos[2])\n          end\n        elseif row == item.pos[1] then\n          line = line:sub(item.pos[2] + 1)\n        elseif row == item.end_pos[1] then\n          line = line:sub(1, item.end_pos[2]) --[[@as string]]\n        end\n        if line ~= \"\" then\n          lines[#lines + 1] = line\n        end\n      end\n      item.item.text = table.concat(lines, \"\\n\")\n    end\n  end\n  return items\nend\n\nreturn M\n"
  },
  {
    "path": "lua/trouble/promise.lua",
    "content": "local Util = require(\"trouble.util\")\n\n---@alias trouble.Promise.state \"pending\" | \"fulfilled\" | \"rejected\"\n\n---@class trouble.Promise\n---@field state trouble.Promise.state\n---@field value any?\n---@field queue (fun())[]\n---@field resolve fun(value)\n---@field reject fun(reason)\n---@field has_next boolean\nlocal P = {}\nP.__index = P\n\n--- Creates a new promise\n---@param executor fun(resolve: fun(value), reject: fun(reason))\n---@return trouble.Promise\nfunction P.new(executor)\n  local self = setmetatable({}, P)\n  self.state = \"pending\"\n  self.value = nil\n  self.queue = {}\n  self.has_next = false\n\n  ---@param state trouble.Promise.state\n  local function transition(state, result)\n    if self.state == \"pending\" then\n      self.state = state\n      self.value = result\n      for _, cb in ipairs(self.queue) do\n        cb()\n      end\n      if state == \"rejected\" and not self.has_next then\n        local bt = debug.traceback()\n        vim.schedule(function()\n          if not self.has_next then\n            Util.error(\"Unhandled promise rejection:\\n```lua\\n\" .. tostring(result) .. \"\\n\\n\" .. bt .. \"```\")\n          end\n        end)\n      end\n    end\n  end\n\n  self.resolve = function(value)\n    transition(\"fulfilled\", value)\n  end\n  self.reject = function(reason)\n    transition(\"rejected\", reason)\n  end\n\n  xpcall(function()\n    executor(self.resolve, self.reject)\n  end, function(err)\n    self.reject(err)\n  end)\n\n  return self\nend\n\n--- Adds fulfillment and rejection handlers to the promise\n---@param on_fulfilled? fun(value):any\n---@param on_rejected? fun(reason):any\n---@return trouble.Promise\nfunction P:next(on_fulfilled, on_rejected)\n  local next = P.new(function() end)\n\n  local function handle()\n    local callback = on_fulfilled\n    if self.state == \"rejected\" then\n      callback = on_rejected\n    end\n    if callback then\n      local ok, ret = pcall(callback, self.value)\n      if ok then\n        if ret and type(ret) == \"table\" and getmetatable(ret) == P then\n          ret:next(next.resolve, next.reject)\n        else\n          next.resolve(ret)\n        end\n      else\n        next.reject(ret) -- reject the next promise with the error\n      end\n    else\n      if self.state == \"fulfilled\" then\n        next.resolve(self.value)\n      else\n        next.reject(self.value)\n      end\n    end\n  end\n\n  if self.state ~= \"pending\" then\n    vim.schedule(handle) -- ensure the callback is called in the next event loop tick\n  else\n    table.insert(self.queue, handle)\n  end\n\n  self.has_next = true -- self.has_rejection_handler or (on_rejected ~= nil)\n  return next\nend\n\nfunction P:catch(on_rejected)\n  return self:next(nil, on_rejected)\nend\n\nfunction P:finally(on_finally)\n  return self:next(function(value)\n    return P.new(function(resolve)\n      on_finally()\n      resolve(value)\n    end)\n  end, function(reason)\n    return P.new(function(_, reject)\n      on_finally()\n      reject(reason)\n    end)\n  end)\nend\n\nfunction P:is_pending()\n  return self.state == \"pending\"\nend\n\nfunction P:timeout(ms)\n  return P.new(function(resolve, reject)\n    local timer = (vim.uv or vim.loop).new_timer()\n    timer:start(ms, 0, function()\n      timer:close()\n      vim.schedule(function()\n        reject(\"timeout\")\n      end)\n    end)\n    self:next(resolve, reject)\n  end)\nend\n\nlocal M = {}\n\nfunction M.resolve(value)\n  return P.new(function(resolve)\n    resolve(value)\n  end)\nend\n\nfunction M.reject(reason)\n  return P.new(function(_, reject)\n    reject(reason)\n  end)\nend\n\n---@param promises trouble.Promise[]\nfunction M.all(promises)\n  return P.new(function(resolve, reject)\n    local results = {}\n    local pending = #promises\n    if pending == 0 then\n      return resolve(results)\n    end\n    for i, promise in ipairs(promises) do\n      promise:next(function(value)\n        results[i] = value\n        pending = pending - 1\n        if pending == 0 then\n          resolve(results)\n        end\n      end, reject)\n    end\n  end)\nend\n\n---@param promises trouble.Promise[]\nfunction M.all_settled(promises)\n  return P.new(function(resolve)\n    local results = {}\n    local pending = #promises\n    if pending == 0 then\n      return resolve(results)\n    end\n    for i, promise in ipairs(promises) do\n      promise:next(function(value)\n        results[i] = { status = \"fulfilled\", value = value }\n        pending = pending - 1\n        if pending == 0 then\n          resolve(results)\n        end\n      end, function(reason)\n        results[i] = { status = \"rejected\", reason = reason }\n        pending = pending - 1\n        if pending == 0 then\n          resolve(results)\n        end\n      end)\n    end\n  end)\nend\n\nM.new = P.new\n\n-- M.new(function() end):timeout(1000)\n\nreturn M\n"
  },
  {
    "path": "lua/trouble/providers/telescope.lua",
    "content": "local T = require(\"trouble.sources.telescope\")\n\nreturn setmetatable({}, {\n  __index = function(_, k)\n    require(\"trouble.util\").warn(\n      ([[\n`%s()` is deprecated\n```lua\n-- Use this:\nrequire(\"trouble.sources.telescope\").open()\n\n-- Instead of:\nrequire(\"trouble.providers.telescope\").%s()\n]]):format(k, k),\n      { once = true }\n    )\n    return T.open\n  end,\n})\n"
  },
  {
    "path": "lua/trouble/sort.lua",
    "content": "local Filter = require(\"trouble.filter\")\n\nlocal M = {}\n\n---@alias trouble.Sort.ctx {opts:trouble.Config, main?:trouble.Main}\n\n---@type table<string, trouble.SorterFn>\nM.sorters = {\n  pos = function(obj)\n    -- Use large multipliers for higher priority fields to ensure their precedence in sorting\n    local primaryScore = obj.pos[1] * 1000000 + obj.pos[2] * 1000\n    local secondaryScore = obj.end_pos[1] * 1000000 + obj.end_pos[2] * 1000\n\n    return primaryScore + secondaryScore\n  end,\n}\n\n---@param items trouble.Item[]\n---@param opts? trouble.Sort[]\n---@param ctx trouble.Sort.ctx\nfunction M.sort(items, opts, ctx)\n  if not opts or #opts == 0 then\n    return items\n  end\n\n  local keys = {} ---@type table<trouble.Item, any[]>\n  local desc = {} ---@type boolean[]\n\n  -- pre-compute fields\n  local fields = {} ---@type trouble.Sort[]\n  for f, field in ipairs(opts) do\n    if field.field then\n      ---@diagnostic disable-next-line: no-unknown\n      local sorter = ctx.opts.sorters and ctx.opts.sorters[field.field] or M.sorters[field.field]\n      if sorter then\n        fields[f] = { sorter = sorter }\n      else\n        fields[f] = { field = field.field }\n      end\n    else\n      fields[f] = field\n    end\n    desc[f] = field.desc or false\n  end\n\n  -- pre-compute keys\n  for _, item in ipairs(items) do\n    local item_keys = {} ---@type any[]\n    for f, field in ipairs(fields) do\n      local key = nil\n      if field.sorter then\n        key = field.sorter(item)\n      elseif field.field then\n        ---@diagnostic disable-next-line: no-unknown\n        key = item[field.field]\n      elseif field.filter then\n        key = Filter.is(item, field.filter, ctx)\n      end\n      if type(key) == \"boolean\" then\n        key = key and 0 or 1\n      end\n      item_keys[f] = key\n    end\n    keys[item] = item_keys\n  end\n\n  -- sort items\n  table.sort(items, function(a, b)\n    local ka = keys[a]\n    local kb = keys[b]\n    for i = 1, #ka do\n      local fa = ka[i]\n      local fb = kb[i]\n      if fa ~= fb then\n        if desc[i] then\n          return fa > fb\n        else\n          return fa < fb\n        end\n      end\n    end\n    return false\n  end)\n  return items\nend\n\nreturn M\n"
  },
  {
    "path": "lua/trouble/sources/diagnostics.lua",
    "content": "---@diagnostic disable: inject-field\nlocal Item = require(\"trouble.item\")\n\n---@class trouble.Source.diagnostics: trouble.Source\nlocal M = {}\n\nM.highlights = {\n  Message = \"TroubleText\",\n  ItemSource = \"Comment\",\n  Code = \"Comment\",\n}\n\nM.config = {\n  modes = {\n    diagnostics = {\n      desc = \"diagnostics\",\n      events = { \"DiagnosticChanged\", \"BufEnter\" },\n      -- Trouble classic for other buffers,\n      -- but only if they are in the current directory\n      source = \"diagnostics\",\n      groups = {\n        -- { format = \"{hl:Special}󰚢 {hl} {hl:Title}Diagnostics{hl} {count}\" },\n        -- { \"severity\", format = \"{severity_icon} {severity} {count}\" },\n        -- { \"dirname\", format = \"{hl:Special} {hl} {dirname} {count}\" },\n        { \"directory\" },\n        { \"filename\", format = \"{file_icon} {basename} {count}\" },\n      },\n      sort = { \"severity\", \"filename\", \"pos\", \"message\" },\n      format = \"{severity_icon} {message:md} {item.source} {code} {pos}\",\n      -- filter = {\n      -- [\"not\"] = {\n      --   any = {\n      --     { severity = vim.diagnostic.severity.ERROR },\n      --     { buf = 0 },\n      --   },\n      -- },\n      -- function(item)\n      --   return item.filename:find((vim.loop or vim.uv).cwd(), 1, true)\n      -- end,\n      -- },\n    },\n    -- {\n    --   -- error from all files\n    --   source = \"diagnostics\",\n    --   groups = { \"severity\", \"code\", \"filename\" },\n    --   filter = {\n    --     -- severity = 1,\n    --   },\n    --   sort = { \"filename\", \"pos\" },\n    --   format = \"sig {severity_sign} {severity} file: {filename} pos: {pos}\",\n    -- },\n    -- {\n    --   -- diagnostics from current buffer\n    --   source = \"diagnostics\",\n    --   groups = { \"severity\", \"filename\" },\n    --   filter = {\n    --     buf = 0,\n    --   },\n    --   sort = { \"pos\" },\n    -- },\n  },\n}\n\n---@type table<number, trouble.Item[]>\nlocal cache = {}\n\nfunction M.setup()\n  vim.api.nvim_create_autocmd(\"DiagnosticChanged\", {\n    group = vim.api.nvim_create_augroup(\"trouble.diagnostics\", { clear = true }),\n    callback = function(event)\n      -- NOTE: unfortunately, we can't use the event.data.diagnostics table here,\n      -- since multiple namespaces exist and we can't tell which namespace the\n      -- diagnostics are from.\n      cache[event.buf] = vim.tbl_map(M.item, vim.diagnostic.get(event.buf))\n      cache[0] = nil\n    end,\n  })\n  for _, diag in ipairs(vim.diagnostic.get()) do\n    local buf = diag.bufnr\n    if buf and vim.api.nvim_buf_is_valid(buf) then\n      cache[buf] = cache[buf] or {}\n      table.insert(cache[buf], M.item(diag))\n      Item.add_id(cache[buf], { \"item.source\", \"severity\", \"code\" })\n    end\n  end\nend\n\n---@param diag vim.Diagnostic\nfunction M.item(diag)\n  return Item.new({\n    source = \"diagnostics\",\n    buf = diag.bufnr,\n    pos = { diag.lnum + 1, diag.col },\n    end_pos = { diag.end_lnum and (diag.end_lnum + 1) or nil, diag.end_col },\n    item = diag,\n  })\nend\n\n---@param cb trouble.Source.Callback\n---@param ctx trouble.Source.ctx)\nfunction M.get(cb, ctx)\n  -- PERF: pre-filter when possible\n  local buf = type(ctx.opts.filter) == \"table\" and ctx.opts.filter.buf or nil\n\n  if buf == 0 then\n    buf = ctx.main.buf\n  end\n\n  if buf then\n    cb(cache[buf] or {})\n  else\n    if not cache[0] then\n      cache[0] = {}\n      for b, items in pairs(cache) do\n        if b ~= 0 then\n          if vim.api.nvim_buf_is_valid(b) then\n            for _, item in ipairs(items) do\n              table.insert(cache[0], item)\n            end\n          else\n            cache[b] = nil\n          end\n        end\n      end\n    end\n    cb(cache[0])\n  end\nend\n\nreturn M\n"
  },
  {
    "path": "lua/trouble/sources/fzf.lua",
    "content": "---@diagnostic disable: inject-field\nlocal Item = require(\"trouble.item\")\n\n---Represents an item in a Neovim quickfix/loclist.\n---@class fzf.Item\n---@field stripped string the fzf item without any highlighting.\n---@field bufnr? number The buffer number of the item.\n---@field bufname? string\n---@field terminal? boolean\n---@field path string\n---@field uri? string\n---@field line number 1-indexed line number\n---@field col number 1-indexed column number\n\n---@class fzf.Opts\n\n---@class trouble.Source.fzf: trouble.Source\nlocal M = {}\n\n---@type trouble.Item[]\nM.items = {}\n\nM.config = {\n  modes = {\n    fzf = {\n      desc = \"FzfLua results previously opened with `require('trouble.sources.fzf').open()`.\",\n      source = \"fzf\",\n      groups = {\n        { \"cmd\", format = \"{hl:Title}fzf{hl} {cmd:Comment} {count}\" },\n        { \"filename\", format = \"{file_icon} {filename} {count}\" },\n      },\n      sort = { \"filename\", \"pos\" },\n      format = \"{text:ts} {pos}\",\n    },\n    fzf_files = {\n      desc = \"FzfLua results previously opened with `require('trouble.sources.fzf').open()`.\",\n      source = \"fzf\",\n      groups = {\n        { \"cmd\", format = \"{hl:Title}fzf{hl} {cmd:Comment} {count}\" },\n      },\n      sort = { \"filename\", \"pos\" },\n      format = \"{file_icon} {filename}\",\n    },\n  },\n}\n\n---@param item fzf.Item\nfunction M.item(item)\n  item.text = item.stripped:match(\":%d+:%d?%d?%d?%d?:?(.*)$\")\n  local word = item.text and item.text:sub(item.col):match(\"%S+\")\n  return Item.new({\n    source = \"fzf\",\n    buf = item.bufnr,\n    filename = item.bufname or item.path or item.uri,\n    pos = { item.line, item.col - 1 },\n    end_pos = word and { item.line, item.col - 1 + #word } or nil,\n    item = item,\n  })\nend\n\n---@param cb trouble.Source.Callback\n---@param _ctx trouble.Source.ctx)\nfunction M.get(cb, _ctx)\n  cb(M.items)\nend\n\n-- Returns the mode based on the items.\nfunction M.mode()\n  for _, item in ipairs(M.items) do\n    if item.text then\n      return \"fzf\"\n    end\n  end\n  return \"fzf_files\"\nend\n\n-- Append the current fzf buffer to the trouble list.\n---@param selected string[]\n---@param fzf_opts fzf.Opts\n---@param opts? trouble.Mode|string\nfunction M.add(selected, fzf_opts, opts)\n  local cmd = fzf_opts.__INFO.cmd\n  local path = require(\"fzf-lua.path\")\n  for _, line in ipairs(selected) do\n    local item = M.item(path.entry_to_file(line, fzf_opts))\n    item.item.cmd = cmd\n    table.insert(M.items, item)\n  end\n\n  vim.schedule(function()\n    opts = opts or {}\n    if type(opts) == \"string\" then\n      opts = { mode = opts }\n    end\n    opts = vim.tbl_extend(\"force\", { mode = M.mode() }, opts)\n    require(\"trouble\").open(opts)\n  end)\nend\n\n-- Opens the current fzf buffer in the trouble list.\n-- This will clear the existing items.\n---@param selected string[]\n---@param fzf_opts fzf.Opts\n---@param opts? trouble.Mode|string\nfunction M.open(selected, fzf_opts, opts)\n  M.items = {}\n  M.add(selected, fzf_opts, opts)\nend\n\nlocal smart_prefix = require(\"trouble.util\").is_win() and \"transform(IF %FZF_SELECT_COUNT% LEQ 0 (echo select-all))\"\n  or \"transform([ $FZF_SELECT_COUNT -eq 0 ] && echo select-all)\"\n\nM.actions = {\n  -- Open selected or all items in the trouble list.\n  open = { fn = M.open, prefix = smart_prefix, desc = \"smart-open-with-trouble\" },\n  -- Open selected items in the trouble list.\n  open_selected = { fn = M.open, desc = \"open-with-trouble\" },\n  -- Open all items in the trouble list.\n  open_all = { fn = M.open, prefix = \"select-all\", desc = \"open-all-with-trouble\" },\n  -- Add selected or all items to the trouble list.\n  add = { fn = M.add, prefix = smart_prefix, desc = \"smart-add-to-trouble\" },\n  -- Add selected items to the trouble list.\n  add_selected = { fn = M.add, desc = \"add-to-trouble\" },\n  -- Add all items to the trouble list.\n  add_all = { fn = M.add, prefix = \"select-all\", desc = \"add-all-to-trouble\" },\n}\n\nreturn M\n"
  },
  {
    "path": "lua/trouble/sources/init.lua",
    "content": "local Config = require(\"trouble.config\")\nlocal Util = require(\"trouble.util\")\n\n---@class trouble.Source\n---@field highlights? table<string, string>\n---@field config? trouble.Config\n---@field setup? fun()\n---@field get trouble.Source.get|table<string, trouble.Source.get>\n---@field preview? fun(item:trouble.Item, ctx:trouble.Preview)\n\n---@alias trouble.Source.ctx {main: trouble.Main, opts:trouble.Mode}\n---@alias trouble.Source.Callback fun(items:trouble.Item[])\n---@alias trouble.Source.get fun(cb:trouble.Source.Callback, ctx:trouble.Source.ctx)\n\nlocal M = {}\n---@type table<string, trouble.Source>\nM.sources = {}\n\n---@param name string\n---@param source? trouble.Source\nfunction M.register(name, source)\n  if M.sources[name] then\n    error(\"source already registered: \" .. name)\n  end\n  source = source or require(\"trouble.sources.\" .. name)\n  if source then\n    if source.setup then\n      source.setup()\n    end\n    require(\"trouble.config.highlights\").source(name, source.highlights)\n    if source.config then\n      Config.defaults(source.config)\n    end\n  end\n  M.sources[name] = source\n  return source\nend\n\n---@param source string\nfunction M.get(source)\n  local parent, child = source:match(\"^(.-)%.(.*)$\")\n  source = parent or source\n  local s = M.sources[source] or M.register(source)\n  if child and type(s.get) ~= \"table\" then\n    error(\"source does not support sub-sources: \" .. source)\n  elseif child and type(s.get[child]) ~= \"function\" then\n    error(\"source does not support sub-source: \" .. source .. \".\" .. child)\n  end\n  return (child and s.get[child] or s.get), s\nend\n\nfunction M.load()\n  local rtp = vim.api.nvim_get_runtime_file(\"lua/trouble/sources/*.lua\", true)\n  for _, file in ipairs(rtp) do\n    local name = file:match(\"lua[/\\\\]trouble[/\\\\]sources[/\\\\](.*)%.lua\")\n    if name and name ~= \"init\" and not M.sources[name] then\n      Util.try(function()\n        M.register(name)\n      end, { msg = \"Error loading source: \" .. name })\n    end\n  end\nend\n\nreturn M\n"
  },
  {
    "path": "lua/trouble/sources/lsp.lua",
    "content": "local Cache = require(\"trouble.cache\")\nlocal Config = require(\"trouble.config\")\nlocal Filter = require(\"trouble.filter\")\nlocal Item = require(\"trouble.item\")\nlocal Promise = require(\"trouble.promise\")\nlocal Util = require(\"trouble.util\")\n\nlocal str_byteindex_new = pcall(vim.str_byteindex, \"aa\", \"utf-8\", 1)\n\n---@param s string line to be indexed\n---@param index integer UTF index\n---@param encoding string utf-8|utf-16|utf-32| defaults to utf-16\n---@return integer byte (utf-8) index of `encoding` index `index` in `line`\nlocal function get_line_col(s, index, encoding)\n  if str_byteindex_new then\n    return vim.str_byteindex(s, encoding, index, false)\n  elseif vim.str_byteindex then\n    ---@diagnostic disable-next-line: param-type-mismatch\n    return vim.str_byteindex(s, index, encoding == \"utf-16\")\n  elseif vim.lsp.util._str_byteindex then\n    return vim.lsp.util._str_byteindex(s, index, encoding)\n  end\n  error(\"No str_byteindex function available\")\nend\n\n---@class trouble.Source.lsp: trouble.Source\n---@diagnostic disable-next-line: missing-fields\nlocal M = {}\n\nfunction M.setup()\n  vim.api.nvim_create_autocmd({ \"LspAttach\", \"LspDetach\" }, {\n    group = vim.api.nvim_create_augroup(\"trouble.lsp.dattach\", { clear = true }),\n    callback = function()\n      Cache.symbols:clear()\n      Cache.locations:clear()\n    end,\n  })\n  vim.api.nvim_create_autocmd({ \"BufDelete\", \"TextChanged\", \"TextChangedI\" }, {\n    group = vim.api.nvim_create_augroup(\"trouble.lsp.buf\", { clear = true }),\n    callback = function(ev)\n      local buf = ev.buf\n      Cache.symbols[buf] = nil\n      if vim.api.nvim_buf_is_valid(ev.buf) and vim.api.nvim_buf_is_loaded(ev.buf) and vim.bo[ev.buf].buftype == \"\" then\n        Cache.locations:clear()\n      end\n    end,\n  })\nend\n\nM.config = {\n  modes = {\n    lsp_document_symbols = {\n      title = \"{hl:Title}Document Symbols{hl} {count}\",\n      desc = \"document symbols\",\n      events = {\n        \"BufEnter\",\n        -- symbols are cached on changedtick,\n        -- so it's ok to refresh often\n        { event = \"TextChanged\", main = true },\n        { event = \"CursorMoved\", main = true },\n        { event = \"LspAttach\", main = true },\n      },\n      source = \"lsp.document_symbols\",\n      groups = {\n        { \"filename\", format = \"{file_icon} {filename} {count}\" },\n      },\n      sort = { \"filename\", \"pos\", \"text\" },\n      -- sort = { { buf = 0 }, { kind = \"Function\" }, \"filename\", \"pos\", \"text\" },\n      format = \"{kind_icon} {symbol.name} {text:Comment} {pos}\",\n    },\n    lsp_base = {\n      events = {\n        \"BufEnter\",\n        { event = \"CursorHold\", main = true },\n        { event = \"LspAttach\", main = true },\n      },\n      groups = {\n        { \"filename\", format = \"{file_icon} {filename} {count}\" },\n      },\n      sort = { \"filename\", \"pos\", \"text\" },\n      format = \"{text:ts} ({item.client}) {pos}\",\n    },\n    lsp = {\n      desc = \"LSP definitions, references, implementations, type definitions, and declarations\",\n      sections = {\n        \"lsp_definitions\",\n        \"lsp_references\",\n        \"lsp_implementations\",\n        \"lsp_type_definitions\",\n        \"lsp_declarations\",\n        \"lsp_incoming_calls\",\n        \"lsp_outgoing_calls\",\n      },\n    },\n  },\n}\n\nfor _, mode in ipairs({ \"incoming_calls\", \"outgoing_calls\" }) do\n  M.config.modes[\"lsp_\" .. mode] = {\n    mode = \"lsp_base\",\n    title = \"{hl:Title}\" .. Util.camel(mode, \" \") .. \"{hl} {count}\",\n    desc = Util.camel(mode, \" \"),\n    source = \"lsp.\" .. mode,\n    format = \"{kind_icon} {text:ts} {pos} {hl:Title}{item.client:Title}{hl}\",\n  }\nend\n\nfor _, mode in ipairs({ \"definitions\", \"references\", \"implementations\", \"type_definitions\", \"declarations\", \"command\" }) do\n  M.config.modes[\"lsp_\" .. mode] = {\n    auto_jump = true,\n    mode = \"lsp_base\",\n    title = \"{hl:Title}\" .. Util.camel(mode, \" \") .. \"{hl} {count}\",\n    source = \"lsp.\" .. mode,\n    desc = Util.camel(mode, \" \"):lower(),\n  }\nend\n\n---@class trouble.lsp.Response<R,P>: {client: vim.lsp.Client, result: R, err: lsp.ResponseError, params: P}\n\n---@param method string\n---@param params? table|fun(client:vim.lsp.Client):table\n---@param opts? {client?:vim.lsp.Client}\nfunction M.request(method, params, opts)\n  opts = opts or {}\n  local buf = vim.api.nvim_get_current_buf()\n  ---@type vim.lsp.Client[]\n  local clients = {}\n\n  if opts.client then\n    clients = { opts.client }\n  else\n    if vim.lsp.get_clients then\n      clients = vim.lsp.get_clients({ method = method, bufnr = buf })\n    else\n      ---@diagnostic disable-next-line: deprecated\n      clients = vim.lsp.get_active_clients({ bufnr = buf })\n      ---@param client vim.lsp.Client\n      clients = vim.tbl_filter(function(client)\n        return client.supports_method(method)\n      end, clients)\n    end\n  end\n\n  ---@param client vim.lsp.Client\n  return Promise.all(vim.tbl_map(function(client)\n    return Promise.new(function(resolve)\n      local p = type(params) == \"function\" and params(client) or params --[[@as table]]\n      local request = vim.fn.has(\"nvim-0.11\") == 0\n          and function(_, ...)\n            return client.request(...)\n          end\n        or client.request\n      request(client, method, p, function(err, result)\n        resolve({ client = client, result = result, err = err, params = p })\n      end, buf)\n    end)\n  end, clients)):next(function(results)\n    ---@param v trouble.lsp.Response<any,any>\n    return vim.tbl_filter(function(v)\n      return v.result\n    end, results)\n  end)\nend\n\n---@param method string\n---@param cb trouble.Source.Callback\n---@param ctx trouble.Source.ctx\n---@param opts? {context?:any, params?:table<string,any>}\nfunction M.get_locations(method, cb, ctx, opts)\n  local win = vim.api.nvim_get_current_win()\n  local buf = vim.api.nvim_get_current_buf()\n  local cursor = vim.api.nvim_win_get_cursor(win)\n  local col = cursor[2]\n\n  local line = vim.api.nvim_get_current_line()\n  while col > 1 and vim.fn.strcharpart(line, col - 1, 1):match(\"^[a-zA-Z_]$\") do\n    col = col - 1\n  end\n\n  opts = opts or {}\n  ---@type fun(client:vim.lsp.Client):lsp.TextDocumentPositionParams\n  local params = function(client)\n    local ret = opts.params or vim.lsp.util.make_position_params(win, client.offset_encoding)\n    ---@diagnostic disable-next-line: inject-field\n    ret.context = ret.context or opts.context or nil\n    return ret\n  end\n\n  local id =\n    table.concat({ buf, cursor[1], col, method, vim.inspect(vim.lsp.util.make_position_params(win, \"utf-16\")) }, \"-\")\n  if Cache.locations[id] then\n    return cb(Cache.locations[id])\n  end\n\n  M.request(method, params):next(\n    ---@param results trouble.lsp.Response<lsp.Loc>[]\n    function(results)\n      local items = {} ---@type trouble.Item[]\n      for _, resp in ipairs(results) do\n        vim.list_extend(items, M.get_items(resp.client, resp.result, ctx.opts.params))\n      end\n      Cache.locations[id] = items\n      cb(items)\n    end\n  )\nend\n\nM.get = {}\n\n---@param cb trouble.Source.Callback\nfunction M.get.document_symbols(cb)\n  local buf = vim.api.nvim_get_current_buf()\n  ---@type trouble.Item[]\n  local ret = Cache.symbols[buf]\n\n  if ret then\n    return cb(ret)\n  end\n\n  ---@type lsp.DocumentSymbolParams\n  local params = { textDocument = vim.lsp.util.make_text_document_params() }\n\n  ---@alias lsp.Symbol lsp.SymbolInformation|lsp.DocumentSymbol\n\n  M.request(\"textDocument/documentSymbol\", params):next(\n    ---@param results trouble.lsp.Response<lsp.SymbolInformation[]|lsp.DocumentSymbol[]>[]\n    function(results)\n      if vim.tbl_isempty(results) then\n        return cb({})\n      end\n      if not vim.api.nvim_buf_is_valid(buf) then\n        return\n      end\n      local items = {} ---@type trouble.Item[]\n\n      for _, res in ipairs(results) do\n        vim.list_extend(items, M.results_to_items(res.client, res.result, params.textDocument.uri))\n      end\n      Item.add_text(items, { mode = \"after\" })\n      ---@diagnostic disable-next-line: no-unknown\n      Cache.symbols[buf] = items\n      cb(items)\n    end\n  )\nend\n\n---@param cb trouble.Source.Callback\nfunction M.call_hierarchy(cb, incoming)\n  local win = vim.api.nvim_get_current_win()\n  M.request(\"textDocument/prepareCallHierarchy\", function(client)\n    return vim.lsp.util.make_position_params(win, client.offset_encoding)\n  end)\n    :next(\n      ---@param results trouble.lsp.Response<lsp.CallHierarchyItem[]>[]\n      function(results)\n        local requests = {} ---@type trouble.Promise[]\n        for _, res in ipairs(results or {}) do\n          for _, chi in ipairs(res.result) do\n            requests[#requests + 1] = M.request(\n              (\"callHierarchy/%sCalls\"):format(incoming and \"incoming\" or \"outgoing\"),\n              { item = chi },\n              { client = res.client }\n            )\n          end\n        end\n        return Promise.all(requests)\n      end\n    )\n    :next(\n      ---@param responses trouble.lsp.Response<(lsp.CallHierarchyIncomingCall|lsp.CallHierarchyOutgoingCall)[]>[][]\n      function(responses)\n        local items = {} ---@type trouble.Item[]\n\n        for _, results in ipairs(responses) do\n          for _, res in ipairs(results) do\n            local client = res.client\n            local calls = res.result\n            local todo = {} ---@type lsp.ResultItem[]\n\n            for _, call in ipairs(calls) do\n              todo[#todo + 1] = call.to or call.from\n            end\n            vim.list_extend(items, M.results_to_items(client, todo))\n          end\n        end\n        Item.add_text(items, { mode = \"after\" })\n\n        if incoming then\n          -- for incoming calls, we actually want the call locations, not just the caller\n          -- but we use the caller's item text as the call location text\n          local texts = {} ---@type table<lsp.CallHierarchyItem, string>\n          for _, item in ipairs(items) do\n            texts[item.item.symbol] = item.item.text\n          end\n\n          items = {}\n          for _, results in ipairs(responses) do\n            for _, res in ipairs(results) do\n              local client = res.client\n              local calls = res.result\n              local todo = {} ---@type lsp.ResultItem[]\n\n              for _, call in ipairs(calls) do\n                for _, r in ipairs(call.fromRanges or {}) do\n                  local t = vim.deepcopy(call.from) --[[@as lsp.ResultItem]]\n                  t.location = { range = r or call.from.selectionRange or call.from.range, uri = call.from.uri }\n                  t.text = texts[call.from]\n                  todo[#todo + 1] = t\n                end\n              end\n              vim.list_extend(items, M.results_to_items(client, todo))\n            end\n          end\n        end\n        cb(items)\n      end\n    )\n  -- :catch(Util.error)\nend\n\n---@param cb trouble.Source.Callback\nfunction M.get.incoming_calls(cb)\n  M.call_hierarchy(cb, true)\nend\n\n---@param cb trouble.Source.Callback\nfunction M.get.outgoing_calls(cb)\n  M.call_hierarchy(cb, false)\nend\n\n---@param client vim.lsp.Client\n---@param locations? lsp.Location[]|lsp.LocationLink[]|lsp.Location\n---@param opts? {include_current?:boolean}\nfunction M.get_items(client, locations, opts)\n  opts = opts or {}\n  locations = locations or {}\n  locations = Util.islist(locations) and locations or { locations }\n  ---@cast locations (lsp.Location|lsp.LocationLink)[]\n\n  locations = vim.list_slice(locations, 1, Config.max_items)\n\n  local items = M.locations_to_items(client, locations)\n\n  local cursor = vim.api.nvim_win_get_cursor(0)\n  local fname = vim.api.nvim_buf_get_name(0)\n  fname = vim.fs.normalize(fname)\n\n  if not opts.include_current then\n    ---@param item trouble.Item\n    items = vim.tbl_filter(function(item)\n      return not (item.filename == fname and Filter.overlaps(cursor, item, { lines = true }))\n    end, items)\n  end\n\n  -- Item.add_text(items, { mode = \"full\" })\n  return items\nend\n\n---@alias lsp.Loc lsp.Location|lsp.LocationLink\n---@param client vim.lsp.Client\n---@param locs lsp.Loc[]\n---@return trouble.Item[]\nfunction M.locations_to_items(client, locs)\n  local ranges = M.locations_to_ranges(client, locs)\n  ---@param range trouble.Range.lsp\n  return vim.tbl_map(function(range)\n    return M.range_to_item(client, range)\n  end, vim.tbl_values(ranges))\nend\n\n---@param client vim.lsp.Client\n---@param range trouble.Range.lsp\n---@return trouble.Item\nfunction M.range_to_item(client, range)\n  return Item.new({\n    buf = range.buf,\n    filename = range.filename,\n    pos = range.pos,\n    end_pos = range.end_pos,\n    source = \"lsp\",\n    item = {\n      client_id = client.id,\n      client = client.name,\n      location = range.location,\n      text = range.line and vim.trim(range.line) or nil,\n    },\n  })\nend\n\nlocal kinds = nil ---@type table<lsp.SymbolKind, string>\n\n--- Gets the original symbol kind name from its number.\n--- Some plugins override the symbol kind names, so this function is needed to get the original name.\n---@param kind lsp.SymbolKind\n---@return string\nfunction M.symbol_kind(kind)\n  if not kinds then\n    kinds = {}\n    for k, v in pairs(vim.lsp.protocol.SymbolKind) do\n      if type(v) == \"number\" then\n        kinds[v] = k\n      end\n    end\n  end\n  return kinds[kind]\nend\n\n---@alias lsp.ResultItem lsp.Symbol|lsp.CallHierarchyItem|{text?:string}\n---@param client vim.lsp.Client\n---@param results lsp.ResultItem[]\n---@param default_uri? string\nfunction M.results_to_items(client, results, default_uri)\n  local items = {} ---@type trouble.Item[]\n  local locs = {} ---@type lsp.Loc[]\n  local processed = {} ---@type table<lsp.ResultItem, {uri:string, loc:lsp.Loc, range?:lsp.Loc}>\n\n  ---@param result lsp.ResultItem\n  local function process(result)\n    local uri = result.location and result.location.uri or result.uri or default_uri\n    local loc = result.location or { range = result.selectionRange or result.range, uri = uri }\n    loc.uri = loc.uri or uri\n    if not loc.uri then\n      assert(loc.uri, \"missing uri in result:\\n\" .. vim.inspect(result))\n    end\n    -- the range enclosing this symbol. Useful to get the symbol of the current cursor position\n    ---@type lsp.Location?\n    local range = result.range and { range = result.range, uri = uri } or nil\n    processed[result] = { uri = uri, loc = loc, range = range }\n    locs[#locs + 1] = loc\n    if range then\n      locs[#locs + 1] = range\n    end\n    for _, child in ipairs(result.children or {}) do\n      process(child)\n    end\n  end\n\n  for _, result in ipairs(results) do\n    process(result)\n  end\n\n  local ranges = M.locations_to_ranges(client, locs)\n\n  ---@param result lsp.ResultItem\n  local function add(result)\n    local loc = processed[result].loc\n    local range = processed[result].range\n\n    local item = M.range_to_item(client, ranges[loc])\n    local id = { item.buf, item.pos[1], item.pos[2], item.end_pos[1], item.end_pos[2], item.kind }\n    item.id = table.concat(id, \"|\")\n    -- item.text = nil\n    -- the range enclosing this symbol. Useful to get the symbol of the current cursor position\n    item.range = range and ranges[range] or nil\n    item.item.kind = M.symbol_kind(result.kind) or tostring(result.kind)\n    item.item.symbol = result\n    item.item.text = result.text\n    items[#items + 1] = item\n    for _, child in ipairs(result.children or {}) do\n      item:add_child(add(child))\n    end\n    result.children = nil\n    return item\n  end\n\n  for _, result in ipairs(results) do\n    add(result)\n  end\n\n  return items\nend\n\n---@class trouble.Range.lsp: trouble.Range\n---@field buf? number\n---@field filename string\n---@field location lsp.Loc\n---@field client vim.lsp.Client\n---@field line string\n\n---@param client vim.lsp.Client\n---@param locs lsp.Loc[]\nfunction M.locations_to_ranges(client, locs)\n  local todo = {} ---@type table<string, {locs:lsp.Loc[], rows:table<number,number>}>\n  for _, d in ipairs(locs) do\n    local uri = d.uri or d.targetUri\n    local range = d.range or d.targetSelectionRange\n    todo[uri] = todo[uri] or { locs = {}, rows = {} }\n    table.insert(todo[uri].locs, d)\n    local from = range.start.line + 1\n    local to = range[\"end\"].line + 1\n    todo[uri].rows[from] = from\n    todo[uri].rows[to] = to\n  end\n\n  local ret = {} ---@type table<lsp.Loc,trouble.Range.lsp>\n\n  for uri, t in pairs(todo) do\n    local buf = vim.uri_to_bufnr(uri)\n    local filename = vim.uri_to_fname(uri)\n    local lines = Util.get_lines({ rows = vim.tbl_keys(t.rows), buf = buf }) or {}\n    for _, loc in ipairs(t.locs) do\n      local range = loc.range or loc.targetSelectionRange\n      local line = lines[range.start.line + 1] or \"\"\n      local end_line = lines[range[\"end\"].line + 1] or \"\"\n      local pos = { range.start.line + 1, get_line_col(line, range.start.character, client.offset_encoding) }\n      local end_pos = { range[\"end\"].line + 1, get_line_col(end_line, range[\"end\"].character, client.offset_encoding) }\n      ret[loc] = {\n        buf = buf,\n        filename = filename,\n        pos = pos,\n        end_pos = end_pos,\n        source = \"lsp\",\n        client = client,\n        location = loc,\n        line = line,\n      }\n    end\n  end\n  return ret\nend\n\n---@param cb trouble.Source.Callback\n---@param ctx trouble.Source.ctx\nfunction M.get.command(cb, ctx)\n  local err = \"Missing command params for `lsp_command`.\\n\"\n    .. \"You need to specify `opts.params = {command = 'the_command', arguments = {}}`\"\n  if not ctx.opts.params then\n    return Util.error(err)\n  end\n  ---@type lsp.ExecuteCommandParams\n  local params = ctx.opts.params\n  if not params.command then\n    return Util.error(err)\n  end\n  M.get_locations(\"workspace/executeCommand\", cb, ctx, { params = params })\nend\n\n---@param ctx trouble.Source.ctx\n---@param cb trouble.Source.Callback\nfunction M.get.references(cb, ctx)\n  local params = ctx.opts.params or {}\n  M.get_locations(\"textDocument/references\", cb, ctx, {\n    context = {\n      includeDeclaration = params.include_declaration ~= false,\n    },\n  })\nend\n\n---@param cb trouble.Source.Callback\n---@param ctx trouble.Source.ctx\nfunction M.get.definitions(cb, ctx)\n  M.get_locations(\"textDocument/definition\", cb, ctx)\nend\n\n---@param cb trouble.Source.Callback\n---@param ctx trouble.Source.ctx\nfunction M.get.implementations(cb, ctx)\n  M.get_locations(\"textDocument/implementation\", cb, ctx)\nend\n\n-- Type Definitions\n---@param cb trouble.Source.Callback\n---@param ctx trouble.Source.ctx\nfunction M.get.type_definitions(cb, ctx)\n  M.get_locations(\"textDocument/typeDefinition\", cb, ctx)\nend\n\n-- Declaration\n---@param cb trouble.Source.Callback\n---@param ctx trouble.Source.ctx\nfunction M.get.declarations(cb, ctx)\n  M.get_locations(\"textDocument/declaration\", cb, ctx)\nend\n\nreturn M\n"
  },
  {
    "path": "lua/trouble/sources/qf.lua",
    "content": "---@diagnostic disable: inject-field\nlocal Item = require(\"trouble.item\")\n\n---Represents an item in a Neovim quickfix/loclist.\n---@class qf.item\n---@field bufnr? number The buffer number where the item originates.\n---@field filename? string\n---@field lnum number The start line number for the item.\n---@field end_lnum? number The end line number for the item.\n---@field pattern string A pattern related to the item. It can be a search pattern or any relevant string.\n---@field col? number The column number where the item starts.\n---@field end_col? number The column number where the item ends.\n---@field module? string Module information (if any) associated with the item.\n---@field nr? number A unique number or ID for the item.\n---@field text? string A description or message related to the item.\n---@field type? string The type of the item. E.g., \"W\" might stand for \"Warning\".\n---@field valid number A flag indicating if the item is valid (1) or not (0).\n---@field user_data? any Any user data associated with the item.\n---@field vcol? number Visual column number. Indicates if the column number is a visual column number (when set to 1) or a byte index (when set to 0).\n\n---@class trouble.Source.qf: trouble.Source\nlocal M = {}\n\nM.config = {\n  modes = {\n    qflist = {\n      desc = \"Quickfix List\",\n      events = {\n        \"QuickFixCmdPost\",\n        { event = \"TextChanged\", main = true },\n      },\n      source = \"qf.qflist\",\n      groups = {\n        { \"filename\", format = \"{file_icon} {filename} {count}\" },\n      },\n      sort = { \"severity\", \"filename\", \"pos\", \"message\" },\n      format = \"{severity_icon|item.type:DiagnosticSignWarn} {text:ts} {pos}\",\n    },\n    loclist = {\n      desc = \"Location List\",\n      events = {\n        \"BufEnter\",\n        { event = \"TextChanged\", main = true },\n      },\n      source = \"qf.loclist\",\n      groups = {\n        { \"filename\", format = \"{file_icon} {filename} {count}\" },\n      },\n      sort = { \"severity\", \"filename\", \"pos\", \"message\" },\n      format = \"{severity_icon|item.type:DiagnosticSignWarn} {text:ts} {pos}\",\n    },\n  },\n}\nM.config.modes.quickfix = M.config.modes.qflist\n\nlocal severities = {\n  E = vim.diagnostic.severity.ERROR,\n  W = vim.diagnostic.severity.WARN,\n  I = vim.diagnostic.severity.INFO,\n  H = vim.diagnostic.severity.HINT,\n  N = vim.diagnostic.severity.HINT,\n}\n\nM.get = {\n  qflist = function(cb)\n    cb(M.get_list())\n  end,\n  loclist = function(cb)\n    cb(M.get_list({ win = vim.api.nvim_get_current_win() }))\n  end,\n}\n\n---@param opts? {win:number}\nfunction M.get_list(opts)\n  opts = opts or {}\n  local list = opts.win == nil and vim.fn.getqflist({ all = true }) or vim.fn.getloclist(opts.win, { all = true })\n  ---@cast list {items?:qf.item[]}?\n\n  local ret = {} ---@type trouble.Item[]\n  for _, item in pairs(list and list.items or {}) do\n    local row = item.lnum == 0 and 1 or item.lnum\n    local col = (item.col == 0 and 1 or item.col) - 1\n    local end_row = item.end_lnum == 0 and row or item.end_lnum\n    local end_col = item.end_col == 0 and col or (item.end_col - 1)\n\n    if item.valid == 1 then\n      ret[#ret + 1] = Item.new({\n        pos = { row, col },\n        end_pos = { end_row, end_col },\n        text = item.text,\n        severity = severities[item.type] or 0,\n        buf = item.bufnr,\n        filename = item.filename,\n        item = item,\n        source = \"qf\",\n      })\n    elseif #ret > 0 and ret[#ret].item.text and item.text then\n      ret[#ret].item.text = ret[#ret].item.text .. \"\\n\" .. item.text\n    end\n  end\n  Item.add_id(ret, { \"severity\" })\n  Item.add_text(ret, { mode = \"full\" })\n  return ret\nend\n\nreturn M\n"
  },
  {
    "path": "lua/trouble/sources/snacks.lua",
    "content": "---@diagnostic disable: inject-field\nlocal Item = require(\"trouble.item\")\n\n---@module 'snacks'\n\n---@class trouble.Source.snacks: trouble.Source\nlocal M = {}\n\n---@type trouble.Item[]\nM.items = {}\n\nM.config = {\n  modes = {\n    snacks = {\n      desc = \"Snacks results previously opened with `require('trouble.sources.snacks').open()`.\",\n      source = \"snacks\",\n      groups = {\n        { \"cmd\", format = \"{hl:Title}Snacks{hl} {cmd:Comment} {count}\" },\n        { \"filename\", format = \"{file_icon} {filename} {count}\" },\n      },\n      sort = { \"filename\", \"pos\" },\n      format = \"{text:ts} {pos}\",\n    },\n    snacks_files = {\n      desc = \"Snacks results previously opened with `require('trouble.sources.snacks').open()`.\",\n      source = \"snacks\",\n      groups = {\n        { \"cmd\", format = \"{hl:Title}Snacks{hl} {cmd:Comment} {count}\" },\n      },\n      sort = { \"filename\", \"pos\" },\n      format = \"{file_icon} {filename}\",\n    },\n  },\n}\n\n---@param item snacks.picker.Item\nfunction M.item(item)\n  return Item.new({\n    source = \"snacks\",\n    buf = item.buf,\n    filename = item.file,\n    pos = item.pos,\n    end_pos = item.end_pos,\n    text = item.line or item.comment or item.label or item.name or item.detail or false,\n    item = item,\n  })\nend\n\n---@param cb trouble.Source.Callback\n---@param _ctx trouble.Source.ctx)\nfunction M.get(cb, _ctx)\n  cb(M.items)\nend\n\n-- Returns the mode based on the items.\nfunction M.mode()\n  for _, item in ipairs(M.items) do\n    if item.text then\n      return \"snacks\"\n    end\n  end\n  return \"snacks_files\"\nend\n\n---@param picker snacks.Picker\n---@param opts? { type?: \"all\" | \"selected\" | \"smart\", add?: boolean }\nfunction M.open(picker, opts)\n  opts = opts or {}\n  if not opts.add then\n    M.items = {}\n  end\n  local sitems = {} ---@type snacks.picker.Item[]\n  local selected = picker:selected()\n  opts.type = opts.type or \"smart\"\n  if opts.type == \"smart\" then\n    opts.type = #selected == 0 and \"all\" or \"selected\"\n  end\n  if opts.type == \"all\" then\n    vim.list_extend(sitems, picker:items())\n  else\n    vim.list_extend(sitems, selected)\n  end\n  for _, i in ipairs(sitems) do\n    local item = M.item(i)\n    table.insert(M.items, item)\n  end\n  picker:close()\n  vim.schedule(function()\n    require(\"trouble\").open({ mode = M.mode() })\n  end)\nend\n\n---@param opts? { type?: \"all\" | \"selected\" | \"smart\", add?: boolean }\nfunction M.wrap(opts)\n  ---@param picker snacks.Picker\n  return function(picker)\n    M.open(picker, vim.deepcopy(opts or {}))\n  end\nend\n\n---@type table<string, snacks.picker.Action.spec>\nM.actions = {\n  -- Open selected or all items in the trouble list.\n  trouble_open = { action = M.wrap({ type = \"smart\" }), desc = \"smart-open-with-trouble\" },\n  -- Open selected items in the trouble list.\n  trouble_open_selected = { action = M.wrap({ type = \"selected\" }), desc = \"open-with-trouble\" },\n  -- Open all items in the trouble list.\n  trouble_open_all = { action = M.wrap({ type = \"all\" }), desc = \"open-all-with-trouble\" },\n  -- Add selected or all items to the trouble list.\n  trouble_add = { action = M.wrap({ type = \"smart\", add = true }), desc = \"smart-add-to-trouble\" },\n  -- Add selected items to the trouble list.\n  trouble_add_selected = { action = M.wrap({ type = \"selected\", add = true }), desc = \"add-to-trouble\" },\n  -- Add all items to the trouble list.\n  trouble_add_all = { action = M.wrap({ type = \"all\" }), desc = \"add-all-to-trouble\" },\n}\n\nreturn M\n"
  },
  {
    "path": "lua/trouble/sources/telescope.lua",
    "content": "---@diagnostic disable: inject-field\nlocal Item = require(\"trouble.item\")\nlocal Util = require(\"trouble.util\")\n\n---Represents an item in a Neovim quickfix/loclist.\n---@class telescope.Item\n---@field lnum? number The start line number for the item.\n---@field col? number The column number where the item starts.\n---@field bufnr? number The buffer number where the item originates.\n---@field filename? string The filename of the item.\n---@field text? string The text of the item.\n---@field cwd? string The current working directory of the item.\n---@field path? string The path of the item.\n\n---@class trouble.Source.telescope: trouble.Source\nlocal M = {}\n\n---@type trouble.Item[]\nM.items = {}\n\nM.config = {\n  modes = {\n    telescope = {\n      desc = \"Telescope results previously opened with `require('trouble.sources.telescope').open()`.\",\n      source = \"telescope\",\n      title = \"{hl:Title}Telescope{hl} {count}\",\n      groups = {\n        { \"filename\", format = \"{file_icon} {filename} {count}\" },\n      },\n      sort = { \"filename\", \"pos\" },\n      format = \"{text:ts} {pos}\",\n    },\n    telescope_files = {\n      desc = \"Telescope results previously opened with `require('trouble.sources.telescope').open()`.\",\n      source = \"telescope\",\n      title = \"{hl:Title}Telescope{hl} {count}\",\n      sort = { \"filename\", \"pos\" },\n      format = \"{file_icon} {filename}\",\n    },\n  },\n}\n\n---@param item telescope.Item\nfunction M.item(item)\n  ---@type string\n  local filename\n  if item.path then\n    filename = item.path\n  else\n    filename = item.filename\n    if item.cwd then\n      filename = item.cwd .. \"/\" .. filename\n    end\n  end\n  local word = item.text and item.col and item.text:sub(item.col):match(\"%S+\")\n  local pos = item.lnum and { item.lnum, item.col and item.col - 1 or 0 } or nil\n  return Item.new({\n    source = \"telescope\",\n    buf = item.bufnr,\n    filename = filename,\n    pos = pos,\n    end_pos = word and pos and { pos[1], pos[2] + #word } or nil,\n    item = item,\n  })\nend\n\n---@param cb trouble.Source.Callback\n---@param _ctx trouble.Source.ctx)\nfunction M.get(cb, _ctx)\n  cb(M.items)\nend\n\n-- Returns the mode based on the items.\nfunction M.mode()\n  for _, item in ipairs(M.items) do\n    if item.text then\n      return \"telescope\"\n    end\n  end\n  return \"telescope_files\"\nend\n\n-- Append the current telescope buffer to the trouble list.\n---@param opts? trouble.Mode|string\nfunction M.add(prompt_bufnr, opts)\n  local action_state = require(\"telescope.actions.state\")\n  ---@type Picker\n  local picker = action_state.get_current_picker(prompt_bufnr)\n  if not picker then\n    return Util.error(\"No Telescope picker found?\")\n  end\n\n  if #picker:get_multi_selection() > 0 then\n    for _, item in ipairs(picker:get_multi_selection()) do\n      table.insert(M.items, M.item(item))\n    end\n  else\n    for item in picker.manager:iter() do\n      table.insert(M.items, M.item(item))\n    end\n  end\n  -- Item.add_text(M.items, { mode = \"after\" })\n\n  vim.schedule(function()\n    require(\"telescope.actions\").close(prompt_bufnr)\n    opts = opts or {}\n    if type(opts) == \"string\" then\n      opts = { mode = opts }\n    end\n    opts = vim.tbl_extend(\"force\", { mode = M.mode() }, opts)\n    require(\"trouble\").open(opts)\n  end)\nend\n\n-- Opens the current telescope buffer in the trouble list.\n-- This will clear the existing items.\n---@param opts? trouble.Mode|string\nfunction M.open(prompt_bufnr, opts)\n  M.items = {}\n  M.add(prompt_bufnr, opts)\nend\n\nreturn M\n"
  },
  {
    "path": "lua/trouble/spec.lua",
    "content": "local Config = require(\"trouble.config\")\nlocal Util = require(\"trouble.util\")\n\n---@alias trouble.SorterFn fun(item: trouble.Item): any?\n\n---@alias trouble.Sort.spec string|trouble.SorterFn|(string|trouble.SorterFn|trouble.Filter.spec)[]\n---@alias trouble.Filter.spec table<string, any>|fun(items: trouble.Item[]): trouble.Item[]\n---@alias trouble.Group.spec string|string[]|{format?:string}\n\n---@alias trouble.Sections.spec (trouble.Section.spec|string)[]\n\n---@class trouble.Section.spec\n---@field source string\n---@field title? string|boolean\n---@field events? (string|trouble.Event)[]\n---@field groups? trouble.Group.spec[]|trouble.Group.spec\n---@field sort? trouble.Sort.spec\n---@field filter? trouble.Filter.spec\n---@field flatten? boolean when true, items with a natural hierarchy will be flattened\n---@field format? string\n---@field max_items? number\n---@field params? table<string, any>\n\n---@alias trouble.Filter table<string, any>|fun(items: trouble.Item[]): trouble.Item[]\n\n---@class trouble.Event\n---@field event string|string[]\n---@field pattern? string|string[]\n---@field main? boolean When true, this event will refresh only when it is the main window\n\n---@class trouble.Sort\n---@field field? string\n---@field sorter? trouble.SorterFn\n---@field filter? trouble.Filter\n---@field desc? boolean\n\n---@class trouble.Group\n---@field fields? string[]\n---@field format? string\n---@field directory? boolean\n\n---@class trouble.Section.opts\n---@field source string\n---@field groups trouble.Group[]\n---@field format string\n---@field flatten? boolean when true, items with a natural hierarchy will be flattened\n---@field events trouble.Event[]\n---@field sort? trouble.Sort[]\n---@field filter? trouble.Filter\n---@field max_items? number\n---@field params? table<string, any>\n\nlocal M = {}\n\n---@param spec trouble.Section.spec|string\n---@return trouble.Section.opts\nfunction M.section(spec)\n  local groups = type(spec.groups) == \"string\" and { spec.groups } or spec.groups\n  ---@cast groups trouble.Group.spec[]\n  local events = {} ---@type trouble.Event[]\n  for _, e in ipairs(spec.events or {}) do\n    if type(e) == \"string\" then\n      local event, pattern = e:match(\"^(%w+)%s+(.*)$\")\n      event = event or e\n      events[#events + 1] = { event = event, pattern = pattern }\n    elseif type(e) == \"table\" and e.event then\n      events[#events + 1] = e\n    else\n      error(\"invalid event: \" .. vim.inspect(e))\n    end\n  end\n\n  local ret = {\n    source = spec.source,\n    groups = vim.tbl_map(M.group, groups or {}),\n    sort = spec.sort and M.sort(spec.sort) or nil,\n    filter = spec.filter,\n    format = spec.format or \"{filename} {pos}\",\n    events = events,\n    flatten = spec.flatten,\n    params = spec.params,\n  }\n  -- A title is just a group without fields\n  if spec.title then\n    table.insert(ret.groups, 1, { fields = {}, format = spec.title })\n  end\n  return ret\nend\n\n---@param action trouble.Action.spec\n---@return trouble.Action\nfunction M.action(action)\n  if type(action) == \"string\" then\n    action = { action = action, desc = action:gsub(\"_\", \" \") }\n  end\n  if type(action) == \"function\" then\n    action = { action = action }\n  end\n  if type(action.action) == \"string\" then\n    local desc = action.action:gsub(\"_\", \" \")\n    action.action = require(\"trouble.config.actions\")[action.action]\n    if type(action.action) == \"table\" then\n      action = action.action\n    end\n    action.desc = action.desc or desc\n  end\n  ---@cast action trouble.Action\n  return action\nend\n\n---@param mode trouble.Mode\n---@return trouble.Section.opts[]\nfunction M.sections(mode)\n  local ret = {} ---@type trouble.Section.opts[]\n\n  if mode.sections then\n    for _, s in ipairs(mode.sections) do\n      ret[#ret + 1] = M.section(Config.get(mode, { sections = false }, s) --[[@as trouble.Mode]])\n    end\n  else\n    local section = M.section(mode)\n    section.max_items = section.max_items or mode.max_items\n    ret[#ret + 1] = section\n  end\n  return ret\nend\n\n---@param spec trouble.Sort.spec\n---@return trouble.Sort[]\nfunction M.sort(spec)\n  spec = type(spec) == \"table\" and Util.islist(spec) and spec or { spec }\n  ---@cast spec (string|trouble.SorterFn|trouble.Filter.spec)[]\n  local fields = {} ---@type trouble.Sort[]\n  for f, field in ipairs(spec) do\n    if type(field) == \"function\" then\n      ---@cast field trouble.SorterFn\n      fields[f] = { sorter = field }\n    elseif type(field) == \"table\" and field.field then\n      ---@cast field {field:string, desc?:boolean}\n      fields[f] = field\n    elseif type(field) == \"table\" then\n      fields[f] = { filter = field }\n    elseif type(field) == \"string\" then\n      local desc = field:sub(1, 1) == \"-\"\n      fields[f] = {\n        field = desc and field:sub(2) or field,\n        desc = desc and true or nil,\n      }\n    else\n      error(\"invalid sort field: \" .. vim.inspect(field))\n    end\n  end\n  return fields\nend\n\n---@param spec trouble.Group.spec\n---@return trouble.Group\nfunction M.group(spec)\n  spec = type(spec) == \"string\" and { spec } or spec\n  ---@cast spec string[]|{format?:string}\n  ---@type trouble.Group\n  local ret = { fields = {}, format = \"\" }\n  for k, v in pairs(spec) do\n    if type(k) == \"number\" then\n      ---@cast v string\n      ret.fields[#ret.fields + 1] = v\n    elseif k == \"format\" then\n      ---@cast v string\n      ret[k] = v\n    else\n      error(\"invalid `group` key: \" .. k)\n    end\n  end\n  if vim.tbl_contains(ret.fields, \"directory\") then\n    ret.directory = true\n    ret.format = ret.format == \"\" and \"{directory_icon} {directory} {count}\" or ret.format\n    if #ret.fields > 1 then\n      error(\"group: cannot specify other fields with `directory`\")\n    end\n    ret.fields = nil\n  end\n  if ret.format == \"\" then\n    ret.format = table.concat(\n      ---@param f string\n      vim.tbl_map(function(f)\n        return \"{\" .. f .. \"}\"\n      end, ret.fields),\n      \" \"\n    )\n  end\n  return ret\nend\n\nreturn M\n"
  },
  {
    "path": "lua/trouble/tree.lua",
    "content": "local Item = require(\"trouble.item\")\nlocal Util = require(\"trouble.util\")\n\n---@class trouble.Node\n---@field id string\n---@field parent? trouble.Node\n---@field item? trouble.Item\n---@field index? table<string, trouble.Node>\n---@field group? trouble.Group\n---@field folded? boolean\n---@field children? trouble.Node[]\n---@field private _depth number\n---@field private _count? number\n---@field private _degree? number\nlocal M = {}\n\n---@alias trouble.GroupFn fun(item: trouble.Item, parent: trouble.Node, group: trouble.Group): trouble.Node\n\n---@param opts {id: string, item?: trouble.Item}\nfunction M.new(opts)\n  local self = setmetatable(opts, { __index = M })\n  self.id = self.id or self.item and self.item.id or nil\n  self.children = {}\n  self.index = {}\n  return self\nend\n\nfunction M:delete()\n  local parent = self.parent\n  if not parent then\n    return\n  end\n  if parent.children then\n    parent.children = vim.tbl_filter(function(c)\n      return c ~= self\n    end, parent.children)\n  end\n  if parent.index and self.id then\n    parent.index[self.id] = nil\n  end\n  parent._count = nil\n  parent._degree = nil\n  if parent:count() == 0 then\n    parent:delete()\n  end\nend\n\n-- Max depth of the tree\nfunction M:degree()\n  if not self._degree then\n    self._degree = 0\n    for _, child in ipairs(self.children or {}) do\n      self._degree = math.max(self._degree, child:degree())\n    end\n    self._degree = self._degree + 1\n  end\n  return self._degree\nend\n\n-- Depth of this node\nfunction M:depth()\n  if not self._depth then\n    self._depth = self.parent and (self.parent:depth() + 1) or 0\n  end\n  return self._depth\nend\n\n-- Number of actual items in the tree\n-- This excludes internal group nodes\nfunction M:count()\n  if not self._count then\n    self._count = 0\n    for _, child in ipairs(self.children or {}) do\n      self._count = self._count + child:count()\n    end\n    if not self.group and self.item then\n      self._count = self._count + 1\n    end\n  end\n  return self._count\nend\n\n--- Gets all the items in the tree, recursively.\n---@param ret trouble.Item[]?\nfunction M:flatten(ret)\n  ret = ret or {}\n  for _, child in ipairs(self.children or {}) do\n    child:flatten(ret)\n  end\n  if not self.group and self.item then\n    ret[#ret + 1] = self.item\n  end\n  return ret\nend\n\n---@param idx number|string\n---@return trouble.Node?\nfunction M:get(idx)\n  return type(idx) == \"number\" and self.children[idx] or self.index[idx]\nend\n\n-- Source of the item of this node\nfunction M:source()\n  return self.item and self.item.source\nend\n\n-- Width of the node (number of children)\nfunction M:width()\n  return self.children and #self.children or 0\nend\n\n-- Item of the parent node\nfunction M:parent_item()\n  return self.parent and self.parent.item\nend\n\nfunction M:add(node)\n  if node.id then\n    if self.index[node.id] then\n      Util.debug(\"node already exists:\\n\" .. node.id)\n      node.id = node.id .. \"_\"\n    end\n    self.index[node.id] = node\n  end\n  node.parent = self\n  table.insert(self.children, node)\n  return node\nend\n\nfunction M:is_leaf()\n  return self.children == nil or #self.children == 0\nend\n\n---@param other? trouble.Node\nfunction M:is(other)\n  if not other then\n    return false\n  end\n\n  if self == other then\n    return true\n  end\n\n  if self.id ~= other.id then\n    return false\n  end\n\n  if self.group ~= other.group then\n    return false\n  end\n\n  if self.group then\n    return true\n  end\n  assert(self.item, \"missing item\")\n\n  if not other.item then\n    return false\n  end\n\n  if self.item == other.item then\n    return true\n  end\n\n  return self.item.id and (self.item.id == other.item.id)\nend\n\n--- Build a tree from a list of items and a section.\n---@param items trouble.Item[]\n---@param section trouble.Section.opts\nfunction M.build(items, section)\n  local root = M.new({ id = \"$root\" })\n  local node_items = {} ---@type table<trouble.Node, trouble.Item[]>\n\n  -- create the group nodes\n  for i, item in ipairs(items) do\n    if section.max_items and i > section.max_items then\n      break\n    end\n    local node = root\n    for _, group in ipairs(section.groups) do\n      local builder = M.builders[group.directory and \"directory\" or \"fields\"]\n      if not builder then\n        assert(builder, \"unknown group type: \" .. vim.inspect(group))\n      end\n      node = builder.group(item, node, group)\n    end\n    node_items[node] = node_items[node] or {}\n    table.insert(node_items[node], item)\n  end\n\n  -- add the items to the nodes.\n  -- this will structure items by their parent node unless flatten is true\n  for node, nitems in pairs(node_items) do\n    M.add_items(node, nitems, { flatten = section.flatten })\n  end\n\n  -- post process the tree\n  for _, group in ipairs(section.groups) do\n    local builder = M.builders[group.directory and \"directory\" or \"fields\"]\n    if builder.post then\n      root = builder.post(root) or root\n    end\n  end\n  return root\nend\n\n--- This will add all the items to the root node,\n--- structured by their parent item, unless flatten is true.\n---@param root trouble.Node\n---@param items trouble.Item[]\n---@param opts? {flatten?: boolean}\nfunction M.add_items(root, items, opts)\n  opts = opts or {}\n  local item_nodes = {} ---@type table<trouble.Item, trouble.Node>\n  for _, item in ipairs(items) do\n    item_nodes[item] = M.new({ item = item })\n  end\n  for _, item in ipairs(items) do\n    local node = item_nodes[item]\n    local parent_node = root\n    if not opts.flatten then\n      local parent = item.parent\n      while parent do\n        if item_nodes[parent] then\n          parent_node = item_nodes[parent]\n          break\n        end\n        parent = parent.parent\n      end\n    end\n    parent_node:add(node)\n  end\nend\n\n---@alias trouble.Group.builder {group:trouble.GroupFn, post?:(fun(node: trouble.Node):trouble.Node?)}\n---@type table<\"directory\"|\"fields\", trouble.Group.builder>\nM.builders = {\n  fields = {\n    group = function(item, parent, group)\n      -- id is based on the parent id and the group fields\n      local id = group.format\n      if #group.fields > 0 then\n        local values = {} ---@type string[]\n        for i = 1, #group.fields do\n          values[#values + 1] = tostring(item[group.fields[i]])\n        end\n        id = table.concat(values, \"|\")\n      end\n      id = parent.id .. \"#\" .. id\n      local child = parent:get(id)\n      if not child then\n        child = M.new({ id = id, item = item, group = group })\n        parent:add(child)\n      end\n      return child\n    end,\n  },\n\n  directory = {\n    group = function(item, root, group)\n      if not item.dirname then\n        return root\n      end\n      local directory = \"\"\n      local parent = root\n      for _, part in Util.split(item.dirname, \"/\") do\n        directory = directory .. part .. \"/\"\n        local id = (root.id or \"\") .. \"#\" .. directory\n        local child = parent:get(id)\n        if not child then\n          local dir = Item.new({\n            filename = directory,\n            source = \"fs\",\n            id = id,\n            pos = { 1, 0 },\n            end_pos = { 1, 0 },\n            dirname = directory,\n            item = { directory = directory, type = \"directory\" },\n          })\n          child = M.new({ id = id, item = dir, group = group })\n          parent:add(child)\n        end\n        parent = child\n      end\n      return parent\n    end,\n    post = function(root)\n      ---@param node trouble.Node\n      local function collapse(node)\n        if node:source() == \"fs\" then\n          if node:width() == 1 then\n            local child = node.children[1]\n            if child:source() == \"fs\" and child.item.type == \"directory\" then\n              child.parent = node.parent\n              return collapse(child)\n            end\n          end\n        end\n        for c, child in ipairs(node.children or {}) do\n          node.children[c] = collapse(child)\n        end\n        return node\n      end\n      return collapse(root)\n    end,\n  },\n}\n\nreturn M\n"
  },
  {
    "path": "lua/trouble/util.lua",
    "content": "local Config = require(\"trouble.config\")\nlocal uv = vim.loop or vim.uv\n\nlocal M = {}\n\n---@param fn function\nfunction M.noautocmd(fn)\n  local ei = vim.o.eventignore\n  vim.o.eventignore = \"all\"\n  fn()\n  vim.o.eventignore = ei\nend\n\nfunction M.is_win()\n  return uv.os_uname().sysname:find(\"Windows\") ~= nil\nend\n\n---@param opts? {msg?: string}\nfunction M.try(fn, opts)\n  local ok, err = pcall(fn)\n  if not ok then\n    local msg = opts and opts.msg or \"Something went wrong:\"\n    msg = msg .. \"\\n\" .. err\n    M.error(msg)\n  end\nend\n\nM.islist = vim.islist or vim.tbl_islist\n\n---@alias NotifyOpts {level?: number, title?: string, once?: boolean, id?:string}\n\n---@type table<string, any>\nlocal notif_ids = {}\n\n---@param msg string|string[]\n---@param opts? NotifyOpts\nfunction M.notify(msg, opts)\n  opts = opts or {}\n  msg = type(msg) == \"table\" and table.concat(msg, \"\\n\") or msg\n  ---@cast msg string\n  msg = vim.trim(msg)\n  local ret = vim[opts.once and \"notify_once\" or \"notify\"](msg, opts.level, {\n    replace = opts.id and notif_ids[opts.id] or nil,\n    title = opts.title or \"Trouble\",\n    on_open = function(win)\n      vim.wo[win].conceallevel = 3\n      vim.wo[win].concealcursor = \"n\"\n      vim.wo[win].spell = false\n      vim.treesitter.start(vim.api.nvim_win_get_buf(win), \"markdown\")\n    end,\n  })\n  if opts.id then\n    notif_ids[opts.id] = ret\n  end\n  return ret\nend\n\n---@param msg string|string[]\n---@param opts? NotifyOpts\nfunction M.warn(msg, opts)\n  M.notify(msg, vim.tbl_extend(\"keep\", { level = vim.log.levels.WARN }, opts or {}))\nend\n\n---@param msg string|string[]\n---@param opts? NotifyOpts\nfunction M.info(msg, opts)\n  M.notify(msg, vim.tbl_extend(\"keep\", { level = vim.log.levels.INFO }, opts or {}))\nend\n\n---@param msg string|string[]\n---@param opts? NotifyOpts\nfunction M.error(msg, opts)\n  M.notify(msg, vim.tbl_extend(\"keep\", { level = vim.log.levels.ERROR }, opts or {}))\nend\n\n---@param msg string|string[]\nfunction M.debug(msg, ...)\n  if Config.debug then\n    if select(\"#\", ...) > 0 then\n      local obj = select(\"#\", ...) == 1 and ... or { ... }\n      msg = msg .. \"\\n```lua\\n\" .. vim.inspect(obj) .. \"\\n```\"\n    end\n    M.notify(msg, { title = \"Trouble (debug)\" })\n  end\nend\n\n---@param buf number\n---@param row number\n---@param ns number\n---@param col number\n---@param opts vim.api.keyset.set_extmark\n---@param debug_info? any\nfunction M.set_extmark(buf, ns, row, col, opts, debug_info)\n  local ok, err = pcall(vim.api.nvim_buf_set_extmark, buf, ns, row, col, opts)\n  if not ok and Config.debug then\n    M.debug(\"Failed to set extmark for preview\", { info = debug_info, row = row, col = col, opts = opts, error = err })\n  end\nend\n\n---@param str string\n---@param sep? string\nfunction M.camel(str, sep)\n  local parts = vim.split(str, \"[%.%-%_]\")\n  ---@diagnostic disable-next-line: no-unknown\n  return table.concat(\n    vim.tbl_map(function(part)\n      return part:sub(1, 1):upper() .. part:sub(2)\n    end, parts),\n    sep or \"\"\n  )\nend\n\n---@param opts? {ms?: number, debounce?: boolean}|number\n---@param default Throttle.opts\n---@return Throttle.opts\nfunction M.throttle_opts(opts, default)\n  opts = opts or {}\n  if type(opts) == \"number\" then\n    opts = { ms = opts }\n  end\n  return vim.tbl_deep_extend(\"force\", default, opts)\nend\n\n---@alias Throttle.opts {ms:number, debounce?:boolean, is_running?:fun():boolean}\n\n-- throttle with trailing execution\n---@generic T: fun()\n---@param fn T\n---@param opts? Throttle.opts\n---@return T\nfunction M.throttle(fn, opts)\n  opts = opts or {}\n  opts.ms = opts.ms or 20\n  local last = 0\n  local args = nil ---@type {n?:number}?\n  local timer = assert(uv.new_timer())\n  local pending = false -- from run() till end of fn\n  local running = false -- from run() till end of fn with is_running()\n\n  local t = {}\n\n  function t.run()\n    pending = true\n    running = true\n    timer:stop()\n    last = uv.now()\n    vim.schedule(function()\n      xpcall(function()\n        if not args then\n          return M.debug(\"Empty args. This should not happen.\")\n        end\n        fn(vim.F.unpack_len(args))\n        args = nil\n      end, function(err)\n        vim.schedule(function()\n          M.error(err)\n        end)\n      end)\n      pending = false\n      t.check()\n    end)\n  end\n\n  function t.schedule()\n    local now = uv.now()\n    local delay = opts.debounce and opts.ms or (opts.ms - (now - last))\n    timer:stop()\n    timer:start(math.max(0, delay), 0, t.run)\n  end\n\n  function t.check()\n    if running and not pending and not (opts.is_running and opts.is_running()) then\n      running = false\n      if args then -- schedule if there are pending args\n        t.schedule()\n      end\n    end\n  end\n\n  local check = assert(uv.new_check())\n  check:start(t.check)\n\n  return function(...)\n    args = vim.F.pack_len(...)\n\n    if timer:is_active() and not opts.debounce then\n      return\n    elseif not running then\n      t.schedule()\n    end\n  end\nend\n\n---@param s string\nfunction M.lines(s)\n  return M.split(s, \"\\n\")\nend\n\n---@param s string\n---@param c? string\nfunction M.split(s, c)\n  c = c or \"\\n\"\n  local pos = 1\n  local l = 0\n  return function()\n    if pos == -1 then\n      return\n    end\n    l = l + 1\n\n    local nl = s:find(c, pos, true)\n    if not nl then\n      local lastLine = s:sub(pos)\n      pos = -1\n      return l, lastLine\n    end\n\n    local line = s:sub(pos, nl - 1)\n    pos = nl + 1\n    return l, line\n  end\nend\n\n--- Gets lines from a file or buffer\n---@param opts {path?:string, buf?: number, rows?: number[]}\n---@return table<number, string>?\nfunction M.get_lines(opts)\n  if opts.buf then\n    local uri = vim.uri_from_bufnr(opts.buf)\n\n    if uri:sub(1, 4) ~= \"file\" then\n      vim.fn.bufload(opts.buf)\n    end\n\n    if vim.api.nvim_buf_is_loaded(opts.buf) then\n      local lines = {} ---@type table<number, string>\n      if not opts.rows then\n        return vim.api.nvim_buf_get_lines(opts.buf, 0, -1, false)\n      end\n      for _, row in ipairs(opts.rows) do\n        lines[row] = vim.api.nvim_buf_get_lines(opts.buf, row - 1, row, false)[1]\n      end\n      return lines\n    end\n    opts.path = vim.uri_to_fname(uri)\n  elseif not opts.path then\n    error(\"buf or filename is required\")\n  end\n\n  local fd = uv.fs_open(opts.path, \"r\", 438)\n  if not fd then\n    return\n  end\n  local stat = assert(uv.fs_fstat(fd))\n  if not (stat.type == \"file\" or stat.type == \"link\") then\n    return\n  end\n  local data = assert(uv.fs_read(fd, stat.size, 0)) --[[@as string]]\n  assert(uv.fs_close(fd))\n\n  local todo = 0\n  local ret = {} ---@type table<number, string|boolean>\n  for _, r in ipairs(opts.rows or {}) do\n    if not ret[r] then\n      todo = todo + 1\n      ret[r] = true\n    end\n  end\n\n  for row, line in M.lines(data) do\n    if not opts.rows or ret[row] then\n      if line:sub(-1) == \"\\r\" then\n        line = line:sub(1, -2)\n      end\n      todo = todo - 1\n      ret[row] = line\n      if todo == 0 then\n        break\n      end\n    end\n  end\n  for i, r in pairs(ret) do\n    if r == true then\n      ret[i] = \"\"\n    end\n  end\n  return ret\nend\n\n--- Creates a weak reference to an object.\n--- Calling the returned function will return the object if it has not been garbage collected.\n---@generic T: table\n---@param obj T\n---@return T|fun():T?\nfunction M.weak(obj)\n  local weak = { _obj = obj }\n  ---@return table<any, any>\n  local function get()\n    local ret = rawget(weak, \"_obj\")\n    return ret == nil and error(\"Object has been garbage collected\", 2) or ret\n  end\n  local mt = {\n    __mode = \"v\",\n    __call = function(t)\n      return rawget(t, \"_obj\")\n    end,\n    __index = function(_, k)\n      return get()[k]\n    end,\n    __newindex = function(_, k, v)\n      get()[k] = v\n    end,\n    __pairs = function()\n      return pairs(get())\n    end,\n  }\n  return setmetatable(weak, mt)\nend\n\nreturn M\n"
  },
  {
    "path": "lua/trouble/view/indent.lua",
    "content": "local Config = require(\"trouble.config\")\nlocal Util = require(\"trouble.util\")\n\n---@alias trouble.Indent.type \"top\"|\"middle\"|\"last\"|\"fold_open\"|\"fold_closed\"|\"ws\"\n---@alias trouble.Indent.symbols table<trouble.Indent.type, string|string[]>\n\n---@class SymbolSegment: TextSegment\n---@field type trouble.Indent.type\n\n---@class trouble.Indent: {[number]: SymbolSegment}\n---@field symbols table<string, SymbolSegment>\nlocal M = {}\nM.__index = M\n\n---@param symbols? trouble.Indent.symbols\nfunction M.new(symbols)\n  local self = setmetatable({}, M)\n  symbols = vim.tbl_deep_extend(\"force\", Config.icons.indent, symbols or {})\n  self.symbols = {}\n  for k, v in pairs(symbols) do\n    local symbol = v\n    self.symbols[k] = type(symbol) == \"string\" and { str = symbol } or { str = symbol[1], hl = symbol[2] }\n    self.symbols[k].type = k\n    self.symbols[k].hl = self.symbols[k].hl or (\"TroubleIndent\" .. Util.camel(k))\n  end\n  return self\nend\n\n---@return number\n---@param opts? {display:boolean}\nfunction M:width(opts)\n  local ret = 0\n  for _, segment in ipairs(self) do\n    ret = ret + (opts and opts.display and vim.fn.strdisplaywidth(segment.str) or #segment.str)\n  end\n  return ret\nend\n\n-- Returns a new indent with all the symbols replaced with whitespace\nfunction M:indent()\n  local new = setmetatable({}, M)\n  for k, v in pairs(self) do\n    new[k] = type(k) == \"number\" and self.symbols.ws or v\n  end\n  return new\nend\n\nfunction M:clone()\n  local new = setmetatable({}, M)\n  for k, v in pairs(self) do\n    new[k] = v\n  end\n  return new\nend\n\n---@param other trouble.Indent.type\nfunction M:add(other)\n  table.insert(self, self.symbols[other])\n  return self\nend\n\n---@return SymbolSegment?\nfunction M:del()\n  return table.remove(self)\nend\n\n---@param other trouble.Indent.type\nfunction M:extend(other)\n  return self:clone():add(other)\nend\n\nfunction M:multi_line()\n  local last = self:del()\n  if last then\n    self:add(last.type == \"middle\" and \"top\" or last.type == \"last\" and \"ws\" or last.type)\n  end\n  return self\nend\n\nreturn M\n"
  },
  {
    "path": "lua/trouble/view/init.lua",
    "content": "local Format = require(\"trouble.format\")\nlocal Main = require(\"trouble.view.main\")\nlocal Preview = require(\"trouble.view.preview\")\nlocal Promise = require(\"trouble.promise\")\nlocal Render = require(\"trouble.view.render\")\nlocal Section = require(\"trouble.view.section\")\nlocal Spec = require(\"trouble.spec\")\nlocal Text = require(\"trouble.view.text\")\nlocal Util = require(\"trouble.util\")\nlocal Window = require(\"trouble.view.window\")\n\n---@class trouble.View\n---@field win trouble.Window\n---@field preview_win? trouble.Window\n---@field opts trouble.Mode\n---@field sections trouble.Section[]\n---@field renderer trouble.Render\n---@field first_render trouble.Promise\n---@field first_update trouble.Promise\n---@field moving uv_timer_t\n---@field clicked uv_timer_t\n---@field state table<string,any>\n---@field _filters table<string, trouble.ViewFilter>\n---@field private _main? trouble.Main\nlocal M = {}\nM.__index = M\nlocal _idx = 0\n---@type table<trouble.View, number>\nM._views = setmetatable({}, { __mode = \"k\" })\n\n---@type trouble.View[]\nM._auto = {}\n\n---@type table<string, trouble.Render.Location>\nM._last = {}\n\nM.MOVING_DELAY = 4000\n\nlocal uv = vim.loop or vim.uv\n\n---@param opts trouble.Mode\nfunction M.new(opts)\n  local self = setmetatable({}, M)\n  _idx = _idx + 1\n  M._views[self] = _idx\n  self.state = {}\n  self.opts = opts or {}\n  self._filters = {}\n  self.first_render = Promise.new(function() end)\n  self.first_update = Promise.new(function() end)\n  self.opts.win = vim.tbl_deep_extend(\"force\", self.opts.win or {}, Window.FOLDS)\n  self.opts.win.on_mount = function()\n    self:on_mount()\n  end\n  self.opts.win.on_close = function()\n    if not self.opts.auto_open then\n      for _, section in ipairs(self.sections) do\n        section:stop()\n      end\n    end\n  end\n\n  self.sections = {}\n  for _, s in ipairs(Spec.sections(self.opts)) do\n    local section = Section.new(s, self.opts)\n    section.on_update = function()\n      self:update()\n    end\n    table.insert(self.sections, section)\n  end\n\n  self.win = Window.new(self.opts.win)\n  self.opts.win = self.win.opts\n\n  self.preview_win = Window.new(self.opts.preview) or nil\n\n  self.renderer = Render.new(self.opts, {\n    padding = vim.tbl_get(self.opts.win, \"padding\", \"left\") or 0,\n    multiline = self.opts.multiline,\n  })\n  self.update = Util.throttle(M.update, Util.throttle_opts(self.opts.throttle.update, { ms = 10 }))\n  self.render = Util.throttle(M.render, Util.throttle_opts(self.opts.throttle.render, { ms = 10 }))\n  self.follow = Util.throttle(M.follow, Util.throttle_opts(self.opts.throttle.follow, { ms = 100 }))\n\n  if self.opts.auto_open then\n    -- add to a table, so that the view doesn't gc\n    table.insert(M._auto, self)\n    self:listen()\n    self:refresh()\n  end\n  self.moving = uv.new_timer()\n  self.clicked = uv.new_timer()\n  return self\nend\n\n---@alias trouble.View.filter {debug?: boolean, open?:boolean, mode?: string}\n\n---@param filter? trouble.View.filter\nfunction M.get(filter)\n  filter = filter or {}\n  ---@type {idx:number, mode?: string, view: trouble.View, is_open: boolean}[]\n  local ret = {}\n  for view, idx in pairs(M._views) do\n    local is_open = view.win:valid()\n    local opening = view.first_update:is_pending()\n    local ok = is_open or view.opts.auto_open or opening\n    ok = ok and (not filter.mode or filter.mode == view.opts.mode)\n    ok = ok and (not filter.open or is_open or opening)\n    if ok then\n      ret[#ret + 1] = {\n        idx = idx,\n        mode = view.opts.mode,\n        view = not filter.debug and view or {},\n        is_open = is_open,\n      }\n    end\n  end\n  table.sort(ret, function(a, b)\n    return a.idx < b.idx\n  end)\n  return ret\nend\n\nfunction M:on_mount()\n  vim.w[self.win.win].trouble = {\n    mode = self.opts.mode,\n    type = self.opts.win.type,\n    relative = self.opts.win.relative,\n    position = self.opts.win.position,\n  }\n\n  self:listen()\n  self.win:on(\"WinLeave\", function()\n    if self.opts.preview.type == \"main\" and self.clicked:is_active() and Preview.is_open() then\n      local main = self.preview_win.opts.win\n      local preview = self.preview_win.win\n      if main and preview and vim.api.nvim_win_is_valid(main) and vim.api.nvim_win_is_valid(preview) then\n        local view = vim.api.nvim_win_call(preview, vim.fn.winsaveview)\n        vim.api.nvim_win_call(main, function()\n          vim.fn.winrestview(view)\n        end)\n        vim.api.nvim_set_current_win(main)\n      end\n    end\n    Preview.close()\n  end)\n\n  local _self = Util.weak(self)\n\n  local preview = Util.throttle(\n    M.preview,\n    Util.throttle_opts(self.opts.throttle.preview, {\n      ms = 100,\n      debounce = true,\n    })\n  )\n\n  self.win:on(\"CursorMoved\", function()\n    local this = _self()\n    if not this then\n      return true\n    end\n    M._last[self.opts.mode or \"\"] = self:at()\n    if this.opts.auto_preview then\n      local loc = this:at()\n      if loc and loc.item then\n        preview(this, loc.item)\n      end\n    end\n  end)\n\n  if self.opts.follow then\n    -- tracking of the current item\n    self.win:on(\"CursorMoved\", function()\n      local this = _self()\n      if not this then\n        return true\n      end\n      if this.win:valid() then\n        this:follow()\n      end\n    end, { buffer = false })\n  end\n\n  self.win:on(\"OptionSet\", function()\n    local this = _self()\n    if not this then\n      return true\n    end\n    if this.win:valid() then\n      local foldlevel = vim.wo[this.win.win].foldlevel\n      if foldlevel ~= this.renderer.foldlevel then\n        this:fold_level({ level = foldlevel })\n      end\n    end\n  end, { pattern = \"foldlevel\", buffer = false })\n\n  for k, v in pairs(self.opts.keys) do\n    if v ~= false then\n      self:map(k, v)\n    end\n  end\n\n  self.win:map(\"<leftmouse>\", function()\n    self.clicked:start(100, 0, function() end)\n    return \"<leftmouse>\"\n  end, { remap = false, expr = true })\nend\n\n---@param node? trouble.Node\nfunction M:delete(node)\n  local selection = node and { node } or self:selection()\n  if #selection == 0 then\n    return\n  end\n  for _, n in ipairs(selection) do\n    n:delete()\n  end\n  self.opts.auto_refresh = false\n  self:render()\nend\n\n---@param node? trouble.Node\n---@param opts? trouble.Render.fold_opts\nfunction M:fold(node, opts)\n  node = node or self:at().node\n  if node then\n    self.renderer:fold(node, opts)\n    self:render()\n  end\nend\n\n---@param opts {level?:number, add?:number}\nfunction M:fold_level(opts)\n  self.renderer:fold_level(opts)\n  self:render()\nend\n\n---@param item? trouble.Item\n---@param opts? {split?: boolean, vsplit?:boolean}\nfunction M:jump(item, opts)\n  opts = opts or {}\n  item = item or self:at().item\n  vim.schedule(function()\n    Preview.close()\n  end)\n  if not item then\n    return Util.warn(\"No item to jump to\")\n  end\n\n  if not (item.buf or item.filename) then\n    Util.warn(\"No buffer or filename for item\")\n    return\n  end\n\n  item.buf = item.buf or vim.fn.bufadd(item.filename)\n\n  if not vim.api.nvim_buf_is_loaded(item.buf) then\n    vim.fn.bufload(item.buf)\n  end\n  if not vim.bo[item.buf].buflisted then\n    vim.bo[item.buf].buflisted = true\n  end\n  local main = self:main()\n  local win = main and main.win or 0\n\n  vim.api.nvim_win_call(win, function()\n    -- save position in jump list\n    vim.cmd(\"normal! m'\")\n  end)\n\n  if opts.split then\n    vim.api.nvim_win_call(win, function()\n      vim.cmd(\"split\")\n      win = vim.api.nvim_get_current_win()\n    end)\n  elseif opts.vsplit then\n    vim.api.nvim_win_call(win, function()\n      vim.cmd(\"vsplit\")\n      win = vim.api.nvim_get_current_win()\n    end)\n  end\n\n  vim.api.nvim_win_set_buf(win, item.buf)\n  -- order of the below seems important with splitkeep=screen\n  vim.api.nvim_set_current_win(win)\n  vim.api.nvim_win_set_cursor(win, item.pos)\n  vim.api.nvim_win_call(win, function()\n    vim.cmd(\"norm! zzzv\")\n  end)\n  return item\nend\n\nfunction M:wait(fn)\n  self.first_render:next(fn)\nend\n\n---@param item? trouble.Item\nfunction M:preview(item)\n  item = item or self:at().item\n  if not item then\n    return Util.warn(\"No item to preview\")\n  end\n\n  return Preview.open(self, item, { scratch = self.opts.preview.scratch })\nend\n\nfunction M:main()\n  self._main = Main.get(self.opts.pinned and self._main or nil)\n  return self._main\nend\n\nfunction M:goto_main()\n  local main = self:main()\n  if main then\n    vim.api.nvim_set_current_win(main.win)\n  end\nend\n\nfunction M:listen()\n  self:main()\n\n  for _, section in ipairs(self.sections) do\n    section:listen()\n  end\nend\n\n---@param cursor? number[]\nfunction M:at(cursor)\n  if not vim.api.nvim_buf_is_valid(self.win.buf) then\n    return {}\n  end\n  cursor = cursor or vim.api.nvim_win_get_cursor(self.win.win)\n  return self.renderer:at(cursor[1])\nend\n\nfunction M:selection()\n  if not vim.fn.mode():lower():find(\"v\") then\n    local ret = self:at()\n    return ret.node and { ret.node } or {}\n  end\n  vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes(\"<esc>\", true, false, true), \"x\", false)\n\n  local from = vim.api.nvim_buf_get_mark(self.win.buf, \"<\")[1]\n  local to = vim.api.nvim_buf_get_mark(self.win.buf, \">\")[1]\n  ---@type trouble.Node[]\n  local ret = {}\n  for row = from, to do\n    local node = self.renderer:at(row).node\n    if not vim.tbl_contains(ret, node) then\n      ret[#ret + 1] = node\n    end\n  end\n  return ret\nend\n\n---@param key string\n---@param action trouble.Action.spec\nfunction M:map(key, action)\n  action = Spec.action(action)\n  local _self = Util.weak(self)\n  self.win:map(key, function()\n    local this = _self()\n    if this then\n      this:action(action)\n    end\n  end, { desc = action.desc, mode = action.mode })\nend\n\n---@param opts? {idx?: number, up?:number, down?:number, jump?:boolean}\nfunction M:move(opts)\n  -- start the moving timer. Will stop any previous timers,\n  -- so this acts as a debounce.\n  -- This is needed to prevent `follow` from being called\n  self.moving:start(M.MOVING_DELAY, 0, function() end)\n\n  opts = opts or {}\n  local cursor = vim.api.nvim_win_get_cursor(self.win.win)\n  local from = 1\n  local to = vim.api.nvim_buf_line_count(self.win.buf)\n  local todo = opts.idx or opts.up or opts.down or 0\n\n  if opts.idx and opts.idx < 0 then\n    from, to = to, 1\n    todo = math.abs(todo)\n  elseif opts.down then\n    from = cursor[1] + 1\n  elseif opts.up then\n    from = cursor[1] - 1\n    to = 1\n  end\n\n  for row = from, to, from > to and -1 or 1 do\n    local info = self.renderer:at(row)\n    if info.item and info.first_line then\n      todo = todo - 1\n      if todo == 0 then\n        vim.api.nvim_win_set_cursor(self.win.win, { row, cursor[2] })\n        if opts.jump then\n          self:jump(info.item)\n        end\n        break\n      end\n    end\n  end\nend\n\n---@param action trouble.Action.spec\n---@param opts? table\nfunction M:action(action, opts)\n  action = Spec.action(action)\n  self:wait(function()\n    local at = self:at() or {}\n    action.action(self, {\n      item = at.item,\n      node = at.node,\n      opts = type(opts) == \"table\" and opts or {},\n    })\n  end)\nend\n\n---@param opts? {update?: boolean, opening?: boolean}\nfunction M:refresh(opts)\n  opts = opts or {}\n  if not (opts.opening or self.win:valid() or self.opts.auto_open) then\n    return\n  end\n  ---@param section trouble.Section\n  return Promise.all(vim.tbl_map(function(section)\n    return section:refresh(opts)\n  end, self.sections))\nend\n\nfunction M:help()\n  local text = Text.new({ padding = 1 })\n\n  text:nl():append(\"# Help \", \"Title\"):nl()\n  text:append(\"Press \", \"Comment\"):append(\"<q>\", \"Special\"):append(\" to close\", \"Comment\"):nl():nl()\n  text:append(\"# Keymaps \", \"Title\"):nl():nl()\n  ---@type string[]\n  local keys = vim.tbl_keys(self.win.keys)\n  table.sort(keys, function(a, b)\n    local lowa = string.lower(a)\n    local lowb = string.lower(b)\n    if lowa == lowb then\n      return a > b -- Preserve original order for equal strings\n    else\n      return lowa < lowb\n    end\n  end)\n  for _, key in ipairs(keys) do\n    local desc = self.win.keys[key]\n    text:append(\"  - \", \"@punctuation.special.markdown\")\n    text:append(key, \"Special\"):append(\" \"):append(desc):nl()\n  end\n  text:trim()\n\n  local win = Window.new({\n    type = \"float\",\n    size = { width = text:width(), height = text:height() },\n    border = \"rounded\",\n    wo = { cursorline = false },\n  })\n  win:open():focus()\n  text:render(win.buf)\n  vim.bo[win.buf].modifiable = false\n\n  win:map(\"<esc>\", win.close)\n  win:map(\"q\", win.close)\nend\n\nfunction M:is_open()\n  return self.win:valid()\nend\n\nfunction M:open()\n  if self.win:valid() then\n    return self\n  end\n  self\n    :refresh({ update = false, opening = true })\n    :next(function()\n      local count = self:count()\n      if count == 0 then\n        if not self.opts.open_no_results then\n          if self.opts.warn_no_results then\n            Util.warn({\n              \"No results for **\" .. self.opts.mode .. \"**\",\n              \"Buffer: \" .. vim.api.nvim_buf_get_name(self:main().buf),\n            })\n          end\n          return\n        end\n      elseif count == 1 and self.opts.auto_jump then\n        self:jump(self:flatten()[1])\n        return self:close()\n      end\n      self.win:open()\n      self:update()\n    end)\n    :next(self.first_update.resolve)\n  return self\nend\n\nfunction M:close()\n  if vim.api.nvim_get_current_win() == self.win.win then\n    self:goto_main()\n  end\n  Preview.close()\n  self.win:close()\n  return self\nend\n\nfunction M:count()\n  local count = 0\n  for _, section in ipairs(self.sections) do\n    if section.node then\n      count = count + section.node:count()\n    end\n  end\n  return count\nend\n\nfunction M:flatten()\n  local ret = {}\n  for _, section in ipairs(self.sections) do\n    section.node:flatten(ret)\n  end\n  return ret\nend\n\n-- called when results are updated\nfunction M:update()\n  local is_open = self.win:valid()\n  local count = self:count()\n\n  if count == 0 and is_open and self.opts.auto_close then\n    return self:close()\n  end\n\n  if self.opts.auto_open and not is_open and count > 0 then\n    self.win:open()\n    is_open = true\n  end\n\n  if not is_open then\n    return\n  end\n\n  self:render()\nend\n\n---@param filter trouble.Filter\n---@param opts? trouble.ViewFilter.opts\nfunction M:filter(filter, opts)\n  opts = opts or {}\n\n  ---@type trouble.ViewFilter\n  local view_filter = vim.tbl_deep_extend(\"force\", {\n    id = vim.inspect(filter),\n    filter = filter,\n    data = opts.data,\n    template = opts.template,\n  }, opts)\n\n  if opts.del or (opts.toggle and self._filters[view_filter.id]) then\n    self._filters[view_filter.id] = nil\n  else\n    self._filters[view_filter.id] = view_filter\n  end\n\n  local filters = vim.tbl_count(self._filters) > 0\n      and vim.tbl_map(function(f)\n        return f.filter\n      end, vim.tbl_values(self._filters))\n    or nil\n\n  for _, section in ipairs(self.sections) do\n    section.filter = filters\n  end\n  self:refresh()\nend\n\nfunction M:header()\n  local ret = {} ---@type trouble.Format[][]\n  for _, filter in pairs(self._filters) do\n    local data = vim.tbl_deep_extend(\"force\", {\n      filter = filter.filter,\n    }, type(filter.filter) == \"table\" and filter.filter or {}, filter.data or {})\n    local template = filter.template or \"{hl:Title}Filter:{hl} {filter}\"\n    ret[#ret + 1] = self:format(template, data)\n  end\n  return ret\nend\n\n---@param id string\nfunction M:get_filter(id)\n  return self._filters[id]\nend\n\n---@param template string\n---@param data table<string,any>\nfunction M:format(template, data)\n  data.source = \"view\"\n  assert(self.opts, \"opts is nil\")\n  return Format.format(template, { item = data, opts = self.opts })\nend\n\n-- render the results\nfunction M:render()\n  if not self.win:valid() then\n    return\n  end\n\n  local loc = self:at()\n  local restore_loc = self.opts.restore and self.first_render:is_pending() and M._last[self.opts.mode or \"\"]\n  if restore_loc then\n    loc = restore_loc\n  end\n\n  -- render sections\n  self.renderer:clear()\n  self.renderer:nl()\n  for _ = 1, vim.tbl_get(self.opts.win, \"padding\", \"top\") or 0 do\n    self.renderer:nl()\n  end\n\n  local header = self:header()\n  for _, h in ipairs(header) do\n    for _, ff in ipairs(h) do\n      self.renderer:append(ff.text, ff)\n    end\n    self.renderer:nl()\n  end\n\n  self.renderer:sections(self.sections)\n  self.renderer:trim()\n\n  -- calculate initial folds\n  if self.renderer.foldlevel == nil then\n    local level = vim.wo[self.win.win].foldlevel\n    if level < self.renderer.max_depth then\n      self.renderer:fold_level({ level = level })\n      -- render again to apply folds\n      return self:render()\n    end\n  end\n\n  vim.schedule(function()\n    self.first_render.resolve()\n  end)\n\n  -- render extmarks and restore window view\n  local view = vim.api.nvim_win_call(self.win.win, vim.fn.winsaveview)\n  self.renderer:render(self.win.buf)\n  vim.api.nvim_win_call(self.win.win, function()\n    vim.fn.winrestview(view)\n  end)\n\n  if self.opts.follow and self:follow() then\n    return\n  end\n\n  -- when window is at top, dont move cursor\n  if not restore_loc and view.topline == 1 then\n    return\n  end\n\n  -- fast exit when cursor is already on the right item\n  local new_loc = self:at()\n  if new_loc.node and loc.node and new_loc.node.id == loc.node.id then\n    return\n  end\n\n  -- Move cursor to the same item\n  local cursor = vim.api.nvim_win_get_cursor(self.win.win)\n  local item_row ---@type number?\n  if loc.node then\n    for row, l in pairs(self.renderer._locations) do\n      if loc.node:is(l.node) then\n        item_row = row\n        break\n      end\n    end\n  end\n\n  -- Move cursor to the actual item when found\n  if item_row and item_row ~= cursor[1] then\n    vim.api.nvim_win_set_cursor(self.win.win, { item_row, cursor[2] })\n    return\n  end\nend\n\n-- When not in the trouble window, try to show the range\nfunction M:follow()\n  if not self.win:valid() then -- trouble is closed\n    return\n  end\n  if self.moving:is_active() then -- dont follow when moving\n    return\n  end\n  local current_win = vim.api.nvim_get_current_win()\n  if current_win == self.win.win then -- inside the trouble window\n    return\n  end\n  local Filter = require(\"trouble.filter\")\n  local ctx = { opts = self.opts, main = self:main() }\n  local fname = vim.api.nvim_buf_get_name(ctx.main.buf or 0)\n  local loc = self:at()\n\n  -- check if we're already in the file group\n  local in_group = loc.node and loc.node.item and loc.node.item.filename == fname\n\n  ---@type number[]|nil\n  local cursor_item = nil\n  local cursor_group = cursor_item\n\n  for row, l in pairs(self.renderer._locations) do\n    -- only return the group if we're not yet in the group\n    -- and the group's filename matches the current file\n    local is_group = not in_group and l.node and l.node.group and l.node.item and l.node.item.filename == fname\n    if is_group then\n      cursor_group = { row, 1 }\n    end\n\n    -- prefer a full match\n    local is_current = l.item and Filter.is(l.item, { range = true }, ctx)\n    if is_current then\n      cursor_item = { row, 1 }\n    end\n  end\n\n  local cursor = cursor_item or cursor_group\n  if cursor then\n    -- make sure the cursorline is visible\n    vim.wo[self.win.win].cursorline = true\n    vim.api.nvim_win_set_cursor(self.win.win, cursor)\n    return true\n  end\nend\n\nreturn M\n"
  },
  {
    "path": "lua/trouble/view/main.lua",
    "content": "local Preview = require(\"trouble.view.preview\")\n\n---@class trouble.Main\n---@field win number\n---@field buf number\n---@field filename string\n---@field cursor trouble.Pos\n\nlocal M = {}\nM._main = nil ---@type trouble.Main?\n\nfunction M.setup()\n  local group = vim.api.nvim_create_augroup(\"trouble.main\", { clear = true })\n  vim.api.nvim_create_autocmd({ \"BufEnter\", \"WinEnter\" }, {\n    group = group,\n    callback = function()\n      local win = vim.api.nvim_get_current_win()\n      local buf = vim.api.nvim_win_get_buf(win)\n      if M._valid(win, buf) then\n        M.set(M._info(win))\n      end\n    end,\n  })\n  M.set(M._find())\nend\n\n---@param main trouble.Main\nfunction M.set(main)\n  M._main = main\nend\n\nfunction M._valid(win, buf)\n  if not win or not buf then\n    return false\n  end\n  if not vim.api.nvim_win_is_valid(win) or not vim.api.nvim_buf_is_valid(buf) then\n    return false\n  end\n  if vim.api.nvim_win_get_buf(win) ~= buf then\n    return false\n  end\n  if Preview.is_win(win) or vim.w[win].trouble then\n    return false\n  end\n  if vim.api.nvim_win_get_config(win).relative ~= \"\" then\n    return false\n  end\n  if vim.bo[buf].buftype ~= \"\" then\n    return false\n  end\n  return true\nend\n\n---@private\nfunction M._find()\n  local wins = vim.api.nvim_list_wins()\n  table.insert(wins, 1, vim.api.nvim_get_current_win())\n  for _, win in ipairs(wins) do\n    local b = vim.api.nvim_win_get_buf(win)\n    if M._valid(win, b) then\n      return M._info(win)\n    end\n  end\nend\n\n---@private\n---@return trouble.Main\nfunction M._info(win)\n  local b = vim.api.nvim_win_get_buf(win)\n  return {\n    win = win,\n    buf = b,\n    filename = vim.fs.normalize(vim.api.nvim_buf_get_name(b)),\n    cursor = vim.api.nvim_win_get_cursor(win),\n  }\nend\n\n---@param main? trouble.Main\n---@return trouble.Main?\nfunction M.get(main)\n  main = main or M._main\n\n  local valid = main and M._valid(main.win, main.buf)\n\n  if not valid then\n    main = M._find()\n  end\n  -- Always return a main window even if it is not valid\n  main = main or M._info(vim.api.nvim_get_current_win())\n  main.cursor = vim.api.nvim_win_get_cursor(main.win)\n  return main\nend\n\nreturn M\n"
  },
  {
    "path": "lua/trouble/view/preview.lua",
    "content": "local Render = require(\"trouble.view.render\")\nlocal Util = require(\"trouble.util\")\n\n---@alias trouble.Preview {item:trouble.Item, win:number, buf: number, close:fun()}\n\nlocal M = {}\nM.preview = nil ---@type trouble.Preview?\n\nfunction M.is_open()\n  return M.preview ~= nil\nend\n\nfunction M.is_win(win)\n  return M.preview and M.preview.win == win\nend\n\nfunction M.item()\n  return M.preview and M.preview.item\nend\n\nfunction M.close()\n  local preview = M.preview\n  M.preview = nil\n  if not preview then\n    return\n  end\n  Render.reset(preview.buf)\n  preview.close()\nend\n\n--- Create a preview buffer for an item.\n--- If the item has a loaded buffer, use that,\n--- otherwise create a new buffer.\n---@param item trouble.Item\n---@param opts? {scratch?:boolean}\nfunction M.create(item, opts)\n  opts = opts or {}\n\n  local buf = item.buf or vim.fn.bufnr(item.filename)\n\n  if item.filename and vim.fn.isdirectory(item.filename) == 1 then\n    return\n  end\n\n  -- create a scratch preview buffer when needed\n  if not (buf and vim.api.nvim_buf_is_loaded(buf)) then\n    if opts.scratch then\n      buf = vim.api.nvim_create_buf(false, true)\n      vim.bo[buf].bufhidden = \"wipe\"\n      vim.bo[buf].buftype = \"nofile\"\n      local lines = Util.get_lines({ path = item.filename, buf = item.buf })\n      if not lines then\n        return\n      end\n      vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines)\n      local ft = item:get_ft(buf)\n      if ft then\n        local lang = vim.treesitter.language.get_lang(ft)\n        if not pcall(vim.treesitter.start, buf, lang) then\n          vim.bo[buf].syntax = ft\n        end\n      end\n    else\n      item.buf = vim.fn.bufadd(item.filename)\n      buf = item.buf\n\n      if not vim.api.nvim_buf_is_loaded(item.buf) then\n        vim.fn.bufload(item.buf)\n      end\n      if not vim.bo[item.buf].buflisted then\n        vim.bo[item.buf].buflisted = true\n      end\n    end\n  end\n\n  return buf\nend\n\n---@param view trouble.View\n---@param item trouble.Item\n---@param opts? {scratch?:boolean}\nfunction M.open(view, item, opts)\n  if M.item() == item then\n    return\n  end\n  if M.preview and M.preview.item.filename ~= item.filename then\n    M.close()\n  end\n\n  if not M.preview then\n    local buf = M.create(item, opts)\n    if not buf then\n      return\n    end\n\n    M.preview = M.preview_win(buf, view)\n\n    M.preview.buf = buf\n  end\n  M.preview.item = item\n\n  Render.reset(M.preview.buf)\n\n  -- make sure we highlight at least one character\n  local end_pos = { item.end_pos[1], item.end_pos[2] }\n  if end_pos[1] == item.pos[1] and end_pos[2] == item.pos[2] then\n    end_pos[2] = end_pos[2] + 1\n  end\n\n  -- highlight the line\n  Util.set_extmark(M.preview.buf, Render.ns, item.pos[1] - 1, 0, {\n    end_row = end_pos[1],\n    hl_group = \"CursorLine\",\n    hl_eol = true,\n    strict = false,\n    priority = 150,\n  })\n\n  -- highlight the range\n  Util.set_extmark(M.preview.buf, Render.ns, item.pos[1] - 1, item.pos[2], {\n    end_row = end_pos[1] - 1,\n    end_col = end_pos[2],\n    hl_group = \"TroublePreview\",\n    strict = false,\n    priority = 160,\n  })\n  local _, source = require(\"trouble.sources\").get(item.source)\n  if source and source.preview then\n    source.preview(item, M.preview)\n  end\n\n  -- no autocmds should be triggered. So LSP's etc won't try to attach in the preview\n  Util.noautocmd(function()\n    if pcall(vim.api.nvim_win_set_cursor, M.preview.win, item.pos) then\n      vim.api.nvim_win_call(M.preview.win, function()\n        vim.cmd(\"norm! zzzv\")\n      end)\n    end\n  end)\n\n  return item\nend\n\n---@param buf number\n---@param view trouble.View\nfunction M.preview_win(buf, view)\n  if view.opts.preview.type == \"main\" then\n    local main = view:main()\n    if not main then\n      Util.debug(\"No main window\")\n      return\n    end\n    view.preview_win.opts.win = main.win\n  else\n    view.preview_win.opts.win = view.win.win\n  end\n\n  view.preview_win:open()\n  Util.noautocmd(function()\n    view.preview_win:set_buf(buf)\n    view.preview_win:set_options(\"win\")\n    vim.w[view.preview_win.win].trouble_preview = true\n  end)\n\n  return {\n    win = view.preview_win.win,\n    close = function()\n      view.preview_win:close()\n    end,\n  }\nend\n\nreturn M\n"
  },
  {
    "path": "lua/trouble/view/render.lua",
    "content": "local Cache = require(\"trouble.cache\")\nlocal Format = require(\"trouble.format\")\nlocal Indent = require(\"trouble.view.indent\")\nlocal Text = require(\"trouble.view.text\")\nlocal Util = require(\"trouble.util\")\n\n---@alias trouble.Render.Location {item?: trouble.Item, node?: trouble.Node, first_line?:boolean}\n---@class trouble.Render: trouble.Text\n---@field _locations trouble.Render.Location[] Maps line numbers to items.\n---@field _folded table<string, true>\n---@field root_nodes trouble.Node[]\n---@field foldlevel? number\n---@field foldenable boolean\n---@field max_depth number\n---@field view trouble.View\n---@field opts trouble.Config\nlocal M = setmetatable({}, Text)\nM.__index = M\n\n---@class trouble.Render.opts: trouble.Text.opts\n---@field indent? trouble.Indent.symbols\n---@field formatters? table<string, trouble.Formatter>\n\n---@param text_opts trouble.Text.opts\n---@param opts trouble.Config\nfunction M.new(opts, text_opts)\n  local text = Text.new(text_opts)\n  ---@type trouble.Render\n  ---@diagnostic disable-next-line: assign-type-mismatch\n  local self = setmetatable(text, M)\n  self.opts = opts\n  self._folded = {}\n  self.foldenable = true\n  self:clear()\n  return self\nend\n\n---@alias trouble.Render.fold_opts {action?: \"open\"|\"close\"|\"toggle\", recursive?: boolean}\n---@param node trouble.Node\n---@param opts? trouble.Render.fold_opts\nfunction M:fold(node, opts)\n  self.foldenable = true\n  opts = opts or {}\n  local action = opts.action or \"toggle\"\n  if node:is_leaf() and node.parent then\n    node = node.parent\n  end\n  local id = node.id\n  if action == \"toggle\" then\n    if self._folded[id] then\n      action = \"open\"\n    else\n      action = \"close\"\n    end\n  end\n  local stack = { node }\n  while #stack > 0 do\n    local n = table.remove(stack) --[[@as trouble.Node]]\n    if not n:is_leaf() then\n      if action == \"open\" then\n        self._folded[n.id] = nil\n        local parent = n.parent\n        while parent do\n          self._folded[parent.id] = nil\n          parent = parent.parent\n        end\n      else\n        self._folded[n.id] = true\n      end\n      if opts.recursive then\n        for _, c in ipairs(n.children or {}) do\n          table.insert(stack, c)\n        end\n      end\n    end\n  end\nend\n\n---@param opts {level?:number, add?:number}\nfunction M:fold_level(opts)\n  self.foldenable = true\n  self.foldlevel = self.foldlevel or (self.max_depth - 1) or 0\n  if opts.level then\n    self.foldlevel = opts.level\n  end\n  if opts.add then\n    self.foldlevel = self.foldlevel + opts.add\n  end\n  self.foldlevel = math.min(self.max_depth - 1, self.foldlevel)\n  self.foldlevel = math.max(0, self.foldlevel)\n  local stack = {}\n  for _, node in ipairs(self.root_nodes) do\n    table.insert(stack, node)\n  end\n  while #stack > 0 do\n    ---@type trouble.Node\n    local node = table.remove(stack)\n    if not node:is_leaf() then\n      if node:depth() > self.foldlevel then\n        self._folded[node.id] = true\n      else\n        self._folded[node.id] = nil\n      end\n      for _, c in ipairs(node.children or {}) do\n        table.insert(stack, c)\n      end\n    end\n  end\nend\n\nfunction M:clear()\n  Cache.langs:clear()\n  self.max_depth = 0\n  self._lines = {}\n  self.ts_regions = {}\n  self._locations = {}\n  self.root_nodes = {}\nend\n\n---@param sections trouble.Section[]\nfunction M:sections(sections)\n  for _, section in ipairs(sections) do\n    local nodes = section.node and section.node.children\n    if nodes and #nodes > 0 then\n      self:section(section.section, nodes)\n    end\n  end\nend\n\n---@param section trouble.Section.opts\n---@param nodes trouble.Node[]\nfunction M:section(section, nodes)\n  for n, node in ipairs(nodes) do\n    table.insert(self.root_nodes, node)\n    self.max_depth = math.max(self.max_depth, node:degree())\n    self:node(node, section, Indent.new(self.opts.icons.indent), n == #nodes)\n  end\nend\n\nfunction M:is_folded(node)\n  return self.foldenable and self._folded[node.id]\nend\n\n---@param node trouble.Node\n---@param section trouble.Section.opts\n---@param indent trouble.Indent\n---@param is_last boolean\nfunction M:node(node, section, indent, is_last)\n  node.folded = self:is_folded(node)\n  if node.item then\n    ---@type trouble.Indent.type\n    local symbol = self:is_folded(node) and \"fold_closed\"\n      or node:depth() == 1 and \"fold_open\"\n      or is_last and \"last\"\n      or \"middle\"\n    symbol = node:depth() == 1 and node:is_leaf() and \"ws\" or symbol\n    indent:add(symbol)\n    -- self:item(node.item, node, section.groups[node.depth].format, true, indent)\n    self:item(node, section, indent)\n    indent:del()\n  end\n\n  if self:is_folded(node) then\n    return -- don't render children\n  end\n\n  indent:add((is_last or node:depth() == 1) and \"ws\" or \"top\")\n\n  for i, n in ipairs(node.children or {}) do\n    self:node(n, section, indent, i == #node.children)\n  end\n\n  indent:del()\nend\n\n--- Returns the item and node at the given row.\n--- For a group, only the node is returned.\n--- To get the group item used for formatting, use `node.items[1]`.\n---@param row number\nfunction M:at(row)\n  return self._locations[row] or {}\nend\n\n---@param node trouble.Node\n---@param section trouble.Section.opts\n---@param indent trouble.Indent\nfunction M:item(node, section, indent)\n  local item = node.item\n  if not item then\n    return\n  end\n  local is_group = node.group ~= nil\n  local row = self:row()\n  local format_string = node.group and node.group.format or section.format\n  local cache_key = \"render:\" .. node:depth() .. format_string\n\n  ---@type TextSegment[]?\n  local segments = not is_group and item.cache[cache_key]\n\n  if not self.opts.indent_guides then\n    indent = indent:indent()\n  end\n  if self._opts.indent ~= false then\n    self:append(indent)\n  end\n  if segments then\n    self:append(segments)\n  else\n    local format = Format.format(format_string, { item = item, node = node, opts = self.opts })\n    indent:multi_line()\n    for _, ff in ipairs(format) do\n      local text = self._opts.multiline and ff.text or ff.text:gsub(\"[\\n\\r]+\", \" \")\n      local offset ---@type number? start column of the first line\n      local first ---@type string? first line\n      if ff.hl == \"ts\" then\n        local lang = item:get_lang()\n        if lang then\n          ff.hl = \"ts.\" .. lang\n        else\n          ff.hl = nil\n        end\n      end\n      for l, line in Util.lines(text) do\n        if l == 1 then\n          first = line\n        else\n          -- PERF: most items are single line, so do heavy lifting only when more than one line\n          offset = offset or (self:col({ display = true }) - vim.fn.strdisplaywidth(first or \"\"))\n          self:nl()\n          self:append(indent)\n          local indent_width = indent:width({ display = true })\n          -- align to item column\n          if offset > indent_width then\n            self:append((\" \"):rep(offset - indent_width))\n          end\n        end\n        self:append(line, {\n          hl = ff.hl,\n          line = l,\n        })\n      end\n    end\n    -- NOTE:\n    -- * don't cache groups, since they can contain aggregates.\n    -- * don't cache multi-line items\n    -- * don't cache the indent part of the line\n    if not is_group and self:row() == row then\n      item.cache[cache_key] =\n        vim.list_slice(self._lines[#self._lines], self._opts.indent == false and 1 or (#indent + 1))\n    end\n  end\n\n  for r = row, self:row() do\n    self._locations[r] = {\n      first_line = r == row,\n      item = not is_group and item or nil,\n      node = node,\n    }\n  end\n\n  self:nl()\nend\n\nreturn M\n"
  },
  {
    "path": "lua/trouble/view/section.lua",
    "content": "local Filter = require(\"trouble.filter\")\nlocal Main = require(\"trouble.view.main\")\nlocal Preview = require(\"trouble.view.preview\")\nlocal Promise = require(\"trouble.promise\")\nlocal Sort = require(\"trouble.sort\")\nlocal Sources = require(\"trouble.sources\")\nlocal Tree = require(\"trouble.tree\")\nlocal Util = require(\"trouble.util\")\n\n---@class trouble.Section\n---@field section trouble.Section.opts\n---@field finder trouble.Source.get\n---@field private _main? trouble.Main\n---@field opts trouble.Config\n---@field items trouble.Item[]\n---@field node? trouble.Node\n---@field fetching boolean\n---@field filter? trouble.Filter\n---@field id number\n---@field on_update? fun(self: trouble.Section)\n---@field _refresh fun()\nlocal M = {}\nM._id = 0\n\n---@param section trouble.Section.opts\n---@param opts trouble.Config\nfunction M.new(section, opts)\n  local self = setmetatable({}, { __index = M })\n  self.section = section\n  self.opts = opts\n  M._id = M._id + 1\n  self.id = M._id\n  self.finder = Sources.get(section.source)\n  self.items = {}\n  self:main()\n\n  local _self = Util.weak(self)\n\n  self._refresh = Util.throttle(\n    M.refresh,\n    Util.throttle_opts(opts.throttle.refresh, {\n      ms = 20,\n      is_running = function()\n        local this = _self()\n        return this and this.fetching\n      end,\n    })\n  )\n\n  return self\nend\n\n---@param opts? {update?: boolean}\nfunction M:refresh(opts)\n  -- if self.section.source ~= \"lsp.document_symbols\" then\n  --   Util.debug(\"Section Refresh\", {\n  --     id = self.id,\n  --     source = self.section.source,\n  --   })\n  -- end\n  self.fetching = true\n  return Promise.new(function(resolve)\n    self:main_call(function(main)\n      local ctx = { opts = self.opts, main = main }\n      self.finder(function(items)\n        items = Filter.filter(items, self.section.filter, ctx)\n        if self.filter then\n          items = Filter.filter(items, self.filter, ctx)\n        end\n        items = Sort.sort(items, self.section.sort, ctx)\n        self.items = items\n        self.node = Tree.build(items, self.section)\n        if not (opts and opts.update == false) then\n          self:update()\n        end\n        resolve(self)\n      end, ctx)\n    end)\n  end)\n    :catch(Util.error)\n    :timeout(2000)\n    :catch(function() end)\n    :finally(function()\n      self.fetching = false\n    end)\nend\n\n---@param fn fun(main: trouble.Main)\nfunction M:main_call(fn)\n  local main = self:main()\n\n  if not main then\n    return\n  end\n\n  if Preview.is_win(main.win) then\n    return\n  end\n\n  local current = {\n    win = vim.api.nvim_get_current_win(),\n    buf = vim.api.nvim_get_current_buf(),\n    cursor = vim.api.nvim_win_get_cursor(0),\n  }\n\n  if vim.deep_equal(current, main) then\n    fn(main)\n  elseif vim.api.nvim_win_get_buf(main.win) == main.buf then\n    vim.api.nvim_win_call(main.win, function()\n      fn(main)\n    end)\n  else\n    Util.debug({\n      \"Main window switched buffers\",\n      \"Main: \" .. vim.api.nvim_buf_get_name(main.buf),\n      \"Current: \" .. vim.api.nvim_buf_get_name(vim.api.nvim_win_get_buf(main.win)),\n    })\n  end\nend\n\nfunction M:update()\n  if self.on_update then\n    self:on_update()\n  end\nend\n\nfunction M:main()\n  self._main = Main.get(self.opts.pinned and self._main or nil)\n  return self._main\nend\n\nfunction M:augroup()\n  return \"trouble.section.\" .. self.section.source .. \".\" .. self.id\nend\n\nfunction M:stop()\n  pcall(vim.api.nvim_del_augroup_by_name, self:augroup())\nend\n\nfunction M:listen()\n  local _self = Util.weak(self)\n\n  local group = vim.api.nvim_create_augroup(self:augroup(), { clear = true })\n  for _, event in ipairs(self.section.events or {}) do\n    vim.api.nvim_create_autocmd(event.event, {\n      group = group,\n      pattern = event.pattern,\n      callback = function(e)\n        local this = _self()\n        if not this then\n          return true\n        end\n        if not this.opts.auto_refresh then\n          return\n        end\n        if not vim.api.nvim_buf_is_valid(e.buf) then\n          return\n        end\n        if event.main then\n          local main = this:main()\n          if main and main.buf ~= e.buf then\n            return\n          end\n        end\n        if e.event == \"BufEnter\" and vim.bo[e.buf].buftype ~= \"\" then\n          return\n        end\n        this:_refresh()\n      end,\n    })\n  end\nend\n\nreturn M\n"
  },
  {
    "path": "lua/trouble/view/text.lua",
    "content": "local Util = require(\"trouble.util\")\n\n---@class TextSegment\n---@field str string Text\n---@field hl? string Extmark hl group\n---@field ts? string TreeSitter language\n---@field line? number line number in a multiline segment\n---@field width? number\n\n---@alias Extmark {hl_group?:string, col?:number, row?:number, end_col?:number}\n\n---@class trouble.Text.opts\n---@field padding? number\n---@field multiline? boolean\n---@field indent? boolean\n\n---@class trouble.Text\n---@field _lines TextSegment[][]\n---@field _col number\n---@field _indents string[]\n---@field _opts trouble.Text.opts\nlocal M = {}\nM.__index = M\n\nM.ns = vim.api.nvim_create_namespace(\"trouble.text\")\n\nfunction M.reset(buf)\n  if vim.api.nvim_buf_is_valid(buf) then\n    vim.api.nvim_buf_clear_namespace(buf, M.ns, 0, -1)\n  end\nend\n\n---@param opts? trouble.Text.opts\nfunction M.new(opts)\n  local self = setmetatable({}, M)\n  self._lines = {}\n  self._col = 0\n  self._opts = opts or {}\n  self._opts.padding = self._opts.padding or 0\n  self._indents = {}\n  for i = 0, 100, 1 do\n    self._indents[i] = (\" \"):rep(i)\n  end\n  return self\nend\n\nfunction M:height()\n  return #self._lines\nend\n\nfunction M:width()\n  local width = 0\n  for _, line in ipairs(self._lines) do\n    local w = 0\n    for _, segment in ipairs(line) do\n      w = w + vim.fn.strdisplaywidth(segment.str)\n    end\n    width = math.max(width, w)\n  end\n  return width + ((self._opts.padding or 0) * 2)\nend\n\n---@param text string|TextSegment[]\n---@param opts? string|{ts?:string, hl?:string, line?:number}\nfunction M:append(text, opts)\n  opts = opts or {}\n  if #self._lines == 0 then\n    self:nl()\n  end\n\n  if type(text) == \"table\" then\n    for _, s in ipairs(text) do\n      s.width = s.width or #s.str\n      self._col = self._col + s.width\n      table.insert(self._lines[#self._lines], s)\n    end\n    return self\n  end\n\n  opts = type(opts) == \"string\" and { hl = opts } or opts\n  if opts.hl == \"md\" then\n    opts.ts = \"markdown\"\n  elseif opts.hl and opts.hl:sub(1, 3) == \"ts.\" then\n    opts.ts = opts.hl:sub(4)\n  end\n\n  for l, line in Util.lines(text) do\n    local width = #line\n    self._col = self._col + width\n    table.insert(self._lines[#self._lines], {\n      str = line,\n      width = width,\n      hl = opts.hl,\n      ts = opts.ts,\n      line = opts.line or l,\n    })\n  end\n  return self\nend\n\nfunction M:nl()\n  table.insert(self._lines, {})\n  self._col = 0\n  return self\nend\n\n---@param opts? {sep?:string}\nfunction M:statusline(opts)\n  local sep = opts and opts.sep or \" \"\n  local lines = {} ---@type string[]\n  for _, line in ipairs(self._lines) do\n    local parts = {}\n    for _, segment in ipairs(line) do\n      local str = segment.str:gsub(\"%%\", \"%%%%\")\n      if segment.hl then\n        str = (\"%%#%s#%s%%*\"):format(segment.hl, str)\n      end\n      parts[#parts + 1] = str\n    end\n    table.insert(lines, table.concat(parts, \"\"))\n  end\n  return table.concat(lines, sep)\nend\n\nfunction M:render(buf)\n  local lines = {}\n\n  local padding = (\" \"):rep(self._opts.padding)\n  for _, line in ipairs(self._lines) do\n    local parts = { padding }\n    for _, segment in ipairs(line) do\n      parts[#parts + 1] = segment.str\n    end\n    table.insert(lines, table.concat(parts, \"\"))\n  end\n\n  vim.bo[buf].modifiable = true\n  vim.api.nvim_buf_clear_namespace(buf, M.ns, 0, -1)\n  vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines)\n\n  local regions = {} ---@type trouble.LangRegions\n\n  for l, line in ipairs(self._lines) do\n    local col = self._opts.padding or 0\n    local row = l - 1\n\n    for _, segment in ipairs(line) do\n      local width = segment.width\n      if segment.ts then\n        regions[segment.ts or \"\"] = regions[segment.ts] or {}\n        local ts_regions = regions[segment.ts or \"\"]\n        local last_region = ts_regions[#ts_regions]\n        if not last_region then\n          last_region = {}\n          table.insert(ts_regions, last_region)\n        end\n        -- combine multiline item segments in one region\n        if segment.line and #last_region ~= segment.line - 1 then\n          last_region = {}\n          table.insert(ts_regions, last_region)\n        end\n        table.insert(last_region, {\n          row,\n          col,\n          row,\n          col + width + 1,\n        })\n      elseif segment.hl then\n        Util.set_extmark(buf, M.ns, row, col, {\n          hl_group = segment.hl,\n          end_col = col + width,\n        })\n      end\n      col = col + width\n    end\n  end\n  vim.bo[buf].modifiable = false\n  local changetick = vim.b[buf].changetick\n\n  vim.schedule(function()\n    if not vim.api.nvim_buf_is_valid(buf) then\n      return\n    end\n    if vim.b[buf].changetick ~= changetick then\n      return\n    end\n    require(\"trouble.view.treesitter\").attach(buf, regions)\n  end)\nend\n\nfunction M:trim()\n  while #self._lines > 0 and #self._lines[#self._lines] == 0 do\n    table.remove(self._lines)\n  end\nend\n\nfunction M:row()\n  return #self._lines == 0 and 1 or #self._lines\nend\n\n---@param opts? {display:boolean}\nfunction M:col(opts)\n  if opts and opts.display then\n    local ret = 0\n    for _, segment in ipairs(self._lines[#self._lines] or {}) do\n      ret = ret + vim.fn.strdisplaywidth(segment.str)\n    end\n    return ret\n  end\n  return self._col\nend\n\nreturn M\n"
  },
  {
    "path": "lua/trouble/view/treesitter.lua",
    "content": "---@alias trouble.LangRegions table<string, number[][][]>\n\nlocal M = {}\n\nM.cache = {} ---@type table<number, table<string,{parser: vim.treesitter.LanguageTree, highlighter:vim.treesitter.highlighter, enabled:boolean}>>\nlocal ns = vim.api.nvim_create_namespace(\"trouble.treesitter\")\n\nlocal TSHighlighter = vim.treesitter.highlighter\n\nlocal function wrap(name)\n  return function(_, win, buf, ...)\n    if not M.cache[buf] then\n      return false\n    end\n    for _, hl in pairs(M.cache[buf] or {}) do\n      if hl.enabled then\n        TSHighlighter.active[buf] = hl.highlighter\n        TSHighlighter[name](_, win, buf, ...)\n      end\n    end\n    TSHighlighter.active[buf] = nil\n  end\nend\n\nM.did_setup = false\nfunction M.setup()\n  if M.did_setup then\n    return\n  end\n  M.did_setup = true\n\n  -- https://github.com/neovim/neovim/commit/5edbabdbec0ac3fba33be8afc008845130158583\n  if TSHighlighter._on_range then\n    vim.api.nvim_set_decoration_provider(ns, {\n      on_win = wrap(\"_on_win\"),\n      on_range = wrap(\"_on_range\"),\n    })\n  else\n    vim.api.nvim_set_decoration_provider(ns, {\n      on_win = wrap(\"_on_win\"),\n      on_line = wrap(\"_on_line\"),\n    })\n  end\n\n  vim.api.nvim_create_autocmd(\"BufWipeout\", {\n    group = vim.api.nvim_create_augroup(\"trouble.treesitter.hl\", { clear = true }),\n    callback = function(ev)\n      M.cache[ev.buf] = nil\n    end,\n  })\nend\n\n---@param buf number\n---@param regions trouble.LangRegions\nfunction M.attach(buf, regions)\n  M.setup()\n  M.cache[buf] = M.cache[buf] or {}\n  for lang in pairs(M.cache[buf]) do\n    M.cache[buf][lang].enabled = regions[lang] ~= nil\n  end\n\n  for lang in pairs(regions) do\n    M._attach_lang(buf, lang, regions[lang])\n  end\nend\n\n---@param buf number\n---@param lang? string\nfunction M._attach_lang(buf, lang, regions)\n  lang = lang or \"markdown\"\n  lang = lang == \"markdown\" and \"markdown_inline\" or lang\n\n  M.cache[buf] = M.cache[buf] or {}\n\n  if not M.cache[buf][lang] then\n    local ok, parser = pcall(vim.treesitter.languagetree.new, buf, lang)\n    if not ok then\n      local msg = \"nvim-treesitter parser missing `\" .. lang .. \"`\"\n      vim.notify_once(msg, vim.log.levels.WARN, { title = \"trouble.nvim\" })\n      return\n    end\n\n    parser:set_included_regions(vim.deepcopy(regions))\n    M.cache[buf][lang] = {\n      parser = parser,\n      highlighter = TSHighlighter.new(parser),\n    }\n  end\n  M.cache[buf][lang].enabled = true\n  local parser = M.cache[buf][lang].parser\n\n  parser:set_included_regions(vim.deepcopy(regions))\nend\n\nreturn M\n"
  },
  {
    "path": "lua/trouble/view/window.lua",
    "content": "local Main = require(\"trouble.view.main\")\nlocal Util = require(\"trouble.util\")\n\n---@class trouble.Window.split\n---@field type \"split\"\n---@field relative \"editor\" | \"win\" cursor is only valid for float\n---@field size number | {width:number, height:number} when a table is provided, either the width or height is used based on the position\n---@field position \"top\" | \"bottom\" | \"left\" | \"right\"\n\n---@class trouble.Window.float\n---@field type \"float\"\n---@field relative \"editor\" | \"win\" | \"cursor\" cursor is only valid for float\n---@field size {width: number, height: number}\n---@field position {[1]: number, [2]: number}\n---@field anchor? string\n---@field focusable? boolean\n---@field zindex? integer\n---@field border? any\n---@field title? string|{[1]: string, [2]: string}\n---@field title_pos? string\n---@field footer? string|{[1]: string, [2]: string}\n---@field footer_pos? string\n---@field fixed? boolean\n\n---@class trouble.Window.main\n---@field type \"main\"\n\n---@class trouble.Window.base\n---@field padding? {top?:number, left?:number}\n---@field wo? vim.wo\n---@field bo? vim.bo\n---@field minimal? boolean (defaults to true)\n---@field win? number\n---@field on_mount? fun(self: trouble.Window)\n---@field on_close? fun(self: trouble.Window)\n\n---@alias trouble.Window.opts trouble.Window.base|trouble.Window.split|trouble.Window.float|trouble.Window.main\n\n---@class trouble.Window\n---@field opts trouble.Window.opts\n---@field win? number\n---@field buf number\n---@field id number\n---@field keys table<string, string>\nlocal M = {}\nM.__index = M\n\nlocal _id = 0\nlocal function next_id()\n  _id = _id + 1\n  return _id\nend\n\nlocal split_commands = {\n  editor = {\n    top = \"topleft\",\n    right = \"vertical botright\",\n    bottom = \"botright\",\n    left = \"vertical topleft\",\n  },\n  win = {\n    top = \"aboveleft\",\n    right = \"vertical rightbelow\",\n    bottom = \"belowright\",\n    left = \"vertical leftabove\",\n  },\n}\n\nlocal float_options = {\n  \"anchor\",\n  \"border\",\n  \"bufpos\",\n  \"col\",\n  \"external\",\n  \"fixed\",\n  \"focusable\",\n  \"footer\",\n  \"footer_pos\",\n  \"height\",\n  \"hide\",\n  \"noautocmd\",\n  \"relative\",\n  \"row\",\n  \"style\",\n  \"title\",\n  \"title_pos\",\n  \"width\",\n  \"win\",\n  \"zindex\",\n}\n\n---@type trouble.Window.opts\nlocal defaults = {\n  padding = { top = 0, left = 1 },\n  bo = {\n    bufhidden = \"wipe\",\n    filetype = \"trouble\",\n    buftype = \"nofile\",\n  },\n  wo = {\n    winbar = \"\",\n    winblend = 0,\n  },\n}\n\nM.FOLDS = {\n  wo = {\n    foldcolumn = \"0\",\n    foldenable = false,\n    foldlevel = 99,\n    foldmethod = \"manual\",\n  },\n}\n\n---@type trouble.Window.opts\nlocal minimal = {\n  wo = {\n    cursorcolumn = false,\n    cursorline = true,\n    cursorlineopt = \"both\",\n    fillchars = \"eob: \",\n    list = false,\n    number = false,\n    relativenumber = false,\n    signcolumn = \"no\",\n    spell = false,\n    winbar = \"\",\n    statuscolumn = \"\",\n    winfixheight = true,\n    winfixwidth = true,\n    winhighlight = \"Normal:TroubleNormal,NormalNC:TroubleNormalNC,EndOfBuffer:TroubleNormal\",\n    wrap = false,\n  },\n}\n\n---@param opts? trouble.Window.opts\nfunction M.new(opts)\n  local self = setmetatable({}, M)\n  self.id = next_id()\n  opts = opts or {}\n\n  if opts.minimal == nil then\n    opts.minimal = opts.type ~= \"main\"\n  end\n\n  opts = vim.tbl_deep_extend(\"force\", {}, defaults, opts.minimal and minimal or {}, opts or {})\n  opts.type = opts.type or \"split\"\n  if opts.type == \"split\" then\n    opts.relative = opts.relative or \"editor\"\n    opts.position = opts.position or \"bottom\"\n    opts.size = opts.size or (opts.position == \"bottom\" or opts.position == \"top\") and 10 or 30\n    opts.win = opts.win or vim.api.nvim_get_current_win()\n  elseif opts.type == \"float\" then\n    opts.relative = opts.relative or \"editor\"\n    opts.size = opts.size or { width = 0.8, height = 0.8 }\n    opts.position = type(opts.position) == \"table\" and opts.position or { 0.5, 0.5 }\n  elseif opts.type == \"main\" then\n    opts.type = \"float\"\n    opts.relative = \"win\"\n    opts.position = { 0, 0 }\n    opts.size = { width = 1, height = 1 }\n    opts.wo.winhighlight = \"Normal:Normal\"\n  end\n  self.opts = opts\n  return self\nend\n\n---@param clear? boolean\nfunction M:augroup(clear)\n  return vim.api.nvim_create_augroup(\"trouble.window.\" .. self.id, { clear = clear == true })\nend\n\nfunction M:parent_size()\n  if self.opts.relative == \"editor\" or self.opts.relative == \"cursor\" then\n    return { width = vim.o.columns, height = vim.o.lines }\n  end\n  local ret = {\n    width = vim.api.nvim_win_get_width(self.opts.win),\n    height = vim.api.nvim_win_get_height(self.opts.win),\n  }\n  -- account for winbar\n  if vim.wo[self.opts.win].winbar ~= \"\" then\n    ret.height = ret.height - 1\n  end\n  return ret\nend\n\n---@param type \"win\" | \"buf\"\nfunction M:set_options(type)\n  local opts = type == \"win\" and self.opts.wo or self.opts.bo\n  ---@diagnostic disable-next-line: no-unknown\n  for k, v in pairs(opts or {}) do\n    ---@diagnostic disable-next-line: no-unknown\n    local ok, err = pcall(vim.api.nvim_set_option_value, k, v, type == \"win\" and {\n      scope = \"local\",\n      win = self.win,\n    } or { buf = self.buf })\n    if not ok then\n      Util.error(\"Error setting option `\" .. k .. \"=\" .. v .. \"`\\n\\n\" .. err)\n    end\n  end\nend\n\nfunction M:mount()\n  self.keys = {}\n  self.buf = vim.api.nvim_create_buf(false, true)\n  self:set_options(\"buf\")\n  if self.opts.type == \"split\" then\n    ---@diagnostic disable-next-line: param-type-mismatch\n    self:mount_split(self.opts)\n  else\n    ---@diagnostic disable-next-line: param-type-mismatch\n    self:mount_float(self.opts)\n  end\n\n  self:set_options(\"win\")\n\n  self:on({ \"BufWinLeave\" }, vim.schedule_wrap(self.check_alien))\n\n  self:on(\"WinClosed\", function()\n    if self.opts.on_close then\n      self.opts.on_close(self)\n    end\n    self:augroup(true)\n  end, { win = true })\n\n  if self.opts.on_mount then\n    self.opts.on_mount(self)\n  end\nend\n\nfunction M:set_buf(buf)\n  self.buf = buf\n  if self.win and vim.api.nvim_win_is_valid(self.win) then\n    vim.api.nvim_win_set_buf(self.win, buf)\n  end\nend\n\nfunction M:check_alien()\n  if self.win and vim.api.nvim_win_is_valid(self.win) then\n    local buf = vim.api.nvim_win_get_buf(self.win)\n    if buf ~= self.buf then\n      -- move the alien buffer to another window\n      local main = Main:get()\n      if main then\n        vim.api.nvim_win_set_buf(main.win, buf)\n        -- restore the trouble window\n        self:close()\n        self:open()\n      end\n    end\n  end\nend\n\nfunction M:close()\n  pcall(vim.api.nvim_win_close, self.win, true)\n  self:augroup(true)\n  self.win = nil\nend\n\nfunction M:open()\n  if self:valid() then\n    return self\n  end\n  self:close()\n  self:mount()\n  return self\nend\n\nfunction M:valid()\n  return self.win\n    and vim.api.nvim_win_is_valid(self.win)\n    and self.buf\n    and vim.api.nvim_buf_is_valid(self.buf)\n    and vim.api.nvim_win_get_buf(self.win) == self.buf\nend\n\n---@param opts trouble.Window.split|trouble.Window.base\nfunction M:mount_split(opts)\n  if self.opts.win and not vim.api.nvim_win_is_valid(self.opts.win) then\n    self.opts.win = 0\n  end\n  local parent_size = self:parent_size()\n  local size = opts.size\n  if type(size) == \"table\" then\n    size = opts.position == \"left\" or opts.position == \"right\" and size.width or size.height\n  end\n  if size <= 1 then\n    local vertical = opts.position == \"left\" or opts.position == \"right\"\n    size = math.floor(parent_size[vertical and \"width\" or \"height\"] * size)\n  end\n  local cmd = split_commands[opts.relative][opts.position]\n  Util.noautocmd(function()\n    vim.api.nvim_win_call(opts.win, function()\n      vim.cmd(\"silent noswapfile \" .. cmd .. \" \" .. size .. \"split\")\n      vim.api.nvim_win_set_buf(0, self.buf)\n      self.win = vim.api.nvim_get_current_win()\n    end)\n  end)\nend\n\n---@param opts trouble.Window.float|trouble.Window.base\nfunction M:mount_float(opts)\n  local parent_size = self:parent_size()\n  ---@type vim.api.keyset.win_config\n  local config = {}\n  for _, v in ipairs(float_options) do\n    ---@diagnostic disable-next-line: no-unknown\n    config[v] = opts[v]\n  end\n  config.focusable = true\n  config.height = opts.size.height <= 1 and math.floor(parent_size.height * opts.size.height) or opts.size.height\n  config.width = opts.size.width <= 1 and math.floor(parent_size.width * opts.size.width) or opts.size.width\n  config.border = opts.border or \"none\"\n\n  config.row = math.abs(opts.position[1]) <= 1 and math.floor((parent_size.height - config.height) * opts.position[1])\n    or opts.position[1]\n  config.row = config.row < 0 and (parent_size.height + config.row) or config.row\n\n  config.col = math.abs(opts.position[2]) <= 1 and math.floor((parent_size.width - config.width) * opts.position[2])\n    or opts.position[2]\n  config.col = config.col < 0 and (parent_size.width + config.col) or config.col\n  if config.relative ~= \"win\" then\n    config.win = nil\n  end\n\n  self.win = vim.api.nvim_open_win(self.buf, false, config)\nend\n\nfunction M:focus()\n  if self:valid() then\n    vim.api.nvim_set_current_win(self.win)\n  end\nend\n\n---@param events string|string[]\n---@param fn fun(self:trouble.Window, event:{buf:number}):boolean?\n---@param opts? vim.api.keyset.create_autocmd | {buffer: false, win?:boolean}\nfunction M:on(events, fn, opts)\n  opts = opts or {}\n  if opts.win then\n    opts.pattern = self.win .. \"\"\n    opts.win = nil\n  elseif opts.buffer == nil then\n    opts.buffer = self.buf\n  elseif opts.buffer == false then\n    opts.buffer = nil\n  end\n  if opts.pattern then\n    opts.buffer = nil\n  end\n  local _self = Util.weak(self)\n  opts.callback = function(e)\n    local this = _self()\n    if not this then\n      -- delete the autocmd\n      return true\n    end\n    return fn(this, e)\n  end\n  opts.group = self:augroup()\n  vim.api.nvim_create_autocmd(events, opts)\nend\n\n---@param key string\n---@param fn fun(self: trouble.Window):any\n---@param opts? string|vim.keymap.set.Opts|{mode?:string}\nfunction M:map(key, fn, opts)\n  opts = vim.tbl_deep_extend(\"force\", {\n    buffer = self.buf,\n    nowait = true,\n    mode = \"n\",\n  }, type(opts) == \"string\" and { desc = opts } or opts or {})\n  local mode = opts.mode\n  opts.mode = nil\n  ---@cast opts vim.keymap.set.Opts\n  if not self:valid() then\n    error(\"Cannot create a keymap for an invalid window\")\n  end\n\n  self.keys[key] = opts.desc or key\n  local weak_self = Util.weak(self)\n  vim.keymap.set(mode, key, function()\n    if weak_self() then\n      return fn(weak_self())\n    end\n  end, opts)\nend\n\nreturn M\n"
  },
  {
    "path": "scripts/docs",
    "content": "#!/bin/env bash\n\nnvim -u tests/minit.lua -l lua/trouble/docs.lua\n"
  },
  {
    "path": "scripts/test",
    "content": "#!/usr/bin/env bash\n\nnvim -l tests/minit.lua --minitest \"$@\"\n"
  },
  {
    "path": "selene.toml",
    "content": "std=\"vim\"\n\n[lints]\nmixed_table=\"allow\"\n"
  },
  {
    "path": "stylua.toml",
    "content": "indent_type = \"Spaces\"\nindent_width = 2\ncolumn_width = 120\n[sort_requires]\nenabled = true\n\n"
  },
  {
    "path": "tests/minit.lua",
    "content": "#!/usr/bin/env -S nvim -l\n\nvim.env.LAZY_STDPATH = \".tests\"\nvim.env.LAZY_PATH = vim.fs.normalize(\"~/projects/lazy.nvim\")\n\nif vim.fn.isdirectory(vim.env.LAZY_PATH) == 1 then\n  loadfile(vim.env.LAZY_PATH .. \"/bootstrap.lua\")()\nelse\n  load(vim.fn.system(\"curl -s https://raw.githubusercontent.com/folke/lazy.nvim/main/bootstrap.lua\"), \"bootstrap.lua\")()\nend\n\n-- Setup lazy.nvim\nrequire(\"lazy.minit\").setup({\n  spec = {\n    { dir = vim.uv.cwd(), opts = {} },\n  },\n})\n"
  },
  {
    "path": "tests/parser_spec.lua",
    "content": "local Parser = require(\"trouble.config.parser\")\ndescribe(\"Input is parsed correctly\", function()\n  local tests = {\n    {\n      input = [[a = \"b\" foo = true bar=1 c = \"g\"]],\n      expected = { opts = { a = \"b\", foo = true, bar = 1, c = \"g\" }, errors = {}, args = {} },\n    },\n    {\n      input = [[key=\"value with spaces\"]],\n      expected = { opts = { key = \"value with spaces\" }, errors = {}, args = {} },\n    },\n    {\n      input = [[arr={one, two}]],\n      expected = { opts = { arr = { \"one\", \"two\" } }, errors = {}, args = {} },\n    },\n    {\n      input = [[a.b = \"c\"]],\n      expected = { opts = { a = { b = \"c\" } }, errors = {}, args = {} },\n    },\n    {\n      input = [[vim=vim.diagnostic.severity.ERROR]],\n      expected = { opts = { vim = vim.diagnostic.severity.ERROR }, errors = {}, args = {} },\n    },\n    {\n      input = [[x.y.z = \"value\" a.b = 2]],\n      expected = { opts = { x = { y = { z = \"value\" } }, a = { b = 2 } }, errors = {}, args = {} },\n    },\n    {\n      input = [[test=\"hello world\"]],\n      expected = { opts = { test = \"hello world\" }, errors = {}, args = {} },\n    },\n    {\n      input = [[one.two.three.four = 4]],\n      expected = { opts = { one = { two = { three = { four = 4 } } } }, errors = {}, args = {} },\n    },\n    {\n      input = [[a=\"b\" c=\"d\" e.f=\"g\" h.i.j=\"k\"]],\n      expected = { opts = { a = \"b\", c = \"d\", e = { f = \"g\" }, h = { i = { j = \"k\" } } }, errors = {}, args = {} },\n    },\n    {\n      input = [[empty = \"\" nonempty=\"not empty\"]],\n      expected = { opts = { empty = \"\", nonempty = \"not empty\" }, errors = {}, args = {} },\n    },\n    {\n      input = [[win.position=\"right\" win.relative=\"win\"]],\n      expected = { opts = { win = { position = \"right\", relative = \"win\" } }, errors = {}, args = {} },\n    },\n    {\n      input = [[a.b=\"c\" a = \"b\"]],\n      expected = { opts = { a = \"b\" }, errors = {}, args = {} }, -- This test is tricky as it will overwrite the first value of 'a'\n    },\n    {\n      input = [[a=\"b\" a.b=\"c\"]],\n      expected = { opts = { a = { b = \"c\" } }, errors = {}, args = {} }, -- This test is tricky as it will overwrite the first value of 'a'\n    },\n    {\n      input = [[a_b = 1]],\n      expected = { opts = { a_b = 1 }, errors = {}, args = {} },\n    },\n    {\n      input = \"foo=bar bar=baz\",\n      expected = { opts = { foo = \"bar\", bar = \"baz\" }, errors = {}, args = {} },\n    },\n    {\n      input = \"foo=bar bar={ one, two, three} \",\n      expected = { opts = { foo = \"bar\", bar = { \"one\", \"two\", \"three\" } }, errors = {}, args = {} },\n    },\n    {\n      input = [[a.x = 1 a.y = 2 a = {z  =3}]],\n      expected = { opts = { a = { z = 3 } }, errors = {}, args = {} },\n    },\n    {\n      input = [[ a = {z  =3} a.x = 1 a.y = 2]],\n      expected = { opts = { a = { x = 1, y = 2, z = 3 } }, errors = {}, args = {} },\n    },\n    {\n      input = \"foo\",\n      expected = { opts = {}, errors = {}, args = { \"foo\" } },\n    },\n    { input = \"foo bar\", expected = { opts = {}, errors = {}, args = { \"foo\", \"bar\" } } },\n    {\n      input = \"foo bar baz\",\n      expected = { opts = {}, errors = {}, args = { \"foo\", \"bar\", \"baz\" } },\n    },\n  }\n\n  for _, test in ipairs(tests) do\n    it(\"parses \" .. test.input, function()\n      local actual = Parser.parse(test.input)\n      assert.same(test.expected, actual)\n    end)\n  end\n\n  return tests\nend)\n"
  },
  {
    "path": "tests/spec_spec.lua",
    "content": "local Spec = require(\"trouble.spec\")\n\ndescribe(\"parses specs\", function()\n  it(\"parses a sort spec\", function()\n    local f1 = function() end\n    ---@type ({input:trouble.Sort.spec, output:trouble.Sort})[]\n    local tests = {\n      {\n        input = \"foo\",\n        output = { { field = \"foo\" } },\n      },\n      {\n        input = { \"foo\", \"bar\" },\n        output = { { field = \"foo\" }, { field = \"bar\" } },\n      },\n      {\n        input = { \"foo\", \"-bar\" },\n        output = { { field = \"foo\" }, { field = \"bar\", desc = true } },\n      },\n      { input = f1, output = { { sorter = f1 } } },\n      { input = { buf = 0 }, output = { { filter = { buf = 0 } } } },\n      { input = { { buf = 0 } }, output = { { filter = { buf = 0 } } } },\n      { input = { f1, \"foo\" }, output = { { sorter = f1 }, { field = \"foo\" } } },\n    }\n\n    for _, test in ipairs(tests) do\n      assert.same(test.output, Spec.sort(test.input))\n    end\n  end)\n\n  it(\"parses a group spec\", function()\n    ---@type ({input:trouble.Group.spec, output:trouble.Group})[]\n    local tests = {\n      {\n        input = \"foo\",\n        output = { fields = { \"foo\" }, format = \"{foo}\" },\n      },\n      {\n        input = \"foo\",\n        output = { fields = { \"foo\" }, format = \"{foo}\" },\n      },\n      {\n        input = { \"foo\", \"bar\" },\n        output = { fields = { \"foo\", \"bar\" }, format = \"{foo} {bar}\" },\n      },\n      {\n        input = { \"foo\", \"bar\" },\n        output = { fields = { \"foo\", \"bar\" }, format = \"{foo} {bar}\" },\n      },\n      {\n        input = { \"foo\", \"bar\" },\n        output = { fields = { \"foo\", \"bar\" }, format = \"{foo} {bar}\" },\n      },\n      {\n        input = {\n          \"directory\",\n          format = \"{kind_icon} {symbol.name} {text:Comment} {pos}\",\n        },\n        output = {\n          directory = true,\n          format = \"{kind_icon} {symbol.name} {text:Comment} {pos}\",\n        },\n      },\n    }\n\n    for _, test in ipairs(tests) do\n      assert.same(test.output, Spec.group(test.input))\n    end\n  end)\n\n  it(\"parses a section spec\", function()\n    local tests = {\n      {\n        input = {\n          -- error from all files\n          source = \"diagnostics\",\n          groups = { \"filename\" },\n          filter = {\n            severity = 1,\n          },\n          sort = { \"filename\", \"-pos\" },\n        },\n        output = {\n          events = {},\n          source = \"diagnostics\",\n          groups = { { fields = { \"filename\" }, format = \"{filename}\" } },\n          sort = { { field = \"filename\" }, { field = \"pos\", desc = true } },\n          filter = { severity = 1 },\n          format = \"{filename} {pos}\",\n        },\n      },\n    }\n    for _, test in ipairs(tests) do\n      assert.same(test.output, Spec.section(test.input))\n    end\n  end)\nend)\n"
  },
  {
    "path": "vim.yml",
    "content": "base: lua51\nlua_versions:\n  - luajit\n\nglobals:\n  Snacks:\n    any: true\n  vim:\n    any: true\n  jit:\n    any: true\n  assert:\n    any: true\n  describe:\n    any: true\n  it:\n    any: true\n  before_each:\n    any: true\n"
  }
]