[
  {
    "path": ".busted",
    "content": "return {\n    _all = {\n        coverage = false,\n        lpath = \"lua/?.lua;lua/?/init.lua\",\n        pattern = \"tests%.lua$\",\n        lua = \"nlua\",\n        ROOT = { \"lua/\" },\n    },\n    default = {\n        verbose = true,\n    },\n    tests = {\n        verbose = true,\n    },\n}\n"
  },
  {
    "path": ".envrc",
    "content": "use flake . -Lv\n"
  },
  {
    "path": ".github/CODEOWNERS",
    "content": "# global rule\n* @vhyrro @benlubas\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\nopen_collective: 'neorg'         # Replace with a single Open Collective username\ngithub: [ 'vhyrro', 'danymat' ]  # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]\nko_fi:                           # Replace with a single Ko-fi username\ntidelift:                        # Replace with a single Tidelift platform-name/package-name e.g., npm/babel\ncommunity_bridge:                # Replace with a single Community Bridge project-name e.g., cloud-foundry\nissuehunt:                       # Replace with a single IssueHunt username\notechie:                         # Replace with a single Otechie username\n\ncustom: [ 'https://buymeacoffee.com/vhyrro' ]\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yml",
    "content": "name: Bug Report\ndescription: Report a problem in Neorg\nlabels: [bug]\nbody:\n\n  - type: checkboxes\n    id: faq-prerequisite\n    attributes:\n      label: Prerequisites\n      options:\n        - label: I am using the latest stable release of Neovim\n          required: true\n        - label: I am using the latest version of the plugin\n          required: true\n\n  - type: input\n    attributes:\n      label: \"Neovim Version\"\n      description: \"`nvim --version`:\"\n    validations:\n      required: true\n\n  - type: textarea\n    attributes:\n      label: \"Neorg setup\"\n      description: |\n        - Copy your entire \"require(\"neorg\").setup\" function\n    validations:\n      required: true\n\n  - type: textarea\n    attributes:\n      label: \"Actual behavior\"\n      description: \"A description of actual behavior. Extra points if it includes images or videos.\"\n    validations:\n      required: true\n\n  - type: textarea\n    attributes:\n      label: \"Expected behavior\"\n      description: \"A description of the behavior you expected.\"\n    validations:\n      required: true\n\n  - type: textarea\n    attributes:\n      label: \"Steps to reproduce\"\n      description: \"Please describe how we can reproduce the issue.\"\n    validations:\n      required: true\n\n  - type: textarea\n    attributes:\n      label: \"Potentially conflicting plugins\"\n      description: \"Other plugins you are using which you think could potentially be conflicting with neorg.\"\n\n  - type: textarea\n    attributes:\n      label: \"Other information\"\n      description: \"Other information that could be helpful with debugging.\"\n\n  - type: dropdown\n    id: help\n    attributes:\n      label: \"Help\"\n      description: \"Would you be able to resolve this issue by submitting a pull request?\"\n      options:\n        - \"Yes\"\n        - \"Yes, but I don't know how to start. I would need guidance (check question below)\"\n        - \"No\"\n\n  - type: textarea\n    attributes:\n      label: \"Implementation help\"\n      description: \"If you selected yes in the last question please specify what you would need help with in order to resolve the issue.\"\n\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "contact_links:\n  - name: Ask a question\n    url: https://github.com/vhyrro/neorg/discussions\n    about: If you need help with configuration or something else\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.yml",
    "content": "name: Feature request\ndescription: Request a feature for Neorg\nlabels: [feature]\nbody:\n\n  - type: checkboxes\n    id: issue-prerequisite\n    attributes:\n      label: Issues\n      options:\n        - label: I have checked [existing issues](https://github.com/vhyrro/neorg/issues?q=is%3Aissue) and there are no existing ones with the same request.\n          required: true\n\n  - type: textarea\n    attributes:\n      label: \"Feature description\"\n    validations:\n      required: true\n\n  - type: dropdown\n    id: help\n    attributes:\n      label: \"Help\"\n      description: \"Would you be able to implement this by submitting a pull request?\"\n      options:\n        - \"Yes\"\n        - \"Yes, but I don't know how to start. I would need guidance\"\n        - \"No\"\n    validations:\n      required: true\n\n  - type: textarea\n    attributes:\n      label: \"Implementation help\"\n      description: \"If you selected yes in the last question please specify in detail what you would need help with in order to implement this.\"\n    validations:\n      required: false\n"
  },
  {
    "path": ".github/workflows/checks.yml",
    "content": "name: \"Codebase Checks\"\n\n# This workflow runs various checks (type checking, code checks)\n# to ensure that Neorg is not breaking.\n\non:\n  push:\n  pull_request:\n  workflow_dispatch:\n\njobs:\n  integration-test:\n    runs-on: ubuntu-24.04\n    steps:\n      - uses: actions/checkout@v4\n      - uses: DeterminateSystems/nix-installer-action@v17\n      - name: Run Checks\n        run: nix flake check -L\n"
  },
  {
    "path": ".github/workflows/gendoc.yml",
    "content": "name: Automatically Generate Documentation\n\non:\n  push:\n    branches:\n      - main\n\njobs:\n  generate-documentation:\n    strategy:\n      fail-fast: false\n    name: Generate Markdown Docs\n    runs-on: ubuntu-24.04\n    steps:\n      - uses: actions/checkout@v4\n\n      - name: Install Plenary\n        uses: actions/checkout@v4\n        with:\n          repository: nvim-lua/plenary.nvim\n          path: plenary.nvim\n\n      - name: Install Treesitter\n        uses: actions/checkout@v4\n        with:\n          repository: nvim-treesitter/nvim-treesitter\n          path: nvim-treesitter\n          ref: v0.9.3\n\n      - name: Install Neovim\n        uses: rhysd/action-setup-vim@v1\n        id: neovim\n        with:\n          neovim: true\n          version: v0.10.4\n\n      - uses: luarocks/gh-actions-lua@v10\n        with:\n          luaVersion: \"luajit-2.1.0-beta3\"\n      - uses: luarocks/gh-actions-luarocks@v5\n        with:\n          luarocksVersion: \"3.12.0\"\n\n      - name: Install all required modules\n        run: |\n          luarocks config lua_version 5.1\n          luarocks install lua-utils.nvim 1.0.2\n          luarocks install nui.nvim 0.3.0\n          luarocks install nvim-nio 1.10.1\n          luarocks install pathlib.nvim 2.2.3\n          luarocks install plenary.nvim 0.1.4\n\n\n      - name: Clone Wiki\n        uses: actions/checkout@v4\n        with:\n          repository: \"nvim-neorg/neorg.wiki\"\n          path: wiki/\n\n      - name: Run Documentation Generator\n        run: |\n          if ls wiki/*.md 1> /dev/null 2>&1; then\n            rm wiki/*.md\n          fi\n          make documentation\n\n      - name: Commit Wiki Changes\n        run: |\n          cd wiki/\n          git config --local user.email \"41898282+github-actions[bot]@users.noreply.github.com\"\n          git config --local user.name \"github-actions[bot]\"\n          if [[ ! -z $(git status -s) ]]; then\n            git add .\n            git commit -m \"chore: autoupdate github wiki\"\n          fi\n\n      - name: Push Wiki to Github\n        uses: ad-m/github-push-action@master\n        with:\n          github_token: ${{ secrets.GITHUB_TOKEN }}\n          repository: \"nvim-neorg/neorg.wiki\"\n          branch: master\n          force: true\n          directory: ./wiki\n"
  },
  {
    "path": ".github/workflows/integration_tests.yml",
    "content": "name: \"Nix-based Integration Test\"\n\n# This workflow attempts to install Neorg with a kickstart config on all major OSes.\n# Uses Nix for reproducibility.\n\non:\n  push:\n  pull_request:\n  workflow_dispatch:\n\njobs:\n  integration-test:\n    runs-on: ${{ matrix.os.host }}\n    strategy:\n      matrix:\n        os:\n          - host: ubuntu-latest\n          # - host: windows-2019\n          - host: macos-26-intel # x86_64\n          - host: macos-latest # aarch64\n    steps:\n      - uses: actions/checkout@v4\n      - uses: DeterminateSystems/nix-installer-action@v17\n      - name: Run Checks\n        run: nix run .#integration-test\n"
  },
  {
    "path": ".github/workflows/luarocks.yml",
    "content": "name: Push to Luarocks\n\non:\n  push:\n    branches:\n      - main\n\njobs:\n  luarocks-upload:\n    runs-on: ubuntu-24.04\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          fetch-depth: 0 # Required to count the commits\n      - name: Get Version\n        run: echo \"LUAROCKS_VERSION=$(git describe --abbrev=0 --tags)\" >> $GITHUB_ENV\n      - name: LuaRocks Upload\n        uses: nvim-neorocks/luarocks-tag-release@v7\n        env:\n          LUAROCKS_API_KEY: ${{ secrets.LUAROCKS_API_KEY }}\n        with:\n          version: ${{ env.LUAROCKS_VERSION }}\n          test_interpreters: \"\"\n          dependencies: |\n            nvim-nio ~> 1.7\n            lua-utils.nvim == 1.0.2\n            plenary.nvim == 0.1.4\n            nui.nvim == 0.3.0\n            pathlib.nvim ~> 2.2\n            nvim-treesitter-legacy-api == 0.9.2\n"
  },
  {
    "path": ".github/workflows/semver.yml",
    "content": "name: Release Please Automatic Semver\n\non:\n  push:\n    branches:\n      - main\n\njobs:\n  release:\n    name: release\n    runs-on: ubuntu-24.04\n    steps:\n      - uses: googleapis/release-please-action@v4\n        with:\n          release-type: simple\n          package-name: neorg\n"
  },
  {
    "path": ".github/workflows/sponsors.yml",
    "content": "name: Generate Sponsors README\non:\n  workflow_dispatch:\n  schedule:\n    - cron: 30 15 * * 0-6\njobs:\n  deploy:\n    runs-on: ubuntu-24.04\n    steps:\n      - name: Checkout 🛎️\n        uses: actions/checkout@v4\n\n      - name: Generate Sponsors 💖\n        uses: JamesIves/github-sponsors-readme-action@v1\n        with:\n          token: ${{ secrets.GH_SPONSORS_TOKEN }}\n          file: 'README.md'\n          template: '<a href=\"https://github.com/{{{ login }}}\"><img src=\"https://github.com/{{{ login }}}.png\" width=\"60px\" alt=\"{{{ login }}}\" /></a>&nbsp;&nbsp;&nbsp;'\n\n      - name: Commit to repository\n        uses: stefanzweifel/git-auto-commit-action@v6\n        with:\n          commit_message: \"docs(README): update sponsors list\"\n"
  },
  {
    "path": ".github/workflows/stylua.yml",
    "content": "name: Formatting\n\non:\n  push:\n    branches: [ \"main\" ]\n    paths-ignore:\n      - \".github/**\"\n      - \"**.md\"\n      - \"**.norg\"\n\njobs:\n  format-with-stylua:\n    runs-on: ubuntu-24.04\n    steps:\n      - uses: actions/checkout@v4\n\n      - name: Cache cargo modules\n        id: cache-cargo\n        uses: actions/cache@v4\n        env:\n          cache-name: cache-node-modules\n        with:\n          path: ~/.cargo\n          key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/Cargo.toml') }}\n          restore-keys: |\n            ${{ runner.os }}-build-${{ env.cache-name }}-\n            ${{ runner.os }}-build-\n            ${{ runner.os }}-\n\n      - name: Install cargo\n        run: curl https://sh.rustup.rs -sSf | sh -s -- -y\n\n      - name: Install stylua\n        run: cargo install stylua --features lua52\n\n      - name: Run formatting\n        run: stylua -v --verify .\n\n      - uses: stefanzweifel/git-auto-commit-action@v6\n        with:\n          commit_message: \"chore: autoformat with stylua\"\n          branch: ${{ github.ref }}\n\n      - name: Push changes\n        uses: ad-m/github-push-action@master\n        with:\n          github_token: ${{ secrets.GITHUB_TOKEN }}\n          branch: ${{ github.ref }}\n"
  },
  {
    "path": ".github/workflows/tests.yml",
    "content": "name: Run tests\non:\n  pull_request: ~\n  push:\n    branches:\n      - main\n\njobs:\n  build:\n    name: Run tests\n    runs-on: ubuntu-24.04\n\n    steps:\n      - uses: actions/checkout@v4\n      - name: Run tests\n        uses: nvim-neorocks/nvim-busted-action@v1\n        with:\n          nvim_version: stable\n"
  },
  {
    "path": ".github/workflows/version_in_code.yml",
    "content": "name: Update Version String in Neorg Code\n\non:\n  workflow_run:\n    workflows: [\"Release Please Automatic Semver\"]\n    branches: [main]\n    types:\n      - completed\n\njobs:\n  release:\n    name: release\n    runs-on: ubuntu-24.04\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          fetch-depth: 0\n\n      - name: Update Version String\n        run: |\n          latest_tag=$(git describe --abbrev=0 --tags)\n\n          echo \"Updating Neorg Version to $latest_tag\"\n          sed -ri \"s/\\sversion\\s*=\\s*\\\"[0-9]+\\.[0-9]+\\.[0-9]+\\\",$/ version = \\\"${latest_tag:1}\\\",/\" lua/neorg/core/config.lua\n\n      - name: Commit Changes\n        run: |\n          git config --local user.email \"41898282+github-actions[bot]@users.noreply.github.com\"\n          git config --local user.name \"github-actions[bot]\"\n          if [[ ! -z $(git status -s) ]]; then\n            git add .\n            git commit -m \"chore(config.lua): update version variable\"\n          fi\n\n      - name: Push to Github\n        uses: ad-m/github-push-action@master\n        with:\n          github_token: ${{ secrets.GITHUB_TOKEN }}\n          repository: \"nvim-neorg/neorg\"\n          branch: main\n"
  },
  {
    "path": ".gitignore",
    "content": "/wiki/\n/luarocks\n/lua_modules\n/.luarocks\n/.luarc.json\ntest.norg\n\n# direnv\n.direnv\n\n# pre-commit\n.pre-commit-config.yaml\n"
  },
  {
    "path": ".luacheckrc",
    "content": "-- Global objects\nglobals = {\n  \"_\",\n  \"vim\",\n  \"neorg\",\n  \"log\",\n  \"_neorgcmd_generate_completions\",\n  \"_neorg_indent_expr\",\n  \"_neorg_module_autocommand_triggered\"\n}\n\nstd = \"max+busted\"\n\nignore = {\n  \"631\",  -- max_line_length\n}\n\n-- Don't report unused self arguments of methods.\nself = false\n"
  },
  {
    "path": ".styluaignore",
    "content": "ldoc/\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\n\n## [9.4.0](https://github.com/nvim-neorg/neorg/compare/v9.3.0...v9.4.0) (2025-12-03)\n\n\n### Features\n\n* export ranged table tag correctly in core.export.markdown ([#1697](https://github.com/nvim-neorg/neorg/issues/1697)) ([233fe1d](https://github.com/nvim-neorg/neorg/commit/233fe1d85754c6cf59245257354afc9a2429911e))\n* foldable lists and ranged verbatim ([#1690](https://github.com/nvim-neorg/neorg/issues/1690)) ([431de0a](https://github.com/nvim-neorg/neorg/commit/431de0a233ce5ab86da4b4e03786d6fe568daf62))\n* **todo_items:** configurable parent update behavior ([91db472](https://github.com/nvim-neorg/neorg/commit/91db472c7f65a1a8f7fdc461e0fb040f565a95c4))\n* winfixwidth for toc ([1dd99ed](https://github.com/nvim-neorg/neorg/commit/1dd99edd73c48196f7fa9ec763089fb774cf5499))\n\n\n### Bug Fixes\n\n* **ci:** typo in gendoc workflow ([9d75ae8](https://github.com/nvim-neorg/neorg/commit/9d75ae8e2625db240636870dd7f029c9a82ab9ef))\n* **ci:** update actions version ([7cac123](https://github.com/nvim-neorg/neorg/commit/7cac12395b06f549b434e9f829ff6389da598e0c))\n* **ci:** update Ubuntu runner version ([f720f55](https://github.com/nvim-neorg/neorg/commit/f720f55ba2057956f585f3c19e5dedd639982d53))\n* **ci:** use `macos-13` to run integration tests on Intel macOS, `macos-11` has been deprecated a long time ago ([a849204](https://github.com/nvim-neorg/neorg/commit/a8492049c2cfb9fcabe511eb2a883a602777acb0))\n* **clipboard:** yank to use correct regtype instead of always 'line-wise' ([de0bf99](https://github.com/nvim-neorg/neorg/commit/de0bf99a95f4053862f0e63c8083b5e14f3ad6ec))\n* compatibility with nvim-treesitter main branch ([87242d4](https://github.com/nvim-neorg/neorg/commit/87242d458d7d0bb50532b791892fb1251a4e7293))\n* **esupports.indent:** keep indentation of verbatim blocks without an injected TS parser ([#1685](https://github.com/nvim-neorg/neorg/issues/1685)) ([aa35325](https://github.com/nvim-neorg/neorg/commit/aa353253aeacf01464a8214373b64ec500571a45))\n* **journal:** journal index generation ([#1680](https://github.com/nvim-neorg/neorg/issues/1680)) ([35da593](https://github.com/nvim-neorg/neorg/commit/35da593c55d78086a3203ee3e6d749fafe2e4e73))\n* load buffer before parsing with TS ([#1689](https://github.com/nvim-neorg/neorg/issues/1689)) ([8fdd9b2](https://github.com/nvim-neorg/neorg/commit/8fdd9b2986acfb4ce310bebfc338111793862f00))\n* replace deprecated `vim.tbl_islist` with `vim.islist` ([#1723](https://github.com/nvim-neorg/neorg/issues/1723)) ([1ebab96](https://github.com/nvim-neorg/neorg/commit/1ebab969b45520b680516ffffced1b7719355ed8))\n* **rockspec:** add `nvim-treesitter-legacy-api` dependency ([208f7ca](https://github.com/nvim-neorg/neorg/commit/208f7ca7f6a9fa97fa049ae17cc1046ae509811f))\n* **todo_items:** update parent node when child state is changed ([4b8e64a](https://github.com/nvim-neorg/neorg/commit/4b8e64a1dd919c934a200f28873eb5be84c5d9fa))\n* **todo-instrospector:** use combine hl_mode ([#1733](https://github.com/nvim-neorg/neorg/issues/1733)) ([c429af2](https://github.com/nvim-neorg/neorg/commit/c429af26352fc5194b24a4aa88fc5ecc406e23f9))\n* ts module bug ([1f40253](https://github.com/nvim-neorg/neorg/commit/1f40253cda954f71a3c2a8f5a6f428a950e36cc8))\n\n## [9.3.0](https://github.com/nvim-neorg/neorg/compare/v9.2.0...v9.3.0) (2025-03-29)\n\n\n### Features\n\n* **todo_items:** todo-changed event ([#1651](https://github.com/nvim-neorg/neorg/issues/1651)) ([5f0195d](https://github.com/nvim-neorg/neorg/commit/5f0195d99e21fc1db3d544f7473b34bf41740c16))\n\n\n### Bug Fixes\n\n* **docgen:** load buffers after opening them ([#1657](https://github.com/nvim-neorg/neorg/issues/1657)) ([18d1a18](https://github.com/nvim-neorg/neorg/commit/18d1a182720fba71ea286d9ce6c3e9970bf7ed51))\n* expand tangle paths ([#1622](https://github.com/nvim-neorg/neorg/issues/1622)) ([10bf607](https://github.com/nvim-neorg/neorg/commit/10bf607f11ed94151fb5496e6127d8823d162a7e))\n* iter_matches returns list of nodes now ([#1674](https://github.com/nvim-neorg/neorg/issues/1674)) ([a48166d](https://github.com/nvim-neorg/neorg/commit/a48166d3f5729d424ad39254351288944994f097))\n* pin to 0.10.4 for doc gen ([#1676](https://github.com/nvim-neorg/neorg/issues/1676)) ([79ffd34](https://github.com/nvim-neorg/neorg/commit/79ffd346ca19af49197d9c1b45d0b902c32c3e14))\n* Tangle should close fd for files ([#1618](https://github.com/nvim-neorg/neorg/issues/1618)) ([cd3056e](https://github.com/nvim-neorg/neorg/commit/cd3056eaf39a838b0a26438ff203bb3123c94aec))\n* **tangle:** create missing dirs ([#1644](https://github.com/nvim-neorg/neorg/issues/1644)) ([e1b4bb4](https://github.com/nvim-neorg/neorg/commit/e1b4bb4ff144a953f802f665afdd9a7f7532014f))\n* **tangle:** proper error handling for fs_close ([#1647](https://github.com/nvim-neorg/neorg/issues/1647)) ([1a4c20a](https://github.com/nvim-neorg/neorg/commit/1a4c20a5ed3dea4abae20e85d66b5f0f94c96f5b))\n* tree-sitter-norg parser builds on MacOS ([#1587](https://github.com/nvim-neorg/neorg/issues/1587)) ([71cd029](https://github.com/nvim-neorg/neorg/commit/71cd0291a80be9b5258f5ec16fd6846d60623c29))\n\n## [9.2.0](https://github.com/nvim-neorg/neorg/compare/v9.1.1...v9.2.0) (2025-01-22)\n\n\n### Features\n\n* automatically typed `module.required` dependencies ([#1537](https://github.com/nvim-neorg/neorg/issues/1537)) ([1985f2d](https://github.com/nvim-neorg/neorg/commit/1985f2d6f152622b0066f48ba8e39d157635dd38))\n* **dirman:** dynamically set default workspace ([#1623](https://github.com/nvim-neorg/neorg/issues/1623)) ([29993a7](https://github.com/nvim-neorg/neorg/commit/29993a7bb8279ffa0ba473a3f393daa28c645825))\n* **dirman:** in_workspace function ([#1615](https://github.com/nvim-neorg/neorg/issues/1615)) ([62671a7](https://github.com/nvim-neorg/neorg/commit/62671a7b03a1f38a6e5e03b006a9b6f8d804be0e))\n* **esupports.hop:** Add tab drop as option for open_mode ([#1580](https://github.com/nvim-neorg/neorg/issues/1580)) ([c7ada78](https://github.com/nvim-neorg/neorg/commit/c7ada7881d7076a235b6323edcd81ae260affb41))\n* **export:** copy to clipboard ([#1627](https://github.com/nvim-neorg/neorg/issues/1627)) ([1783928](https://github.com/nvim-neorg/neorg/commit/178392822c8c2ef0911458b7f43c980667784011))\n* **introspector:** implement introspector customizability and improvements ([#1539](https://github.com/nvim-neorg/neorg/issues/1539)) ([fd11950](https://github.com/nvim-neorg/neorg/commit/fd11950048d111b837b9f615c5d023e79bd1af9a))\n* **keybinds:** add back keybind \"gO\" for ToC ([#1633](https://github.com/nvim-neorg/neorg/issues/1633)) ([bed58f8](https://github.com/nvim-neorg/neorg/commit/bed58f884ecf9b0b5e855cebe26760bfadbc8f38))\n* make journal methods public ([8b59db7](https://github.com/nvim-neorg/neorg/commit/8b59db79307037032c5f83fc79dff5741d6da869))\n* support ranges in Neorg command, ([c04bd96](https://github.com/nvim-neorg/neorg/commit/c04bd96eeb3bed19f23d394bebb6193bcd5271da))\n\n\n### Bug Fixes\n\n* coq_nvim completion integration ([#1597](https://github.com/nvim-neorg/neorg/issues/1597)) ([488507b](https://github.com/nvim-neorg/neorg/commit/488507bb996f75ee29073f50dec32fa220867ca5))\n* don't suggest leading chars for file completions ([ba35900](https://github.com/nvim-neorg/neorg/commit/ba35900b21921c439e676b063a79c8fad914eac9))\n* intuitive default undone icon ([#1578](https://github.com/nvim-neorg/neorg/issues/1578)) ([13d1d54](https://github.com/nvim-neorg/neorg/commit/13d1d546684c83ba464adbf463a8a272c884e1e8))\n* Neorg return to most recent file ([e5e797e](https://github.com/nvim-neorg/neorg/commit/e5e797e6eddcb6efb1d2c3fc2612b31ad9a76cef))\n* **summary:** unpack summary category list ([#1637](https://github.com/nvim-neorg/neorg/issues/1637)) ([41aa380](https://github.com/nvim-neorg/neorg/commit/41aa3800cf5d30a5f90520c2a31b34727b443219))\n* **toc:** only capture first detached modifier ([#1631](https://github.com/nvim-neorg/neorg/issues/1631)) ([399832e](https://github.com/nvim-neorg/neorg/commit/399832e5437de0cea5efb1d5428de03adc42cc79))\n* **ToC:** only try to open when still in norg buffer ([#1549](https://github.com/nvim-neorg/neorg/issues/1549)) ([88dbab5](https://github.com/nvim-neorg/neorg/commit/88dbab5325ce07092ab7a38b160bc8e988830524))\n* weird tangle file path logic ([#1604](https://github.com/nvim-neorg/neorg/issues/1604)) ([993f077](https://github.com/nvim-neorg/neorg/commit/993f077f0bff8faa68dbdb89ad95f67116b8007a))\n\n## [9.1.1](https://github.com/nvim-neorg/neorg/compare/v9.1.0...v9.1.1) (2024-07-24)\n\n\n### Bug Fixes\n\n* non-functional toggling of sublists ([#1492](https://github.com/nvim-neorg/neorg/issues/1492)) ([169495c](https://github.com/nvim-neorg/neorg/commit/169495ca3ed67f919589499d20fa89bfea9e4de4))\n\n## [9.1.0](https://github.com/nvim-neorg/neorg/compare/v9.0.3...v9.1.0) (2024-07-23)\n\n\n### Features\n\n* add `&lt;LocalLeader&gt;cm` keybind for code block magnification ([c52c00f](https://github.com/nvim-neorg/neorg/commit/c52c00f72c85d3ca258de623e40e64c3e552185a))\n* **concealer:** properly display blockquotes, allow entities to set their own clear functions ([28bbefc](https://github.com/nvim-neorg/neorg/commit/28bbefcfce10a51d7d834c6b6c0f7229bc6b38e1))\n\n\n### Bug Fixes\n\n* **`:h neorg`:** remove `|example` blocks until parser is fixed ([a93190d](https://github.com/nvim-neorg/neorg/commit/a93190db5b804c7db7d2f8f4d1c7edf6414cbe39))\n* `insert-date` not working in insert mode ([7af14b3](https://github.com/nvim-neorg/neorg/commit/7af14b30b6ce9321e5a399eb6eb491ad111b0cda))\n* **concealer:** don't overflow folds into other, non-norg files ([19e4bea](https://github.com/nvim-neorg/neorg/commit/19e4beabdcd6080ececbd5ffcb7ecb50b1590461))\n* **concealer:** properly render quotes ([08277bb](https://github.com/nvim-neorg/neorg/commit/08277bb5cf1b310d2527adcb0d613a8625c7b3d3))\n* **concealer:** quote and list rendering bugs ([e292cf5](https://github.com/nvim-neorg/neorg/commit/e292cf5f3d19a8066e13928ff00efece10915124))\n\n## [9.0.3](https://github.com/nvim-neorg/neorg/compare/v9.0.2...v9.0.3) (2024-07-19)\n\n\n### Bug Fixes\n\n* **keybinds:** fixed tempus insert date insert mode default keybind command ([87e55f3](https://github.com/nvim-neorg/neorg/commit/87e55f3727ad84ecc261f7236892f4dbec82efc1))\n\n## [9.0.2](https://github.com/nvim-neorg/neorg/compare/v9.0.1...v9.0.2) (2024-07-17)\n\n\n### Bug Fixes\n\n* **keybinds:** correctly get keymap opts ([4a2f5b2](https://github.com/nvim-neorg/neorg/commit/4a2f5b2104169883131646f27ffaeb6af324b05a))\n\n## [9.0.1](https://github.com/nvim-neorg/neorg/compare/v9.0.0...v9.0.1) (2024-07-16)\n\n\n### Bug Fixes\n\n* **metagen:** proper iteration on metadata fields ([#1518](https://github.com/nvim-neorg/neorg/issues/1518)) ([0306887](https://github.com/nvim-neorg/neorg/commit/0306887c0c6ea302e3a3fecbb36998793f4c27d5))\n\n## [9.0.0](https://github.com/nvim-neorg/neorg/compare/v8.9.0...v9.0.0) (2024-07-16)\n\n\n### ⚠ BREAKING CHANGES\n\n* tangle files relative to norg file ([#1415](https://github.com/nvim-neorg/neorg/issues/1415))\n* remove `traverse-link` and `traverse-heading` modes\n* remove all references to `core.mode`\n* remove `core.mode`\n* move rest of modules to new keybind system\n* move core.pivot to new keybind system\n* migrate core.promo to new keybind system\n* move esupports.hop to the new keybind schema\n* move qol.todo_items to new keybind API\n* keybind refactor, update core.itero to new changes\n* make Neovim 0.10 a requirement for Neorg\n* simplify code in preparation of lazy.nvim luarocks support\n* remove `ftdetect` file as Neorg is now natively recognized by Neovim ([#1083](https://github.com/nvim-neorg/neorg/issues/1083))\n\n### Features\n\n* add `core.ui.calendar` to the default list of modules ([52a37e0](https://github.com/nvim-neorg/neorg/commit/52a37e01abe059c6431f744eab9f22626fb275c5))\n* add descriptions to all Neorg keybinds ([a042515](https://github.com/nvim-neorg/neorg/commit/a042515bc832ecab6d47a18ffc2976ee72f4bc1b))\n* add dotrepeat to all common commands ([6b49397](https://github.com/nvim-neorg/neorg/commit/6b49397f1e8bea2f19064012e392cd4b36e00d18))\n* auto tangle ([#1413](https://github.com/nvim-neorg/neorg/issues/1413)) ([0f24e4a](https://github.com/nvim-neorg/neorg/commit/0f24e4a53b05328a0ae6496be9867ea5df7b7f40))\n* **calendar:** unify exit keybind to always be `q` instead of a mix of `q`/`&lt;Esc&gt;` ([582d7b6](https://github.com/nvim-neorg/neorg/commit/582d7b616d9bdb5e2bbcba4ebd0e82f7fb9130e0))\n* **completion:** allow custom completion engines ([04d201d](https://github.com/nvim-neorg/neorg/commit/04d201d56857073efecf79a8be29fae45b57ebeb))\n* **docgen:** add default keybinds list ([d762f6d](https://github.com/nvim-neorg/neorg/commit/d762f6dd4cfc7f8337272582abf0459b4c85fe3b))\n* **docgen:** add more links to sidebar ([5bef42a](https://github.com/nvim-neorg/neorg/commit/5bef42ab385e0d2da9e68a60e4ba484c583b9aa7))\n* **health:** check for keybind clashes in checkhealth ([bbe4243](https://github.com/nvim-neorg/neorg/commit/bbe42438a90afd25a5d52b843ebbcc19d8476cef))\n* **intergrations.otter:** update to use otter 2.0 ([1347eeb](https://github.com/nvim-neorg/neorg/commit/1347eebc8a0116524f17a7c33240ae782efb974e))\n* keybind refactor, update core.itero to new changes ([3dd946a](https://github.com/nvim-neorg/neorg/commit/3dd946ae976ee45147a60eeb5174f0f951f04f94))\n* **keybinds:** add `extend_preset` function ([4f09926](https://github.com/nvim-neorg/neorg/commit/4f0992643b42d544a442f6e1928bd5838e355bcd))\n* **keybinds:** don't try to forcefully override user keys ([591b883](https://github.com/nvim-neorg/neorg/commit/591b8831587895b95cbce30ad5a30c53f01b882c))\n* **keybinds:** split presets into norg and non-norg, move to buffer-local mappings instead ([113c21b](https://github.com/nvim-neorg/neorg/commit/113c21b2de4f68c150a5778ff754cdbbec04758f))\n* support multi-line values in metagen ([#1514](https://github.com/nvim-neorg/neorg/issues/1514)) ([321c435](https://github.com/nvim-neorg/neorg/commit/321c435e96a738a32ba2376f7f8f27b401759236))\n\n\n### Bug Fixes\n\n* better formatting, properly handle complex keybind descriptions ([c087db0](https://github.com/nvim-neorg/neorg/commit/c087db0473b3d8363e31135ef42d1290994075e6))\n* calendar not working with the latest breakages ([c659b09](https://github.com/nvim-neorg/neorg/commit/c659b0901bea4143667489ee2af4c78762fabc5c))\n* **docgen:** beautify keybind output in &lt;details&gt; tag ([7a9d54c](https://github.com/nvim-neorg/neorg/commit/7a9d54c3c81bb1c403b3591cbc3b0cf27949fc6b))\n* **docgen:** better mnemonic rendering ([b3bf963](https://github.com/nvim-neorg/neorg/commit/b3bf9639d2ebc905f7a99197844bf6da0547a2c9))\n* **docgen:** broken wiki ([e23d0d3](https://github.com/nvim-neorg/neorg/commit/e23d0d32ea11d13c9da67b841a809b6cfda02887))\n* **docgen:** display keybind data in the form of a dropdown ([572de72](https://github.com/nvim-neorg/neorg/commit/572de724159fcf929f3feb125da72b25ccad7bd2))\n* **docgen:** invalid github markdown ([75edcdc](https://github.com/nvim-neorg/neorg/commit/75edcdc68ba9ce4aae5f0df6543f9818c55c5206))\n* error in hasmapto() ([dfcc78a](https://github.com/nvim-neorg/neorg/commit/dfcc78a110051aaedef8c19b48fda486960e1089))\n* feed keys without remaps ([ecf5f41](https://github.com/nvim-neorg/neorg/commit/ecf5f415c96cf7a12b74a8233b8f6d4ecc3779c4))\n* improve startup time by removing pcall on every module load ([7d9bd33](https://github.com/nvim-neorg/neorg/commit/7d9bd33a176fa86c65030776eb9b45cdb729250b))\n* itero keybind not functioning, add fallback functions to core.keybinds ([cd950aa](https://github.com/nvim-neorg/neorg/commit/cd950aa89ae2125882b235b8e79afde13c90e1b1))\n* make core.promo keybinds functional again ([8a48172](https://github.com/nvim-neorg/neorg/commit/8a48172e94854d364b3cb3ecd2940cbe84b2f7bd))\n* properly error when failing to load module using `:Neorg module load` ([721fd28](https://github.com/nvim-neorg/neorg/commit/721fd28f39ba2cb0978e410bd9a7668f8c74ccca))\n* remove all references to `core.mode` ([53429c4](https://github.com/nvim-neorg/neorg/commit/53429c497bda64671c7161b3f59d4640415bf145))\n* set global keybinds only once, set keys on filetype ([e00042a](https://github.com/nvim-neorg/neorg/commit/e00042af322802f4db38706c5eeee8e77145fe65))\n* set keybinds for the appropriate buffer (fixes telescope issues) ([b49c214](https://github.com/nvim-neorg/neorg/commit/b49c214f72ba33d5d76a63f7d70da43c840dc1e2))\n* tangle files relative to norg file ([#1415](https://github.com/nvim-neorg/neorg/issues/1415)) ([3c3b977](https://github.com/nvim-neorg/neorg/commit/3c3b977dff916aecf5b2d63747896691c70639df))\n\n\n### Code Refactoring\n\n* make Neovim 0.10 a requirement for Neorg ([c916501](https://github.com/nvim-neorg/neorg/commit/c91650128130f05c79a2cf1e981a8d87b1f91113))\n* migrate core.promo to new keybind system ([faad665](https://github.com/nvim-neorg/neorg/commit/faad665a8e9c32f9dceae613e7c4c2abdbda9585))\n* move core.pivot to new keybind system ([0c1222b](https://github.com/nvim-neorg/neorg/commit/0c1222b4aa4faf21a76158fe8de1339700442e08))\n* move esupports.hop to the new keybind schema ([cdfbe19](https://github.com/nvim-neorg/neorg/commit/cdfbe19125a5d71288ea5c28f7916f709ca57ddd))\n* move qol.todo_items to new keybind API ([b71d8ba](https://github.com/nvim-neorg/neorg/commit/b71d8ba34c53a0a4d022cd72af90513029800b27))\n* move rest of modules to new keybind system ([94b860b](https://github.com/nvim-neorg/neorg/commit/94b860b704bceb1180eb82443064e6530e001fae))\n* remove `core.mode` ([49e8710](https://github.com/nvim-neorg/neorg/commit/49e8710b3c09b19d69fcce322769fcbbdc4e6f30))\n* remove `ftdetect` file as Neorg is now natively recognized by Neovim ([#1083](https://github.com/nvim-neorg/neorg/issues/1083)) ([5c32056](https://github.com/nvim-neorg/neorg/commit/5c320566757d334ab255a287da960d961d7a9012))\n* remove `traverse-link` and `traverse-heading` modes ([da3e062](https://github.com/nvim-neorg/neorg/commit/da3e0621e03ad33f62cdd2fa77ba02ffb1b52d2b))\n* simplify code in preparation of lazy.nvim luarocks support ([12b7cf2](https://github.com/nvim-neorg/neorg/commit/12b7cf253e60f6ce8552e4498a1598c8b57acf66))\n\n## [8.9.0](https://github.com/nvim-neorg/neorg/compare/v8.8.1...v8.9.0) (2024-07-07)\n\n\n### Features\n\n* **calendar-ui:** new mappings ([2a4081f](https://github.com/nvim-neorg/neorg/commit/2a4081fe89f8f264c672eff2ab88b79f91aa6898))\n* **calendar-ui:** support count in keymappings ([6b4751c](https://github.com/nvim-neorg/neorg/commit/6b4751c2c486578c8a11dfd2f79dfd35cacaa5b8))\n* **calendar:** even more keybinds + ; repeat ([a5c2624](https://github.com/nvim-neorg/neorg/commit/a5c2624bc41cb760c4689734b2c3c5a9f17e4c48))\n\n\n### Bug Fixes\n\n* **promo:** promote/demote range + keybinds ([a94d1e6](https://github.com/nvim-neorg/neorg/commit/a94d1e67f7df3a97c0f57894c107b70a54523a4d))\n* **return:** return causing early exit when paired with auto_toc.exit_nvim ([959b8a2](https://github.com/nvim-neorg/neorg/commit/959b8a20114c63097261cd1b48f2dacfbe4ccd5f))\n* **summary:** ordering and indentation of nested entries ([9279672](https://github.com/nvim-neorg/neorg/commit/9279672d2b97929fc52d1b182af754497dfd8d8a))\n* **text-objects:** node selection inconsistencies ([99b3249](https://github.com/nvim-neorg/neorg/commit/99b32492b836b64a5ff4544d4c466496f0aec9bd))\n* **toc:** various fixes ([#1500](https://github.com/nvim-neorg/neorg/issues/1500)) ([83637f1](https://github.com/nvim-neorg/neorg/commit/83637f11295aaaa6db5b0a28d6db8ac727592759))\n\n## [8.8.1](https://github.com/nvim-neorg/neorg/compare/v8.8.0...v8.8.1) (2024-06-26)\n\n\n### Bug Fixes\n\n* remove `tree-sitter-norg` from the luarocks dependency list momentarily (delegate work to nvim-treesitter for the moment) ([4103d43](https://github.com/nvim-neorg/neorg/commit/4103d43898f0a612f1b702c5a6d2ef1e7fb76058))\n\n## [8.8.0](https://github.com/nvim-neorg/neorg/compare/v8.7.1...v8.8.0) (2024-06-25)\n\n\n### Features\n\n* **latex-renderer:** add toggle renderer command ([a00628f](https://github.com/nvim-neorg/neorg/commit/a00628f54f895774dde09e4d7a3c99eb8340cbb6))\n* **toc:** auto open/enter ToC ([988c2c1](https://github.com/nvim-neorg/neorg/commit/988c2c197c9f431d338519a3b81825cc5690b4e4))\n* **toc:** close toc when neorg win is closed ([78e1155](https://github.com/nvim-neorg/neorg/commit/78e1155b8c3cc3f63e98817b7eea85f84ca7f6af))\n* **toc:** configurable max width ([b0544ed](https://github.com/nvim-neorg/neorg/commit/b0544ed42c3aa28ceb8edf54a3a71c777de91e5d))\n* **toc:** exit nvim when toc is the last window ([baf9934](https://github.com/nvim-neorg/neorg/commit/baf9934832866d28762b59c7e52c82854366d7dd))\n\n\n### Bug Fixes\n\n* broken true-zen integration ([#1477](https://github.com/nvim-neorg/neorg/issues/1477)) ([ace1cda](https://github.com/nvim-neorg/neorg/commit/ace1cdae9d0a95083bf9bf8cfb5e70dbb38d6351))\n* don't update metadata unless buffer is modified ([#1469](https://github.com/nvim-neorg/neorg/issues/1469)) ([286d94b](https://github.com/nvim-neorg/neorg/commit/286d94bf6e30be4a5fc7ce89aa860538d39189a0))\n* **hop:** describe which links are currently unsupported instead of \"not found\" ([9626def](https://github.com/nvim-neorg/neorg/commit/9626def65687a052b0e2c390e9ee9ee599344415))\n* insert template at the end of a new journal entry ([#1468](https://github.com/nvim-neorg/neorg/issues/1468)) ([f33a491](https://github.com/nvim-neorg/neorg/commit/f33a4919b3aa2744209d8106886879e5522a38a4))\n* **latex-renderer:** fixed renderer breaking when a snippet change made it stop compiling ([7fc91bb](https://github.com/nvim-neorg/neorg/commit/7fc91bbc7c316e0ad33132fb61325adc71d4f260))\n* **toc:** don't try to open a toc from a toc ([6cdd6a1](https://github.com/nvim-neorg/neorg/commit/6cdd6a121c113d15c2aa55d79b6ec9915cc47284))\n* **toc:** open new win from toc when neorg win was closed ([c2d58da](https://github.com/nvim-neorg/neorg/commit/c2d58da7c4800e9b8cb5b5ed1a6f38cde1a176d7))\n* **todo-introspector:** check that the buffer is valid ([369ed28](https://github.com/nvim-neorg/neorg/commit/369ed28b0b0c3f221b46816ad53a509d73c7e7ed))\n\n## [8.7.1](https://github.com/nvim-neorg/neorg/compare/v8.7.0...v8.7.1) (2024-06-12)\n\n\n### Bug Fixes\n\n* **latex-renderer:** fixed renderer failing silently ([4b22a4e](https://github.com/nvim-neorg/neorg/commit/4b22a4ea798efeeb756a6df52baa369715832a5a))\n\n## [8.7.0](https://github.com/nvim-neorg/neorg/compare/v8.6.0...v8.7.0) (2024-06-11)\n\n\n### Features\n\n* anchor completion ([9917f99](https://github.com/nvim-neorg/neorg/commit/9917f993c505c3ab7d122e253a3af7cf13820fd0))\n* **completion:** additional detached modifier extensions ([6b02bf1](https://github.com/nvim-neorg/neorg/commit/6b02bf156d4cf1ded9b2bb93cb4669a00e6a1e7f))\n* link name completions ([8ec38e0](https://github.com/nvim-neorg/neorg/commit/8ec38e07ddffa84d0925faf425d4d52e5c1f91b7))\n* **treesitter:** add get_ts_parser ([5221820](https://github.com/nvim-neorg/neorg/commit/5221820166a9cfac67451581ea79a9e2e7680215))\n* **treesitter:** get_document_root accepts more sources ([4ebb7c7](https://github.com/nvim-neorg/neorg/commit/4ebb7c7bd62d12b77f0a8a0ec27e2e64ef204d65))\n\n\n### Bug Fixes\n\n* **concealer:** invalidate extmarks when range is deleted ([51be069](https://github.com/nvim-neorg/neorg/commit/51be06957fc6cc7140b310abd30be6682041962a))\n* foreign link completions with item ([8537710](https://github.com/nvim-neorg/neorg/commit/85377108531d4083c0526623023a35aab2509410))\n* supress swap file errors ([4420ddc](https://github.com/nvim-neorg/neorg/commit/4420ddc26ab80b42d4735ec78efea39c7cc7f547))\n* thoroughly test for current Neovim version ([352570c](https://github.com/nvim-neorg/neorg/commit/352570cb174c73d50a82376f06b05badb1cda338))\n* **todo-items:** error when switching from on-hold item with children ([f03435f](https://github.com/nvim-neorg/neorg/commit/f03435f03b5649598fb1478429d4e985dc5789bb))\n\n## [8.6.0](https://github.com/nvim-neorg/neorg/compare/v8.5.0...v8.6.0) (2024-05-31)\n\n\n### Features\n\n* add `integrations.coq_nvim` as a new completion engine ([b8f9f83](https://github.com/nvim-neorg/neorg/commit/b8f9f834d999a6807ee9476857fb3af2c58f64a2))\n* create `deps.json` for a universal list of Neorg dependencies ([a64c6af](https://github.com/nvim-neorg/neorg/commit/a64c6af4ac1ab4aa3a5de93d86111814125e3ed8))\n* **latex:** async image rendering ([b1c96a0](https://github.com/nvim-neorg/neorg/commit/b1c96a049da8d534820f7452195fc9d961f3d025))\n* **latex:** minimum length to render ([5a9d638](https://github.com/nvim-neorg/neorg/commit/5a9d6381581014c67219a823f149ce871f3af15d))\n* modify deps.json to have proper version constraints ([5e9a3ef](https://github.com/nvim-neorg/neorg/commit/5e9a3ef798726fd2001d1596e6134b03f331a333))\n* treesitter-based completions ([#1412](https://github.com/nvim-neorg/neorg/issues/1412)) ([79f6a49](https://github.com/nvim-neorg/neorg/commit/79f6a49b869a891bca9ce746f77781af46977e71))\n\n\n### Bug Fixes\n\n* clear extmarks on disable ([1be7808](https://github.com/nvim-neorg/neorg/commit/1be78080714b6f0cc1d77432629b91328880ce53))\n* clear images at cursor after change ([9edd802](https://github.com/nvim-neorg/neorg/commit/9edd802c194ef180587c9c836ea84142927bc887))\n* clear package.loaded cache when first installing neorg ([3d50b49](https://github.com/nvim-neorg/neorg/commit/3d50b49e1c1d37182c2ed94e718ecd5eed8cacd4))\n* compute image dimensions before rendering ([94abd99](https://github.com/nvim-neorg/neorg/commit/94abd999cbb21d66080ee3970f41303c7093e1a6))\n* conceal cursor updates ([2701e07](https://github.com/nvim-neorg/neorg/commit/2701e0770697ca10665277c0afd692567b24103d))\n* **dirman:** swapfile error when opening index.norg ([#1451](https://github.com/nvim-neorg/neorg/issues/1451)) ([70d4b89](https://github.com/nvim-neorg/neorg/commit/70d4b899928d72ec9ab7961f69ae47fd24b8c3c4))\n* handle switching buffers ([af4001e](https://github.com/nvim-neorg/neorg/commit/af4001ebd8678261e334591be4435f11e1aac294))\n* image flashing on text change ([19737b0](https://github.com/nvim-neorg/neorg/commit/19737b02be7aee6c4395439fbd756901adb428a1))\n* **latex-renderer:** handle broken latex ([#1438](https://github.com/nvim-neorg/neorg/issues/1438)) ([8140135](https://github.com/nvim-neorg/neorg/commit/81401353dc13ff87f4879b6e0b5f96ff2de14d9c))\n* **latex:** predict image size for scale &gt; 1 ([bde2402](https://github.com/nvim-neorg/neorg/commit/bde24023d2b1ae507034c0839144f36e96dc7dc2))\n* limages losing track of their extmarks ([f05bce2](https://github.com/nvim-neorg/neorg/commit/f05bce247e53a53a19bfe47c57d6bc8023b5c43b))\n* logic when inline = false ([fc8c054](https://github.com/nvim-neorg/neorg/commit/fc8c0542d8676155e8c47d0b735e816a366235b1))\n* mark core.links as internal ([22e7151](https://github.com/nvim-neorg/neorg/commit/22e7151f02559cea0320d02b57e59d2fb8294956))\n* render images on cursor line on enable ([dc51ff4](https://github.com/nvim-neorg/neorg/commit/dc51ff489a653c292c6bf84751a5d060e8018c6c))\n* rendering order for multiple img in one line ([a50c034](https://github.com/nvim-neorg/neorg/commit/a50c03432ba9cd4c370ebedfa6d84db1f0e61b6d))\n* **text-objects:** nil check node's parent ([72da6e0](https://github.com/nvim-neorg/neorg/commit/72da6e0773298356f5a83ce52c9efdd88d4147e5))\n* use nio.scheduler to avoid double wrap ([2a9c3fa](https://github.com/nvim-neorg/neorg/commit/2a9c3fab1bb6beabc4160264835be7f3b9a579e7))\n* various type errors in codebase ([c564e6c](https://github.com/nvim-neorg/neorg/commit/c564e6cd470e1582414b63720ef85f1d0abe1d64))\n* virt text disapearing on conceallevel=0 ([acd4293](https://github.com/nvim-neorg/neorg/commit/acd4293ab269c42eb9f3294d182eb87e7a34d66b))\n* work on one buffer at a time ([ba41187](https://github.com/nvim-neorg/neorg/commit/ba41187669002622b7f9778e4c49723f47faf69a))\n\n\n### Reverts\n\n* d8ba19a18c896cc146e7ecbd4d67bdbe03f2e0c4 ([c8e2947](https://github.com/nvim-neorg/neorg/commit/c8e2947bdb71838ce90614cb3d9a0eb530c45fd7))\n\n## [8.5.0](https://github.com/nvim-neorg/neorg/compare/v8.4.1...v8.5.0) (2024-05-17)\n\n\n### Features\n\n* text-objects and working swap ([#1421](https://github.com/nvim-neorg/neorg/issues/1421)) ([49a3c64](https://github.com/nvim-neorg/neorg/commit/49a3c64c06dae55b5424b218dc7c6e6b643fd4f5))\n\n\n### Bug Fixes\n\n* **core.todo-introspector:** treesitter highlighter errors ([cb4f25b](https://github.com/nvim-neorg/neorg/commit/cb4f25bca799c49a81e471aeca1d6d95322c87a6))\n\n## [8.4.1](https://github.com/nvim-neorg/neorg/compare/v8.4.0...v8.4.1) (2024-04-13)\n\n\n### Bug Fixes\n\n* **checkhealth:** add missing parameter to string.format ([#1386](https://github.com/nvim-neorg/neorg/issues/1386)) ([9656757](https://github.com/nvim-neorg/neorg/commit/9656757f7085e9e36d2469b8876f6b27f8e87c7b))\n* lack of comma ([65c7550](https://github.com/nvim-neorg/neorg/commit/65c75508626ad02a3cb40a84c4c3df0cde7b0e5a))\n* properly check for existence of a given module by trying to load it instead of passively scanning the loaded module list ([727ef2a](https://github.com/nvim-neorg/neorg/commit/727ef2ab4247b52733d1a84d87143257e783d4e9))\n\n## [8.4.0](https://github.com/nvim-neorg/neorg/compare/v8.3.0...v8.4.0) (2024-04-04)\n\n\n### Features\n\n* add `integrations.otter` for LSP-like behaviours in code blocks ([#1329](https://github.com/nvim-neorg/neorg/issues/1329)) ([ccb7555](https://github.com/nvim-neorg/neorg/commit/ccb75557f8582e044c687452b8b249151f6e7098))\n\n\n### Bug Fixes\n\n* broken wiki ([68ea6b5](https://github.com/nvim-neorg/neorg/commit/68ea6b53b6cb58c54ac51256cdfd76eec540806c))\n* don't load `core.todo-introspector` if not on nightly (oops) ([bc7830e](https://github.com/nvim-neorg/neorg/commit/bc7830ec3e538d381f5361ca80a9bc5f97fc8fa9))\n\n## [8.3.0](https://github.com/nvim-neorg/neorg/compare/v8.2.1...v8.3.0) (2024-04-04)\n\n\n### Features\n\n* basic checkhealth for configuration checking ([645cd0c](https://github.com/nvim-neorg/neorg/commit/645cd0c257b2fccc655a32d0b04aa706c96fb1a8))\n* check for the existence of `vhyrro/luarocks.nvim` ([a5aa931](https://github.com/nvim-neorg/neorg/commit/a5aa93108999de75c8d075a0ee4dcc6a715a9e1a))\n* **dirman:** use pathlib for all dirman operations  ([#1354](https://github.com/nvim-neorg/neorg/issues/1354)) ([1190dc7](https://github.com/nvim-neorg/neorg/commit/1190dc78b9785ad75301de9153ed8de83c179b66))\n* implement the basics of the new todo introspector ([a7ad515](https://github.com/nvim-neorg/neorg/commit/a7ad51519f2e7a7274c069e35d4396a0a5f88ddc))\n* **todo-introspector:** correctly enumerate amounts of done/undone items ([d284488](https://github.com/nvim-neorg/neorg/commit/d2844882ec0d18d59a7b8727b893a964fe76a754))\n* **todo-introspector:** display all TODOs on file entry ([80b2d33](https://github.com/nvim-neorg/neorg/commit/80b2d33b1f402b0279009442caf6cfaa9cd308f4))\n* **todo-introspector:** finalize display code ([949ae96](https://github.com/nvim-neorg/neorg/commit/949ae962558d43673130159cd6bce89e4bf4febc))\n* **todo-introspector:** properly handle nodes broken into two ([76e6443](https://github.com/nvim-neorg/neorg/commit/76e6443c9823470897245951f37cef0cc5b966f0))\n\n\n### Bug Fixes\n\n* add `core.todo-introspector` to the list of default modules ([5202271](https://github.com/nvim-neorg/neorg/commit/52022710d47fd66c73280f7a8d7fe2013d899224))\n* **checkhealth:** fix erroneous boolean check for lazy users ([d523688](https://github.com/nvim-neorg/neorg/commit/d523688ecc6ddf5fbac9b42e0b62515c7655bed3))\n* **dirman:** norg link with `.` was misinterpreted as the extension ([96fe2c9](https://github.com/nvim-neorg/neorg/commit/96fe2c92d0e8fb2a2a853847909155aae4d5ca46))\n* **dirman:** respect force option in dirman create_file ([0782ca4](https://github.com/nvim-neorg/neorg/commit/0782ca4a221cfd965f05752d7178b2692bb64ae0))\n* **introspector:** various bugs related to extmark updates ([e8c9193](https://github.com/nvim-neorg/neorg/commit/e8c9193b10ea946e6f90e06daf0efeafb55fa970))\n* issue a warning to users when dependencies cannot be found (instead of silently failing) ([04f4127](https://github.com/nvim-neorg/neorg/commit/04f4127a08a59d110c882464b11294dec0cf1258))\n* **luarocks:** pin `pathlib` to `2.0` (as luarocks doesn't like it otherwise) ([5b93840](https://github.com/nvim-neorg/neorg/commit/5b93840d97c2ac0d1534321ca3659f4b8c1342e4))\n* **pathlib:** bump pathlib version; fix `path:touch` ([bedbbe2](https://github.com/nvim-neorg/neorg/commit/bedbbe208e61491d1d8be0b6775793b246b444d0))\n* **todo-introspector:** correctly handle deletion of lines ([a8b7ad0](https://github.com/nvim-neorg/neorg/commit/a8b7ad08f6e8cd36c83e371ab9a74cc1e5252a0b))\n* **todo-introspector:** do not error when a line out of bounds is deleted ([62c7925](https://github.com/nvim-neorg/neorg/commit/62c7925e17ab25cc40c50cd266701a36aa854a50))\n* **todo-introspector:** properly clear namespace for refreshed buffers ([5f67407](https://github.com/nvim-neorg/neorg/commit/5f6740795303a03b58c81bf6396ae03d487d5b7c))\n\n\n### Reverts\n\n* backout of commit 5b93840d97c2ac0d1534321ca3659f4b8c1342e4 ([e0690fa](https://github.com/nvim-neorg/neorg/commit/e0690fa65546bd91f9aa4883f1ce4cbea45e1898))\n\n## [8.2.1](https://github.com/nvim-neorg/neorg/compare/v8.2.0...v8.2.1) (2024-03-28)\n\n\n### Bug Fixes\n\n* **core.neorgcmd.list:** don't accidentally open a separate file when displaying loaded modules ([eef1705](https://github.com/nvim-neorg/neorg/commit/eef1705ee78ae6e66917901da507a666743de877))\n* don't allow TODOs with nested items of the same type to be given the pending status ([5942fdf](https://github.com/nvim-neorg/neorg/commit/5942fdf7eb42b6364ca0a754ac88bd0ca05ae660))\n* load default modules even if an empty table is provided as input to setup() ([c1d36ad](https://github.com/nvim-neorg/neorg/commit/c1d36add07364e024dbf39276388741e54f7e955))\n* **neorgcmd.modules.list:** add basic escape keybinds ([71e2f05](https://github.com/nvim-neorg/neorg/commit/71e2f05ee19c94705fadbc1f84e11990baf8ff1c))\n* **typecheck:** use type definitions from nvim runtime instead ([#1358](https://github.com/nvim-neorg/neorg/issues/1358)) ([e7f393f](https://github.com/nvim-neorg/neorg/commit/e7f393f9e4a17c32289875e273f687863336894c))\n\n## [8.2.0](https://github.com/nvim-neorg/neorg/compare/v8.1.0...v8.2.0) (2024-03-25)\n\n\n### Features\n\n* **metagen:** add author field to provide persistent custom author name ([#1331](https://github.com/nvim-neorg/neorg/issues/1331)) ([e576308](https://github.com/nvim-neorg/neorg/commit/e576308243b58838ed97309bec60bf180cde3c91))\n\n\n### Bug Fixes\n\n* **ci:** \"could not find upvalue `lib`\" error ([486a148](https://github.com/nvim-neorg/neorg/commit/486a148d1bf5b7fd14f52a771a0dacc1e6839174))\n* **ci:** supply correct version to the lua setup CI ([c814ef6](https://github.com/nvim-neorg/neorg/commit/c814ef68295baffefed7bfb8a48f8835f73a55a6))\n* **core/events:** fall back to the current window ID if it cannot be located ([22df349](https://github.com/nvim-neorg/neorg/commit/22df349df39d9401a95f7dc0e3dc13113f91a60a))\n* **dirman:** properly escape directories and filenames ([#1232](https://github.com/nvim-neorg/neorg/issues/1232)) ([e1f5556](https://github.com/nvim-neorg/neorg/commit/e1f5556bfbe50cbae262dffc35f376f7469f68cf))\n* do not add the line jump of a link to the jump list ([#1325](https://github.com/nvim-neorg/neorg/issues/1325)) ([918f2a3](https://github.com/nvim-neorg/neorg/commit/918f2a39f96e1447c00871eb611bed2018a047b5))\n* **export.markdown:** export `authors` metadata field key as `author` ([#1319](https://github.com/nvim-neorg/neorg/issues/1319)) ([f30ce72](https://github.com/nvim-neorg/neorg/commit/f30ce728e1b99e23320114c3bddb18be2776baf7))\n* **export.markdown:** fix incorrect reset of ordered list item count ([#1324](https://github.com/nvim-neorg/neorg/issues/1324)) ([ba58c1b](https://github.com/nvim-neorg/neorg/commit/ba58c1b29c9b013928025db345c6ff170e9693bf))\n\n## [8.1.0](https://github.com/nvim-neorg/neorg/compare/v8.0.1...v8.1.0) (2024-03-24)\n\n\n### Features\n\n* **todo_items:** convert TODO item to \"on hold\" if all items are done but the rest are on hold ([#1339](https://github.com/nvim-neorg/neorg/issues/1339)) ([c32b238](https://github.com/nvim-neorg/neorg/commit/c32b238438a8f1130c89c13a2284961fe10e3e68))\n\n\n### Bug Fixes\n\n* remove old and hacky code related to nvim-treesitter's query cache invalidation ([e8d8d1e](https://github.com/nvim-neorg/neorg/commit/e8d8d1e6608e53e366109fc4f9d7ab364ea0fb5c))\n\n## [8.0.1](https://github.com/nvim-neorg/neorg/compare/v8.0.0...v8.0.1) (2024-03-24)\n\n\n### Bug Fixes\n\n* broken wiki on github ([d4c10fe](https://github.com/nvim-neorg/neorg/commit/d4c10fe58519ce0d827cfc02f87832c75395045a))\n* **ci:** try to fix the wiki generator with luarocks ([27ac595](https://github.com/nvim-neorg/neorg/commit/27ac595d90481bd8fa2d13290289d46287346903))\n* **docgen:** invalid upvalues ([84ee928](https://github.com/nvim-neorg/neorg/commit/84ee928cd91db8705111c3d485e2a38ca5de61ec))\n* **luarocks:** add proper dependencies ([81328d1](https://github.com/nvim-neorg/neorg/commit/81328d17ed9d5509e7dea8f1efc0fa568535e0e0))\n\n\n### Reverts\n\n* return back old logger code ([a8151f1](https://github.com/nvim-neorg/neorg/commit/a8151f1e21445739c9574d5eba9f4c635688cf98))\n\n## [8.0.0](https://github.com/nvim-neorg/neorg/compare/v7.0.0...v8.0.0) (2024-03-24)\n\n\n### ⚠ BREAKING CHANGES\n\n* use decoupled lua-utils instead of the regular neorg utils\n* **lib:** deprecate `lib.map`\n* deprecate `core.upgrade`\n* **concealer:** simpler config for ordered list icon & multichar icon for unordered list ([#1179](https://github.com/nvim-neorg/neorg/issues/1179))\n* **neorgcmd:** slowly move away from the deprecated `commands` directory\n* **highlights:** updated default groups to match names in treesitter\n\n### Features\n\n* add basic build.lua ([efac9eb](https://github.com/nvim-neorg/neorg/commit/efac9eb8c16cfe5cd1a45705d2add4eca749e63f))\n* add lua-utils.nvim to the list of required rocks ([b7b9eda](https://github.com/nvim-neorg/neorg/commit/b7b9edad6a852f33a2ce99051c748823dabd28cc))\n* add new dependencies for norgopolis ([0e88310](https://github.com/nvim-neorg/neorg/commit/0e883108d8c782335615cf2108a703847a1295d9))\n* add support for inline link targets ([132b73b](https://github.com/nvim-neorg/neorg/commit/132b73bfacd3014dc8afb56ddf7eed8c7acf6d6d))\n* auto complete links ([#1295](https://github.com/nvim-neorg/neorg/issues/1295)) ([bd12dac](https://github.com/nvim-neorg/neorg/commit/bd12dacc9cf561cbffc8d6f8f4b76aa9d734665b))\n* **concealer:** code block background `min_width` ([#1328](https://github.com/nvim-neorg/neorg/issues/1328)) ([efac835](https://github.com/nvim-neorg/neorg/commit/efac8350f4afe0b49f278129ef92ffb0a02d1c6f))\n* **concealer:** simpler config for ordered list icon & multichar icon for unordered list ([#1179](https://github.com/nvim-neorg/neorg/issues/1179)) ([da74d14](https://github.com/nvim-neorg/neorg/commit/da74d14f217dc81bc364758bbecea3c5e934ba60))\n* **concealer:** use empty foldmethod on nightly releases (for full folding passthrough) ([086891d](https://github.com/nvim-neorg/neorg/commit/086891d396ac9fccd91faf1520f563b6eb9eb942))\n* **export.markdown:** option to export latex `embed` tags ([0abe7b7](https://github.com/nvim-neorg/neorg/commit/0abe7b737d35f2abd082bc6f694cf5a9fc166fb7))\n* fix build.lua process (maybe once and for all?) ([eea6263](https://github.com/nvim-neorg/neorg/commit/eea6263ac4f3506d34d6e79839606e60b074757b))\n* include plenary as a dependency ([6ea1eff](https://github.com/nvim-neorg/neorg/commit/6ea1eff15d3f1fa947255a94f99cadb298c8b66f))\n* **keybinds:** add `opts` arg to `remap(_event)` ([27af839](https://github.com/nvim-neorg/neorg/commit/27af839eb6833f82765bc3066ab7e9b437233dd2))\n* prepare neorg.core.lib for extraction ([c4eb7e9](https://github.com/nvim-neorg/neorg/commit/c4eb7e96ea1e2a0a4b6d47e6bda4f6816a908262))\n* run sync-parsers as a build step ([9dd8331](https://github.com/nvim-neorg/neorg/commit/9dd8331bc1ad42117c7173cd5501b93570db85d5))\n* **summary:** reimplement nested categories ([#1274](https://github.com/nvim-neorg/neorg/issues/1274)) ([6202285](https://github.com/nvim-neorg/neorg/commit/6202285214e70efe0d861c5a4969f8ee817bc985))\n* undojoin timestamp updates ([#1272](https://github.com/nvim-neorg/neorg/issues/1272)) ([fe25e93](https://github.com/nvim-neorg/neorg/commit/fe25e93336b6a71b3cb3d7fd53ab6e4cb4a125c1))\n* when absolutely no parameters are supplied, load Neorg with core.defaults ([b6fb57b](https://github.com/nvim-neorg/neorg/commit/b6fb57b723c02255a9d0c0f1a8fc957fe007d9c2))\n\n\n### Bug Fixes\n\n* **build.lua:** install dependencies instead of the actual plugin itself (prevent conflicts) ([da25527](https://github.com/nvim-neorg/neorg/commit/da2552769b572c012ff2f0ee9c11e3a26f061252))\n* **build:** attempt to fix build script by deferring code execution ([fb45f83](https://github.com/nvim-neorg/neorg/commit/fb45f836da9dd43940c3fdd182e8255bbce9d9dc))\n* bump version of `norgopolis-server` to 1.3.1 ([0d8a7ec](https://github.com/nvim-neorg/neorg/commit/0d8a7ecae258e15f40e88bc3b312d2b92192743f))\n* **ci:** fix abs path to libs in luarc ([#1267](https://github.com/nvim-neorg/neorg/issues/1267)) ([0edde97](https://github.com/nvim-neorg/neorg/commit/0edde97b51a5247bd4db351a38d5f36131b642f7))\n* **ci:** wrong version on typecheck ([fb23d2e](https://github.com/nvim-neorg/neorg/commit/fb23d2e78bf6ee601ed1de2a9ded23d6201f7506))\n* **concealer:** footnote pattern should be matched against full string ([fc09cfc](https://github.com/nvim-neorg/neorg/commit/fc09cfc25e243a82653a758bc137395f4860b6f5))\n* **config:** add support for bsd operating systems ([#1281](https://github.com/nvim-neorg/neorg/issues/1281)) ([2bdb89c](https://github.com/nvim-neorg/neorg/commit/2bdb89c388d5c9e1956e7aab949ffb003e9a8ea5))\n* **config:** make the type system happy ([27482dc](https://github.com/nvim-neorg/neorg/commit/27482dcee4b14ed61a10ba51261919cb45351dad))\n* **core.keybinds:** type errors with events ([dbe2841](https://github.com/nvim-neorg/neorg/commit/dbe28417222e044bcbec5bb016f0d604004bcbb3))\n* **core.mode:** type errors with events ([fb2c561](https://github.com/nvim-neorg/neorg/commit/fb2c561f0080b621fd2853a3190d48f885a13b6d))\n* **core.neorgcmd:** type errors with events ([1ab6236](https://github.com/nvim-neorg/neorg/commit/1ab6236a954cf2de6fe4b736a66ca5a17d85a6ff))\n* **core.promo:** type errors with events ([0016fdd](https://github.com/nvim-neorg/neorg/commit/0016fdd8f2349dec1c1865f3412dbd08232b1bbd))\n* **core.syntax:** remove deprecated functions, fix type errors in the code ([221bb2e](https://github.com/nvim-neorg/neorg/commit/221bb2eb10c8d7b7f62537393a9dce385d36b638))\n* **core/modules:** reorder comments so that they are properly parsed by luals ([f20b40a](https://github.com/nvim-neorg/neorg/commit/f20b40a44a4e96ff9fa5ed252c3a678629adfda9))\n* **docgen:** make the wiki work again ([d44dd38](https://github.com/nvim-neorg/neorg/commit/d44dd387d8f553791671f52f691be7580b98c6db))\n* don't try to pull lua-utils when it's not applicable ([bcac799](https://github.com/nvim-neorg/neorg/commit/bcac79933f3930f04d9b1517106646a56efd8606))\n* enable source of `nvim-cmp` only norg file type ([#1298](https://github.com/nvim-neorg/neorg/issues/1298)) ([1ab15f4](https://github.com/nvim-neorg/neorg/commit/1ab15f4b30627fd5e6dd175a23c7360c2c08b2bd))\n* enforce contraint on norgopolis-server ([4b9f25c](https://github.com/nvim-neorg/neorg/commit/4b9f25ca9760e89702ccbe117d1ce17780b64641))\n* error with import loop ([16b5479](https://github.com/nvim-neorg/neorg/commit/16b54794a545d8f80c0e9007952e374df2e417cd))\n* **export.markdown:** fix error on unexported verbatim tags without parameters ([#1280](https://github.com/nvim-neorg/neorg/issues/1280)) ([e6d89d3](https://github.com/nvim-neorg/neorg/commit/e6d89d333aff65a771a98955fac9fc178345c01c))\n* **export.markdown:** fix html `embed` tags not being exported ([5b2022c](https://github.com/nvim-neorg/neorg/commit/5b2022caaf689dc1c78b8959a2547249f8b05769))\n* **export.markdown:** fix markdown `embed` tags not being exported ([f3d4230](https://github.com/nvim-neorg/neorg/commit/f3d4230d37da5d727d3ae13e1bada30e37b433ad))\n* **export.markdown:** fix the first `tag` always being exported by default ([bda456d](https://github.com/nvim-neorg/neorg/commit/bda456d6685545893d446e841f2ee41633b6548a))\n* **export.markdown:** use proper amount of parameters ([b0b5a43](https://github.com/nvim-neorg/neorg/commit/b0b5a4370228f27bd98516b9061bd6c87386c8f3))\n* **highlights:** updated default groups to match names in treesitter ([56ad805](https://github.com/nvim-neorg/neorg/commit/56ad8056b6180dba60ddbd5bca2f29de12f3bd1d))\n* **highlights:** updated unordered list, underline and strikethrough groups with standard names ([e7f524c](https://github.com/nvim-neorg/neorg/commit/e7f524c44f1a5d6fba6cced7e4eb3c22b9ff1473))\n* incorrect code in upgrade module ([07967f1](https://github.com/nvim-neorg/neorg/commit/07967f1982b589974958689c7a055b33ea194691))\n* **integrations.truezen:** use `setup()` instead of `load()` ([26cfe0e](https://github.com/nvim-neorg/neorg/commit/26cfe0e155c35695d2d4af7d938a9ffd160b8797))\n* **integrations.truezen:** use `setup()` instead of `load()` ([3506236](https://github.com/nvim-neorg/neorg/commit/3506236e292de6d7989b6d6541ed5fcfa1e73bab))\n* invalid vim.cmd syntax ([affdd6f](https://github.com/nvim-neorg/neorg/commit/affdd6fcbc2092fca293817d65e1664afbafe223))\n* nobody figured it out so away it goes :) ([7b3e794](https://github.com/nvim-neorg/neorg/commit/7b3e794aa8722826418501608c8a3ffe4e19ea30))\n* perform setup after the parsers have been installed ([f90c965](https://github.com/nvim-neorg/neorg/commit/f90c9654352f424690327271e3bd9a2c036489d0))\n* properly install parsers ([59b6d61](https://github.com/nvim-neorg/neorg/commit/59b6d619213506e405a8ed13669dc82120653ac5))\n* properly log TS error messages ([73db6b5](https://github.com/nvim-neorg/neorg/commit/73db6b51e9e28cce7ef17baf78a8416b563ca53a))\n* properly require lua-utils ([b8a78c0](https://github.com/nvim-neorg/neorg/commit/b8a78c0c84dcfd3996480339c3d10c6e1ade8363))\n* refactor library to not use lua utils ([5fcae0b](https://github.com/nvim-neorg/neorg/commit/5fcae0b080531ac1438faeefd47ae11e1633b463))\n* refresh lua cache upon succesful installation of dependencies ([f1473cf](https://github.com/nvim-neorg/neorg/commit/f1473cf9ab1c1b610758e28fcb9e8a792a51ddf4))\n* remove lua-utils from the loaded list to force a refresh ([af1e06c](https://github.com/nvim-neorg/neorg/commit/af1e06c801d6cb5682dde9a63b22053a8cf28665))\n* rename Neorg index message to be more insightful ([6d686cd](https://github.com/nvim-neorg/neorg/commit/6d686cdc064489ed17b49b6f1463fc9b3e5ba698))\n* **syntax:** ignore type annotation errors in syntax module ([6d94c2a](https://github.com/nvim-neorg/neorg/commit/6d94c2ac08f13208d84ce21b1e3eea13158b6491))\n* TSInstallSync not found ([df6cc22](https://github.com/nvim-neorg/neorg/commit/df6cc22f36e347856bc14807b9db396e67b927d7))\n* update module name to reflect breaking changes within `luarocks.nvim` ([1779e59](https://github.com/nvim-neorg/neorg/commit/1779e5962badca89505b60e9617b939489c661b0))\n* use lua-utils ([b1ce837](https://github.com/nvim-neorg/neorg/commit/b1ce8374a88d638f42f0ce97b3b4b6b2b4e89023))\n\n\n### Code Refactoring\n\n* deprecate `core.upgrade` ([45f51ed](https://github.com/nvim-neorg/neorg/commit/45f51ed759d9cdd6c69b67e57ecbd054fd4cbaba))\n* **lib:** deprecate `lib.map` ([8340274](https://github.com/nvim-neorg/neorg/commit/83402746b8b43190edb360329a023040bd388294))\n* **neorgcmd:** slowly move away from the deprecated `commands` directory ([560d5a0](https://github.com/nvim-neorg/neorg/commit/560d5a04fb8143aaa5e64ba8eb100df97631fa36))\n* use decoupled lua-utils instead of the regular neorg utils ([5f6bf7e](https://github.com/nvim-neorg/neorg/commit/5f6bf7e5444fe839d739bd376ec5cdb362f02dc6))\n\n## [7.0.0](https://github.com/nvim-neorg/neorg/compare/v6.2.0...v7.0.0) (2023-12-28)\n\n\n### ⚠ BREAKING CHANGES\n\n* **selection_popup:** modernize code of selection popup\n\n### ref\n\n* **selection_popup:** modernize code of selection popup ([310f3a4](https://github.com/nvim-neorg/neorg/commit/310f3a484d3d98b0d05650a38407dcaa7f090b96))\n\n\n### Features\n\n* allow upward paths in tangle ([265e6af](https://github.com/nvim-neorg/neorg/commit/265e6af8decbb30b0ee14aee373b1bfe9a78b858))\n* **concealer:** add ability to disable spell checking in code blocks ([316403a](https://github.com/nvim-neorg/neorg/commit/316403ad1cbb665e7838f596384d44b1649f6c1b))\n* **concealer:** add config for concealing numeric footnote title to superscript ([2a6fc9c](https://github.com/nvim-neorg/neorg/commit/2a6fc9c808f6d643bf7c2f911a767e4aac500560))\n* **concealer:** add configuration for hrule start and end position ([3db316a](https://github.com/nvim-neorg/neorg/commit/3db316a33838eb0875eacd659af9d49bbd4aef39))\n* **keyinds:** add keybind for entering link traversal mode ([#1177](https://github.com/nvim-neorg/neorg/issues/1177)) ([8cf5205](https://github.com/nvim-neorg/neorg/commit/8cf52058fb7e9c3057882430ade90be5bdfb3a94))\n* prefix all keybind descriptions with \"neorg\" for discoverability ([15c24cd](https://github.com/nvim-neorg/neorg/commit/15c24cdb264807b09e9281e2d72b324145da1d57))\n* **selection_popup:** allow keybinds to be processed from another buffer ([603b633](https://github.com/nvim-neorg/neorg/commit/603b633b8df231fe37a338856b1dea7cd955a969))\n* **summary:** add strategy which uses workspace subfolders as category ([aa8e66d](https://github.com/nvim-neorg/neorg/commit/aa8e66dd40c07a4de58f9ed93f27ab4dac9a241c))\n* **tangle:** add `report_on_empty` option in `core.tangle` ([#1250](https://github.com/nvim-neorg/neorg/issues/1250)) ([cc6d8b1](https://github.com/nvim-neorg/neorg/commit/cc6d8b150de7bf806f3a191867a7f143970b5112))\n* **toc:** add config for enabling synchronized cursorline in toc window ([d3cbb45](https://github.com/nvim-neorg/neorg/commit/d3cbb45b66c865b1b92b5f8b2dbd5a5fff7f1a2f))\n* **toc:** add toc item filter ([#1195](https://github.com/nvim-neorg/neorg/issues/1195)) ([5c42084](https://github.com/nvim-neorg/neorg/commit/5c420844227c75390cc9fdf6047bfc49466169d9))\n* **toc:** auto adjust toc vsplit width upon creation ([81f6330](https://github.com/nvim-neorg/neorg/commit/81f6330af951e89f98e8468d23a648fc32acdd2f))\n* **toc:** don't scroll content window when switching to toc ([c4fc7e6](https://github.com/nvim-neorg/neorg/commit/c4fc7e629e8ea7ecc9610107622f46e888764534))\n* **toc:** enable folding in toc ([218e7eb](https://github.com/nvim-neorg/neorg/commit/218e7ebbce010846c5ed6da647264c556c6a7ad4))\n* **toc:** faster toc generation ([0171df1](https://github.com/nvim-neorg/neorg/commit/0171df1d0f8a6db254020e8b02ac576188ffad23))\n* **toc:** support one ToC per tabpage ([d8a456b](https://github.com/nvim-neorg/neorg/commit/d8a456b7fa1b9d860fc36750b6e9a200a8eff5f3))\n* **toc:** support todo status ([4ac077b](https://github.com/nvim-neorg/neorg/commit/4ac077b1f19efe63fcec4e6c744bc6a68dfc7f6a))\n* **toc:** sync cursor from ToC to content buffer ([47e7c86](https://github.com/nvim-neorg/neorg/commit/47e7c86877aaae4d85c1a2add166ad6c15b8add4))\n* **toc:** sync toc cursor after creating toc, scroll content to center when previewing ([cfcb51e](https://github.com/nvim-neorg/neorg/commit/cfcb51ea9a403ee7223e49d4afb0142d6d5e1659))\n\n\n### Bug Fixes\n\n* \"Keybind not found\" display causing errors ([#1215](https://github.com/nvim-neorg/neorg/issues/1215)) ([a51abd5](https://github.com/nvim-neorg/neorg/commit/a51abd53d8afc7de81e35d0a4247c3aa6ccfc76a))\n* `update-metadata` would fail to work with several parse trees in the document ([#1234](https://github.com/nvim-neorg/neorg/issues/1234)) ([5a44d3f](https://github.com/nvim-neorg/neorg/commit/5a44d3ffbd3b4fff762f8b2712ab1cfd16cff016))\n* **action:** run lint action against pr head ([f367396](https://github.com/nvim-neorg/neorg/commit/f36739620410917a3119ee4299894c353a0d88af))\n* **autocommands:** pass correct buffer id ([941119d](https://github.com/nvim-neorg/neorg/commit/941119d48a5e354cfbed24a4b314bb4eb401a75b))\n* **concealer:** BufNewFile-&gt;FileType, get winid of bufid when rendering ([c0983ca](https://github.com/nvim-neorg/neorg/commit/c0983ca60f02e1a65e5990593726e57678e03c4a))\n* **concealer:** do not render on range change if concealer is disabled ([9b0c31a](https://github.com/nvim-neorg/neorg/commit/9b0c31a5179f3881f9ff2350da22c9a5a11f32ab))\n* **concealer:** ensure backwards compatibility for `vim.treesitter.foldexpr` ([5921cc4](https://github.com/nvim-neorg/neorg/commit/5921cc48cb3be616db0071fa058cfa4d6633c8a6))\n* **concealer:** use vim.treesitter.foldexpr for stabler folding ([53cbffb](https://github.com/nvim-neorg/neorg/commit/53cbffb7ecfcb60f19c10c72c4162978e8021959))\n* **config:** delete `neovim_version` as it is no longer in use ([00f9a62](https://github.com/nvim-neorg/neorg/commit/00f9a628683b7b3f738e1d1d1a79d517c26b6ff5))\n* **config:** fix luajit version detection ([237abac](https://github.com/nvim-neorg/neorg/commit/237abac43a38e4aa770bb5819f30b3d38ae5f392))\n* **export:** better handling of new lines in markdown metadata ([d56cc3c](https://github.com/nvim-neorg/neorg/commit/d56cc3c9a9cd10bfac5eac2514a9457a3e9e848d))\n* **export:** fix metadata values being ignored when converting to markdown ([6f9b66c](https://github.com/nvim-neorg/neorg/commit/6f9b66cfa75241d4b8c0890a312872104a2d96a1))\n* **export:** handle empty `object`/`array` nodes in markdown metadata ([3afbadb](https://github.com/nvim-neorg/neorg/commit/3afbadb3d116d6f8a5fb0aa3af1c06563c4a038e))\n* **hop:** fix range check across lines ([1038016](https://github.com/nvim-neorg/neorg/commit/10380167975732444f21c882e522d15b0ec55b34))\n* **journal:** value assigned to variable current_quarter is unused ([0e88151](https://github.com/nvim-neorg/neorg/commit/0e8815116b08bfbceb2b36a8c82d81005e2596e0))\n* **latex:** Want image integration ([a80c025](https://github.com/nvim-neorg/neorg/commit/a80c025b231a6acd925d625d6d9ea302bc20bd49))\n* **luacheck:** setting non-standard global variables in latex renderer module ([#1176](https://github.com/nvim-neorg/neorg/issues/1176)) ([3f4b279](https://github.com/nvim-neorg/neorg/commit/3f4b279d7505ac854fcd31d1aad24991542ea5d8))\n* **modules:** Check the right config key in module.wants ([8b25435](https://github.com/nvim-neorg/neorg/commit/8b25435e8bc60f9e6f665b3a28870d64d20f2b59))\n* **neorg.norg:** clarify horizontal line syntax ([#1230](https://github.com/nvim-neorg/neorg/issues/1230)) ([e35bf90](https://github.com/nvim-neorg/neorg/commit/e35bf907533281a6c641505eae3bb42100d7b5a0))\n* record that module `upgrade` requires at least 1 arg ([#1207](https://github.com/nvim-neorg/neorg/issues/1207)) ([51f55f5](https://github.com/nvim-neorg/neorg/commit/51f55f5c6d54fa86fdaae805b55ca88aa9607c37))\n* **summary:** set correct indentation for list items ([120fb52](https://github.com/nvim-neorg/neorg/commit/120fb52f5fe21c43fcc7285bac4a9bce8a54a6ec))\n* **toc:** clear title after assigning prefix ([f446645](https://github.com/nvim-neorg/neorg/commit/f4466457396717d10d2d235d019e0a80e1770087))\n* **toc:** fix all stylua errors ([ae38baf](https://github.com/nvim-neorg/neorg/commit/ae38baf90a319488b726ed25166fc00641b3e0ce))\n* **toc:** get window id on the fly to avoid assertion errors ([1b0ab75](https://github.com/nvim-neorg/neorg/commit/1b0ab75e8e57b08bc981e0d72fe928b0fff34fe2))\n* **toc:** handle buf close ([985364f](https://github.com/nvim-neorg/neorg/commit/985364f561518502cc002494db4d48ec92b00d80))\n* **toc:** handle buf close ([2d65f6c](https://github.com/nvim-neorg/neorg/commit/2d65f6cf7a0f40b9a474e17bc347255514dbde0e))\n* **toc:** listen cursormoved for all norg files ([19bff13](https://github.com/nvim-neorg/neorg/commit/19bff133659c16973e52546f54a13469bfecb1b6))\n* **toc:** stop synching cursor when content window is hidden ([15ed981](https://github.com/nvim-neorg/neorg/commit/15ed981858658796b698f6fc204f1378eef4b01d))\n* **typecheck:** fix type errors caused by autoformat ([3f531c3](https://github.com/nvim-neorg/neorg/commit/3f531c362d07d52c4956520e3798e9cfb5aeabdf))\n\n## [6.2.0](https://github.com/nvim-neorg/neorg/compare/v6.1.0...v6.2.0) (2023-11-18)\n\n\n### Features\n\n* add `traverse-link` Neorg mode ([#1170](https://github.com/nvim-neorg/neorg/issues/1170)) ([ed25267](https://github.com/nvim-neorg/neorg/commit/ed25267eec3b08a3de8bdb4b55243f869ea4b8fd))\n* add LaTex rendering for inline equations ([#1133](https://github.com/nvim-neorg/neorg/issues/1133)) ([b5393e8](https://github.com/nvim-neorg/neorg/commit/b5393e8bdcf704f660fa86cace89033c5fc95504))\n* allow arguments for `:Neorg generate-workspace-summary` ([#1156](https://github.com/nvim-neorg/neorg/issues/1156)) ([46741ed](https://github.com/nvim-neorg/neorg/commit/46741ede577392f36cad1cb8c8e6029fabb729f6))\n* allow nested workspace summaries ([#1144](https://github.com/nvim-neorg/neorg/issues/1144)) ([a923055](https://github.com/nvim-neorg/neorg/commit/a9230559fb6871f1f62996f8e862876169432f08))\n* **hop:** feed wslview with decoded link ([c3b9653](https://github.com/nvim-neorg/neorg/commit/c3b965340f380740a12432536d2b23ee6c7564f9))\n* **metagen:** customize timezone and its format ([b458149](https://github.com/nvim-neorg/neorg/commit/b4581496328d47ab7912148ec030dcb3ec1951c4))\n* option to inject specific metadata instead of defaults ([#1128](https://github.com/nvim-neorg/neorg/issues/1128)) ([5509079](https://github.com/nvim-neorg/neorg/commit/55090798a2eed2dd00fc1b2774bc6bf309a3bd0b))\n\n\n### Bug Fixes\n\n* **dirman:** add `raw_path` option to work with arbitrary filetype ([#1143](https://github.com/nvim-neorg/neorg/issues/1143)) ([0c9f5de](https://github.com/nvim-neorg/neorg/commit/0c9f5dea0cfe8b7c3d38f26651d82624079774ed))\n* **journal:** toc reset month & add link indent ([#1165](https://github.com/nvim-neorg/neorg/issues/1165)) ([16af444](https://github.com/nvim-neorg/neorg/commit/16af444ef804aa7f099c7a5ae03640dfc2b60303))\n* remove LaTeX renderer and image.nvim integration from `core.defaults` ([5a88bcb](https://github.com/nvim-neorg/neorg/commit/5a88bcbf60590348e4196493c9c7642f23ba21d7))\n* workspace summary ignore closed files and title field of metadata tag ([#1139](https://github.com/nvim-neorg/neorg/issues/1139)) ([d081937](https://github.com/nvim-neorg/neorg/commit/d081937a00e0f0c6966116428117e159a785abb5))\n\n## [6.1.0](https://github.com/nvim-neorg/neorg/compare/v6.0.0...v6.1.0) (2023-10-29)\n\n\n### Features\n\n* support dotrepeat for `promo` and `todo_items` ([#1105](https://github.com/nvim-neorg/neorg/issues/1105)) ([2c43e6b](https://github.com/nvim-neorg/neorg/commit/2c43e6b3252af198973cbe91f8fa7a762ff61a77))\n\n\n### Bug Fixes\n\n* **calendar:** display weekdays based on `nvim_strwidth` ([5eadb3c](https://github.com/nvim-neorg/neorg/commit/5eadb3cce8ab490222d12dfbb5c86372c89a5773))\n* **calendar:** use `nvim_strwidth` for month names as well ([a081397](https://github.com/nvim-neorg/neorg/commit/a0813979663d5e55c481bb557c250b551042d115))\n* **dirman:** open index file in default workspace only if it exists ([d1bda3c](https://github.com/nvim-neorg/neorg/commit/d1bda3caf7d73ec93bed125d2d76ba32ce897789))\n* don't autoload `core.neorgcmd` nor `core.keybinds` as dependencies of other modules ([#1051](https://github.com/nvim-neorg/neorg/issues/1051)) ([62ba931](https://github.com/nvim-neorg/neorg/commit/62ba93130eb795ccc2133841ce0e541f8bc51eb7))\n* **meta:** fix treesitter deprecation warning ([#1104](https://github.com/nvim-neorg/neorg/issues/1104)) ([#1130](https://github.com/nvim-neorg/neorg/issues/1130)) ([5205f3f](https://github.com/nvim-neorg/neorg/commit/5205f3f1ed23545a3015021be11d35a012e3b02a))\n* **utils:** don't dotrepeat insert mode actions ([#1111](https://github.com/nvim-neorg/neorg/issues/1111)) ([969b3f1](https://github.com/nvim-neorg/neorg/commit/969b3f106683c66ab685ecba2a67bf11cb806785))\n\n## [6.0.0](https://github.com/nvim-neorg/neorg/compare/v5.0.0...v6.0.0) (2023-09-23)\n\n\n### ⚠ BREAKING CHANGES\n\n* adapt to new injection syntax for treesitter\n* **codebase:** make the `neorg` object local to a `core` module ([#1001](https://github.com/nvim-neorg/neorg/issues/1001))\n\n### Features\n\n* add blank lines between tangled blocks ([#958](https://github.com/nvim-neorg/neorg/issues/958)) ([1c41592](https://github.com/nvim-neorg/neorg/commit/1c41592ec975189c79987aa32228778c111eb67f))\n* **concealer:** add option for opening all folds by default ([#1049](https://github.com/nvim-neorg/neorg/issues/1049)) ([6bfcaeb](https://github.com/nvim-neorg/neorg/commit/6bfcaeb8f36e0e4d2ae52dbde5e18b39d2351d5e))\n* delimit tangle code blocks with file content ([#1014](https://github.com/nvim-neorg/neorg/issues/1014)) ([1809236](https://github.com/nvim-neorg/neorg/commit/18092365b21c73a0478b6bd6d9b3a66fd4b77a36))\n* delimit tangled code blocks with headings ([#981](https://github.com/nvim-neorg/neorg/issues/981)) ([99bfcb1](https://github.com/nvim-neorg/neorg/commit/99bfcb11dc3fbc72c08259d5516738d3a1f7bd11))\n* **document.meta:** indent items of incomplete lists/objects for nicer writing experience ([92f2e9d](https://github.com/nvim-neorg/neorg/commit/92f2e9d4a7bfdbb7ed0e9dcd9b8768db63188149))\n* **esupports.hop:** add open mode for external link target ([#1072](https://github.com/nvim-neorg/neorg/issues/1072)) ([851a3a2](https://github.com/nvim-neorg/neorg/commit/851a3a2b3cea5335fca233273d3c8861a017da14))\n* **esupports.hop:** support `os_open_link` for WSL ([#963](https://github.com/nvim-neorg/neorg/issues/963)) ([628ba9f](https://github.com/nvim-neorg/neorg/commit/628ba9f58e02db6b2818f68b62a1499c22eb9cd4))\n* **esupports:** use `wslview` to open `wsl2` files ([#1038](https://github.com/nvim-neorg/neorg/issues/1038)) ([20502e5](https://github.com/nvim-neorg/neorg/commit/20502e50e9087248f6f8ed8d29fae9c849c1c77f))\n* **itero:** allow fallback keys for when there is no object to iterate ([ba2899d](https://github.com/nvim-neorg/neorg/commit/ba2899d6580706cbf727720db2765aead9d342de))\n* **keybinds:** allow `core.itero.next-iteration` to fall back to a specific key ([51ca15b](https://github.com/nvim-neorg/neorg/commit/51ca15b13e9a7b107bef54c9bed94b5863b9c5d5))\n* **metagen:** allow falling back to the default template functions ([#1079](https://github.com/nvim-neorg/neorg/issues/1079)) ([8200ebc](https://github.com/nvim-neorg/neorg/commit/8200ebc5a5730a14efa2e47751a43539c8a16fb5))\n* **metagen:** more precise timestamp with HH:MM:SS and timezone ([#1052](https://github.com/nvim-neorg/neorg/issues/1052)) ([a8f7a9e](https://github.com/nvim-neorg/neorg/commit/a8f7a9eeef5c22eac626e7533eeee0ac9def72ad))\n\n\n### Bug Fixes\n\n* `:h neorg` not working as intended ([0b3df86](https://github.com/nvim-neorg/neorg/commit/0b3df8633cc1cbb3ffd6f34d4e9073fd6f5083ab))\n* **`:h neorg`:** make link point to correct line in specs ([#1092](https://github.com/nvim-neorg/neorg/issues/1092)) ([e20d032](https://github.com/nvim-neorg/neorg/commit/e20d032ea3c485fc499f4dbc4bf7ce6afd6767ba))\n* `folke/todo-comments.nvim` comments highlighting (again) ([#1094](https://github.com/nvim-neorg/neorg/issues/1094)) ([d8e2c8e](https://github.com/nvim-neorg/neorg/commit/d8e2c8e309c05a7db4ca84fc1216be38cf6a010f))\n* broken configuration merging in modules.lua ([#1062](https://github.com/nvim-neorg/neorg/issues/1062)) ([b4c7935](https://github.com/nvim-neorg/neorg/commit/b4c7935a0e692870f38ff34689fd900de40ea479))\n* **calendar:** call `os.date` twice to generate correct weekday ([#1058](https://github.com/nvim-neorg/neorg/issues/1058)) ([61fb605](https://github.com/nvim-neorg/neorg/commit/61fb60508516b224ec78666187e70074397b37f8))\n* **calendar:** give calendar enough space to render ([#950](https://github.com/nvim-neorg/neorg/issues/950)) ([6fece15](https://github.com/nvim-neorg/neorg/commit/6fece1546d051a5f2a2d932d5978beec1ef920ab))\n* **concealer,indent:** \"require'neorg'\" missing in v:lua call ([#1010](https://github.com/nvim-neorg/neorg/issues/1010)) ([1d3b425](https://github.com/nvim-neorg/neorg/commit/1d3b4252862cadf80751e0e03463b27a1782ce94))\n* **concealer:** avoid conflict between preset and custom icons ([9a0aab0](https://github.com/nvim-neorg/neorg/commit/9a0aab039b174625bfc4ff708ba32f3fc5713649))\n* **concealer:** do not render missing node ([#1004](https://github.com/nvim-neorg/neorg/issues/1004)) ([08c7d19](https://github.com/nvim-neorg/neorg/commit/08c7d19125f5f8aa36911bfd3ea166b650e05e07))\n* **concealer:** don't rerender at `conceallevel` change when disabled ([#1068](https://github.com/nvim-neorg/neorg/issues/1068)) ([63a7a10](https://github.com/nvim-neorg/neorg/commit/63a7a101387550a220186cab7e85df15635f3356))\n* **concealer:** more precise anticonceal feature detection ([#1056](https://github.com/nvim-neorg/neorg/issues/1056)) ([b0117a4](https://github.com/nvim-neorg/neorg/commit/b0117a40675398cb6b7f0967a52e148d5ddb6f42))\n* **concealer:** revert a wrong fix, make luacheck ignore empty if branch instead (supercedes [#1080](https://github.com/nvim-neorg/neorg/issues/1080)) ([0c82917](https://github.com/nvim-neorg/neorg/commit/0c82917b89a187662cf8c1f5fc3a17153866df9b))\n* **concealer:** tolerate duplicate marks caused by undo during rendering ([#1015](https://github.com/nvim-neorg/neorg/issues/1015)) ([44bb353](https://github.com/nvim-neorg/neorg/commit/44bb3533465d30062b28a334115e37dbbe7e5118))\n* **core:** assign custom field ([4b057ad](https://github.com/nvim-neorg/neorg/commit/4b057ad071f0e395fb1e983c9611913e9b46108f))\n* **dirman:** correctly create nested directory ([#1061](https://github.com/nvim-neorg/neorg/issues/1061)) ([4f0888b](https://github.com/nvim-neorg/neorg/commit/4f0888bdf98f7b1eeb96365aca17aa08ba4a07ea))\n* **docgen:** `neorg.core` not found ([bb29db9](https://github.com/nvim-neorg/neorg/commit/bb29db9320b353da8abdfaebcba74a0a1d6e1a20))\n* **docgen:** inline `esupports.metagen` template function definitions ([#945](https://github.com/nvim-neorg/neorg/issues/945)) ([a993b35](https://github.com/nvim-neorg/neorg/commit/a993b357ab86e153ecd50e2d4b704b8dcffedc1f))\n* don't use deprecated `query.get_node_text()` call ([#1067](https://github.com/nvim-neorg/neorg/issues/1067)) ([7248c34](https://github.com/nvim-neorg/neorg/commit/7248c347704d658daf0fa0a84706c120e92eb1a5))\n* error in loading preventing wiki from generating ([2745ee1](https://github.com/nvim-neorg/neorg/commit/2745ee1371c1029171bb98f2d9fb258e688d2c20))\n* fetched get_language_list from utils ([#1003](https://github.com/nvim-neorg/neorg/issues/1003)) ([3db1001](https://github.com/nvim-neorg/neorg/commit/3db10018e8893aee47f3b5eb9f4d7440f8db5136))\n* **highlights:** always try to attach highlights when triggered ([#1025](https://github.com/nvim-neorg/neorg/issues/1025)) ([31b3bfd](https://github.com/nvim-neorg/neorg/commit/31b3bfddfc1a4e426b41879bdb1a039babc554e3))\n* indents within `document.meta` would not work ([b14334e](https://github.com/nvim-neorg/neorg/commit/b14334e39dcf6d8a6edb18547b7c4580387dce63))\n* issue a more friendly error message when user loads tempus pre-Neovim `0.10.0` ([#1035](https://github.com/nvim-neorg/neorg/issues/1035)) ([333a1fd](https://github.com/nvim-neorg/neorg/commit/333a1fd67aad3dee49305b0278bd59f8ae740f13))\n* **journal:** expand entry path correctly (fixes [#780](https://github.com/nvim-neorg/neorg/issues/780)) ([#995](https://github.com/nvim-neorg/neorg/issues/995)) ([e76f0cb](https://github.com/nvim-neorg/neorg/commit/e76f0cb6b3ae5e990052343ebb73a5c8d8cac783))\n* **journal:** Remove condition from 'toc' subcommand (fixes [#597](https://github.com/nvim-neorg/neorg/issues/597)) ([#996](https://github.com/nvim-neorg/neorg/issues/996)) ([99f33e0](https://github.com/nvim-neorg/neorg/commit/99f33e08fe074126b491e02854e5d00dab10f5ae))\n* **looking-glass:** ensure both the target buffer and the source are loaded before pursuing any operations ([fba064d](https://github.com/nvim-neorg/neorg/commit/fba064db88eae3419d20ce35cf3961d02c355a8f))\n* **maneoeuvre:** `lib` -&gt; `utils` ([0949a4a](https://github.com/nvim-neorg/neorg/commit/0949a4a2816ef19cb19e0ef8d483d3410dd0895a))\n* On close of TOC, only delete buffer if it exists ([#978](https://github.com/nvim-neorg/neorg/issues/978)) ([32bae17](https://github.com/nvim-neorg/neorg/commit/32bae172814611f82e90b696b72cac99ff8de0e9))\n* **presenter:** ensure module.private is not overriden ([#1037](https://github.com/nvim-neorg/neorg/issues/1037)) ([c9dd9f7](https://github.com/nvim-neorg/neorg/commit/c9dd9f7d506717b00e99409e4088e5b739c36b39))\n* replace `get_filetype` with `vim.filetype.match` ([#982](https://github.com/nvim-neorg/neorg/issues/982)) ([4e6dbb1](https://github.com/nvim-neorg/neorg/commit/4e6dbb184442bc33e20ce760f093c07b32ad4128))\n* **summary:** escape ws_root special characters ([#1012](https://github.com/nvim-neorg/neorg/issues/1012)) ([32abc0d](https://github.com/nvim-neorg/neorg/commit/32abc0da29dd5bf4b42d340810b64754fd7a37b8))\n* **tags:** make new tags work with updated neorg help document ([#994](https://github.com/nvim-neorg/neorg/issues/994)) ([3f946f8](https://github.com/nvim-neorg/neorg/commit/3f946f8814a59ac16baaf4bc1dd0f4aca3807736))\n* **tangle:** accessing unused variable ([0f37ab8](https://github.com/nvim-neorg/neorg/commit/0f37ab86ea82838ddd9feeab94986d6d72d0d85a))\n* **toc:** preserve heading hierarchy ([#1053](https://github.com/nvim-neorg/neorg/issues/1053)) ([1c1060f](https://github.com/nvim-neorg/neorg/commit/1c1060f0d187cd0939b05c1310bb58911e84bc22))\n* **ui:** remove possible ui noise caused by user's opts ([68eae35](https://github.com/nvim-neorg/neorg/commit/68eae352bf4b936e667b5eb4d454d4d280d2286d))\n* Update `get_username` call ([#1005](https://github.com/nvim-neorg/neorg/issues/1005)) ([93bf092](https://github.com/nvim-neorg/neorg/commit/93bf092a817df07f75cee578c74b4eabab3b7c87))\n\n\n### Code Refactoring\n\n* adapt to new injection syntax for treesitter ([064f8f6](https://github.com/nvim-neorg/neorg/commit/064f8f65dd32f4fe728e76acfa3e4e153b121147))\n* **codebase:** make the `neorg` object local to a `core` module ([#1001](https://github.com/nvim-neorg/neorg/issues/1001)) ([5706f1e](https://github.com/nvim-neorg/neorg/commit/5706f1efdcf55f273de8f52deeb35375a303be72))\n\n## [5.0.0](https://github.com/nvim-neorg/neorg/compare/v4.6.0...v5.0.0) (2023-06-07)\n\n\n### ⚠ BREAKING CHANGES\n\n* **core.ui:** don't use old Neovim APIs, fix errors when using `<LocalLeader>nn`\n* **core.highlights:** remove `todo_items_match_color` option\n* **highlights:** simplify highlights for performance reasons\n* **summary:** fix norg links, use first heading as title if found ([#928](https://github.com/nvim-neorg/neorg/issues/928))\n* **core:** remove `real`/imaginary components of modules, improve startup time, remove `imports` from `module.setup`\n* remove the `core.news` module\n* **concealer:** rewrite for performance and stability ([#834](https://github.com/nvim-neorg/neorg/issues/834))\n* since 5.0 do not longer warn about deprecated `core.norg.*` modules\n* move to new/improved metadata parser, change highlight queries\n\n### Features\n\n* add extra nesting level, make icons specific to non-anticonceal usage ([84ea792](https://github.com/nvim-neorg/neorg/commit/84ea792d97977b98caab8e63538d3286f58b2b1b))\n* add highlights to `&variable&`s ([#710](https://github.com/nvim-neorg/neorg/issues/710)) ([97080f7](https://github.com/nvim-neorg/neorg/commit/97080f798e0872a52510e33cf7f9064af5501da3))\n* add neorg to luarocks ([4fceaa6](https://github.com/nvim-neorg/neorg/commit/4fceaa67656a0ebf17daeac133db2387df44552a))\n* conceal the `{* }` parts of links ([729e7ac](https://github.com/nvim-neorg/neorg/commit/729e7ac46b5feac7f97826f755695f0e2c4799f9))\n* **concealer:** add more icon generators ([49b9788](https://github.com/nvim-neorg/neorg/commit/49b9788a4988235d4357f8ae87d3ce82ee39302e))\n* **concealer:** add numeric anticonceal if supported ([55feccf](https://github.com/nvim-neorg/neorg/commit/55feccf37df2b1143ea85151b9430149c617aa99))\n* **concealer:** rewrite for performance and stability ([#834](https://github.com/nvim-neorg/neorg/issues/834)) ([151c033](https://github.com/nvim-neorg/neorg/commit/151c0337684a30ab8a9b31683b7a2fa28b0a15b0))\n* **esupports.hop:** link jump to line + fixes + refactoring ([#903](https://github.com/nvim-neorg/neorg/issues/903)) ([49610cd](https://github.com/nvim-neorg/neorg/commit/49610cdee13050fc872cc006a690a911dda68413))\n* **indent:** add `dedent_excess` configuration option ([#624](https://github.com/nvim-neorg/neorg/issues/624)) ([66d5a22](https://github.com/nvim-neorg/neorg/commit/66d5a2251b0871aa037135644b6fca2a856de5b4))\n* **itero:** don't start newline on empty line ([#911](https://github.com/nvim-neorg/neorg/issues/911)) ([4c76b74](https://github.com/nvim-neorg/neorg/commit/4c76b741a0003417ed38bf0f43727810c27fb042))\n* **keybinds.lua:** add `desc` fields to task keybinds ([#926](https://github.com/nvim-neorg/neorg/issues/926)) ([978fdc1](https://github.com/nvim-neorg/neorg/commit/978fdc1dede2325374dc5a32db10a4b6dad87bf0))\n* **keybinds.lua:** add descriptions to all keybinds ([bb50538](https://github.com/nvim-neorg/neorg/commit/bb505384372b87ae6193c9ceeb02312d50f0df3c))\n* move to new/improved metadata parser, change highlight queries ([962e45a](https://github.com/nvim-neorg/neorg/commit/962e45a29f1d61f685a5bacb9a2b00eb0a11d9c5))\n* **promo:** promote/demote prefix without following text ([#912](https://github.com/nvim-neorg/neorg/issues/912)) ([544bb06](https://github.com/nvim-neorg/neorg/commit/544bb06c28956c4e21b6d6d32b1b3ea7415be7cd))\n\n\n### Bug Fixes\n\n* **completion:** selected completion engine not being engaged ([474af82](https://github.com/nvim-neorg/neorg/commit/474af829b0f3e25e09e68d2842ffcb6ca24d359b))\n* **concealer:** disable assertion for prefixes until parser changes ([#932](https://github.com/nvim-neorg/neorg/issues/932)) ([92aa737](https://github.com/nvim-neorg/neorg/commit/92aa7373ccdfc5c9d1616027173237ee9cc4098e))\n* **concealer:** do not listen vimleavepre ([#920](https://github.com/nvim-neorg/neorg/issues/920)) ([865224a](https://github.com/nvim-neorg/neorg/commit/865224a59982a148e9b11647d23e2de61272c42c))\n* **concealer:** fix concealing in anchors, don't error on broken config ([#923](https://github.com/nvim-neorg/neorg/issues/923)) ([f448b58](https://github.com/nvim-neorg/neorg/commit/f448b581c6a6cf2747b33ff6bfece6c21c72b03f))\n* **concealer:** minor fixes, plus wiki error fix ([#916](https://github.com/nvim-neorg/neorg/issues/916)) ([5629898](https://github.com/nvim-neorg/neorg/commit/5629898cf24bf25a39723e4113ce87a08f0d9dc1))\n* **concealer:** record cursor upon init to fix first line conceal ([#924](https://github.com/nvim-neorg/neorg/issues/924)) ([44ee0cb](https://github.com/nvim-neorg/neorg/commit/44ee0cb8db3d655d45d5ca5cedc2b0745b232659))\n* **core.highlights:** fix disappearing highlights when opening up norg files ([9db5645](https://github.com/nvim-neorg/neorg/commit/9db56453e2f7f6bc7e81baa338e09a2565ccaff1))\n* **core.highlights:** wrongly placed bracket ([1886d36](https://github.com/nvim-neorg/neorg/commit/1886d363e9f397251060a4d6681fa975ef9d3b64))\n* **core.summary:** bugs + flexibility around incomplete metadata ([#927](https://github.com/nvim-neorg/neorg/issues/927)) ([30343db](https://github.com/nvim-neorg/neorg/commit/30343dbdcdb511ecb6f484c46a9ae6f20a66ff7d))\n* **docgen:** don't fail on mixed-type tables (lists and dictionaries at the same time) ([1afcaf8](https://github.com/nvim-neorg/neorg/commit/1afcaf804bae0048bfca1c0d49b69c968f2c187b))\n* **docgen:** fix incorrect markdown indentation in wiki ([2bf6e63](https://github.com/nvim-neorg/neorg/commit/2bf6e63c299903d6e83fe14a521987dd0745efb0))\n* **docgen:** propagate docgen error exit code ([#917](https://github.com/nvim-neorg/neorg/issues/917)) ([0e97976](https://github.com/nvim-neorg/neorg/commit/0e97976417d3e387d9be2f4fb42cd66c72254b6b))\n* **highlights:** assert on treesitter being enabled ([#914](https://github.com/nvim-neorg/neorg/issues/914)) ([330f04e](https://github.com/nvim-neorg/neorg/commit/330f04ef693fb379c5ff199a05813e270718c850))\n* **highlights:** attempt to reenable highlighting when none is found ([d1fb8c9](https://github.com/nvim-neorg/neorg/commit/d1fb8c94c57161e675402ec06ed80dc9223df655))\n* **presenter:** errors on startup ([ea5fe1b](https://github.com/nvim-neorg/neorg/commit/ea5fe1b51d0a5b9f33a2fdd81906c5661b9198d6))\n* **summary:** fix norg links, use first heading as title if found ([#928](https://github.com/nvim-neorg/neorg/issues/928)) ([6f893a2](https://github.com/nvim-neorg/neorg/commit/6f893a205a7543f2b7390b31176cf6e4ee2442c0))\n* **todo_items:** don't look at child if parent is todo ([#909](https://github.com/nvim-neorg/neorg/issues/909)) ([8e3bcb2](https://github.com/nvim-neorg/neorg/commit/8e3bcb295a834dd57ba1d41ef2903f3dcc53a70e))\n\n\n### Performance Improvements\n\n* **core.highlights:** remove `todo_items_match_color` option ([7b5d550](https://github.com/nvim-neorg/neorg/commit/7b5d550843a3a2576aa95a90972c2ffc0e5c682f))\n* **core.neorgcmd:** unnecessary `vim.tbl_deep_extend` ([71d291f](https://github.com/nvim-neorg/neorg/commit/71d291f97dc7e7fab4ca5740181e25f6d50a6e2d))\n* **core.promo:** don't check `v.count`, use `v.count1` instead ([ca98238](https://github.com/nvim-neorg/neorg/commit/ca982387110ce2b796e585a10cd6f6922cec6c69))\n* **events:** don't deepcopy a table on each new event ([12198ef](https://github.com/nvim-neorg/neorg/commit/12198efd76ec057be207e567dbeed3c8022d6eb6))\n* **hop:** load plenary only when required, remove startup hiccup ([3caca5a](https://github.com/nvim-neorg/neorg/commit/3caca5ac209aa8098a355837b5c4696d16804e19))\n\n\n### Code Refactoring\n\n* **core.ui:** don't use old Neovim APIs, fix errors when using `&lt;LocalLeader&gt;nn` ([bbb25ff](https://github.com/nvim-neorg/neorg/commit/bbb25ffa380a2c159b0d301df9b81a8fcf3ab67a))\n* **core:** remove `real`/imaginary components of modules, improve startup time, remove `imports` from `module.setup` ([593e9b2](https://github.com/nvim-neorg/neorg/commit/593e9b2a0826dfb8068a02277f4a45db00573e9a))\n* **highlights:** simplify highlights for performance reasons ([f1ecd61](https://github.com/nvim-neorg/neorg/commit/f1ecd613d9c2911c7f7d5abd7f6f471614d05518))\n* remove the `core.news` module ([1b9f8da](https://github.com/nvim-neorg/neorg/commit/1b9f8da57fb3e0bab9d1594fce87808ead8d650d))\n* since 5.0 do not longer warn about deprecated `core.norg.*` modules ([19e0e8a](https://github.com/nvim-neorg/neorg/commit/19e0e8a3e983bf0a87c5c791863d4a480f0ff54c))\n\n## [4.6.0](https://github.com/nvim-neorg/neorg/compare/v4.5.0...v4.6.0) (2023-05-25)\n\n\n### Features\n\n* **todo-items:** add missing \"need input\" icon and action ([#896](https://github.com/nvim-neorg/neorg/issues/896)) ([4cb0fa9](https://github.com/nvim-neorg/neorg/commit/4cb0fa9e56cf16672c258d1d97545d0526b506b5))\n\n\n### Bug Fixes\n\n* **esupports:** use structured api to avoid injection ([#899](https://github.com/nvim-neorg/neorg/issues/899)) ([e50b8ae](https://github.com/nvim-neorg/neorg/commit/e50b8aecb61dae1dd726fe00f40d3a554ba1b694))\n* **tempus:** supply unprovided parameters from the current date when converting to `osdate` (supercedes [#897](https://github.com/nvim-neorg/neorg/issues/897)) ([f367451](https://github.com/nvim-neorg/neorg/commit/f36745161d82067e0f26865d93858fd3a15d8ad4))\n\n## [4.5.0](https://github.com/nvim-neorg/neorg/compare/v4.4.1...v4.5.0) (2023-05-24)\n\n\n### Features\n\n* add colouring to TODO items ([238152a](https://github.com/nvim-neorg/neorg/commit/238152ab40ec1fb293fae75744942146876ed08f))\n\n\n### Bug Fixes\n\n* **metagen:** update generation to use user config for `updated` tag ([#882](https://github.com/nvim-neorg/neorg/issues/882)) ([6ed0f3a](https://github.com/nvim-neorg/neorg/commit/6ed0f3aa088e7b3141f01e3a82f3ec6517c34485)), closes [#865](https://github.com/nvim-neorg/neorg/issues/865)\n* TSInstall issues on macOS, hopefully once and for good ([#891](https://github.com/nvim-neorg/neorg/issues/891)) ([4988a6f](https://github.com/nvim-neorg/neorg/commit/4988a6f9166b6ac7b9ba5115e61dc3a2b13e820c))\n\n## [4.4.1](https://github.com/nvim-neorg/neorg/compare/v4.4.0...v4.4.1) (2023-05-17)\n\n\n### Bug Fixes\n\n* **tempus:** paste correct weekday from calendar ([ba54231](https://github.com/nvim-neorg/neorg/commit/ba54231e14a31c0571ff7baa4828de121a5e3072))\n* **tempus:** properly handle conversions w.r.t Sun-Sat/Mon-Sun ([e39fa1b](https://github.com/nvim-neorg/neorg/commit/e39fa1b1626fc6f4bb9f4695b15d7065561c2567))\n\n## [4.4.0](https://github.com/nvim-neorg/neorg/compare/v4.3.0...v4.4.0) (2023-05-16)\n\n\n### Features\n\n* **journal:** allow `custom` to take in no arguments, in which case ([ea0497a](https://github.com/nvim-neorg/neorg/commit/ea0497aea783507ce640e909b6764be4fcd5a388))\n\n\n### Bug Fixes\n\n* **promo:** don't add whitespace to empty lines ([#852](https://github.com/nvim-neorg/neorg/issues/852)) ([a7291f4](https://github.com/nvim-neorg/neorg/commit/a7291f4662664d0c3be3016adff6767dc52f907d))\n* **tempus:** don't use the `re` module if it doesn't exist ([#872](https://github.com/nvim-neorg/neorg/issues/872)) ([3c99638](https://github.com/nvim-neorg/neorg/commit/3c99638db0ce4293e221216bdda03a55da6ad82b))\n\n## [4.3.0](https://github.com/nvim-neorg/neorg/compare/v4.2.0...v4.3.0) (2023-05-15)\n\n\n### Features\n\n* **calendar:** add `t` command for \"today\" ([e53a509](https://github.com/nvim-neorg/neorg/commit/e53a5099b5725162c8f0a626823cac4819a9427d))\n* **hop:** allow users to jump to timestamps ([22b12fb](https://github.com/nvim-neorg/neorg/commit/22b12fb2301582fd9552ab10ac0c934cda4d0a14))\n\n\n### Bug Fixes\n\n* **hop:** assume &lt;current-day&gt; when some parameters to dates are not supplied ([65bf064](https://github.com/nvim-neorg/neorg/commit/65bf06493ecb411b1589ad345771ae29aa17cd33))\n* **tempus:** days like `4th`/`2nd` would not get parsed properly ([7368a8a](https://github.com/nvim-neorg/neorg/commit/7368a8ae10a0bab32729bd00dcac6f24cb55a8ef))\n\n## [4.2.0](https://github.com/nvim-neorg/neorg/compare/v4.1.1...v4.2.0) (2023-05-15)\n\n\n### Features\n\n* **tempus:** add `,id` (insert date) keybinding ([34f13ba](https://github.com/nvim-neorg/neorg/commit/34f13ba253c160e72ef7817a950508430ed050d1))\n* **tempus:** add insert mode `&lt;M-d&gt;` keybind to insert a date ([b420f69](https://github.com/nvim-neorg/neorg/commit/b420f69602b23fa8fc2f7f6526f49838f9521b10))\n* **tempus:** allow dates to be converted to norg-compatible dates with `tostring()` ([3ec5f96](https://github.com/nvim-neorg/neorg/commit/3ec5f96dfd673c2c2a34b09748518accf61ec677))\n\n\n### Bug Fixes\n\n* don't allow tempus to load unless the Neovim ver is at least 0.10.0 ([c4429fa](https://github.com/nvim-neorg/neorg/commit/c4429fa1e1eb0c3c5652495b00aa4e1c56068914))\n* **tempus:** do not assume `osdate` has all fields set ([c37a104](https://github.com/nvim-neorg/neorg/commit/c37a104c992326f8924de783d667f7c4c34f92b7))\n\n## [4.1.1](https://github.com/nvim-neorg/neorg/compare/v4.1.0...v4.1.1) (2023-05-15)\n\n\n### Bug Fixes\n\n* remove calendar as a dependency of `core.ui`, fix errors for people not on nightly ([cd26a22](https://github.com/nvim-neorg/neorg/commit/cd26a220e999cc9103a2502299d16ae8e6fab4d9))\n\n## [4.1.0](https://github.com/nvim-neorg/neorg/compare/v4.0.1...v4.1.0) (2023-05-14)\n\n\n### Features\n\n* add `core.tempus` module for date management ([b73ec2f](https://github.com/nvim-neorg/neorg/commit/b73ec2f5e1b11864ca0628a842a53a617d5851ce))\n* add left-right cursor movement ([ea588bb](https://github.com/nvim-neorg/neorg/commit/ea588bbc2cabe37f90652a8cb49bf8b286498d2a))\n* add skeleton for the calendar UI element ([3c99106](https://github.com/nvim-neorg/neorg/commit/3c99106d64792533a3cf10ac6ef20a089e94c1ff))\n* **calendar:** add `?` help page for custom input ([211b0ba](https://github.com/nvim-neorg/neorg/commit/211b0ba61b5cf8f4520b5e03f5235f6de87e4417))\n* **calendar:** add `$` and `0`/`_` navigation keybinds ([0061928](https://github.com/nvim-neorg/neorg/commit/006192808d436c27f8ceca0fffcc4a238ec402a7))\n* **calendar:** add `m`/`M`, `L`/`H` and `y`/`Y` keybinds for the monthly view ([9bf562d](https://github.com/nvim-neorg/neorg/commit/9bf562d4633abac71b749ad7380cfe010a4c3bd7))\n* **calendar:** add basic help popup when `?` is invoked ([779d089](https://github.com/nvim-neorg/neorg/commit/779d089e17139acfdd2a4988c34eea892f29a475))\n* **calendar:** allow many simultaneous calendars ([f816fe7](https://github.com/nvim-neorg/neorg/commit/f816fe77ef2abecff9e98d8d35ff48a453317cf0))\n* **calendar:** generalize functions even further, allow for offsets ([d857c34](https://github.com/nvim-neorg/neorg/commit/d857c34fe7a4645501551f2b66dd7915b9575b4f))\n* **calendar:** implement basic `i` functionality ([6713f40](https://github.com/nvim-neorg/neorg/commit/6713f40d5d1f9e7a0e8b80ffdc82d4fff79c16c0))\n* **calendar:** render as many months as is possible on screen ([fa23767](https://github.com/nvim-neorg/neorg/commit/fa237674cf75bf2bbc62a438b1606b65cc277ebd))\n* **core.ui.calendar:** add day of the month rendering ([8bc3364](https://github.com/nvim-neorg/neorg/commit/8bc3364f306d5df528193a8ca68fa8b4a45701ef))\n* **core.ui.calendar:** add static calendar ui ([adbb415](https://github.com/nvim-neorg/neorg/commit/adbb4151677bf22c809f9b6dfd35de5e07da6c7a))\n* **core.ui.calendar:** highlight the current day differently ([eada386](https://github.com/nvim-neorg/neorg/commit/eada386cc79c122b648580de50b1f825b74a9627))\n* **core.ui.calendar:** implement more of the barebones UI ([364f44a](https://github.com/nvim-neorg/neorg/commit/364f44a7d1179d5aa98d1f4ff6b4b6b1b6078bd3))\n* **core.ui.calendar:** make the calendar display full month names ([c6cc059](https://github.com/nvim-neorg/neorg/commit/c6cc059992c812712c9a2bb4075b2d9b31f84f5c))\n* **core.ui:** let `create_split` take in a `height` variable ([7dbbe9d](https://github.com/nvim-neorg/neorg/commit/7dbbe9d236596d8990827e717ea892cd98e79b23))\n* correctly handle year boundaries ([58b55e1](https://github.com/nvim-neorg/neorg/commit/58b55e16366ecd431bece7ba4d42d512b21b972e))\n* implement `render_month` function ([343fb8d](https://github.com/nvim-neorg/neorg/commit/343fb8d02422fe2f2a3c791f2bdba0be95c3c96b))\n* place cursor over current day when creating calendar ([3ce268b](https://github.com/nvim-neorg/neorg/commit/3ce268b703d321561b86e546c7633326b39fa494))\n* **tempus:** add `to_lua_date` function ([ef62e53](https://github.com/nvim-neorg/neorg/commit/ef62e5308c684468a822684382d14de8f8f63193))\n\n\n### Bug Fixes\n\n* **calendar:** allow the view to be written to on rerender ([8e247d4](https://github.com/nvim-neorg/neorg/commit/8e247d414bcb0d1123b2b12c7ff29bdf36c50cbd))\n* **calendar:** fix incorrect movement with `H` across boundaries of months with different lengths ([48face2](https://github.com/nvim-neorg/neorg/commit/48face25855d7844302b13a125363c30b8a6fe9a))\n* **calendar:** fix rest of highlight groups ([ead4c4c](https://github.com/nvim-neorg/neorg/commit/ead4c4c53769839b5063fab71ebb92d155d53676))\n* **calendar:** if another calendar is open then close it instead of erroring ([9751e7d](https://github.com/nvim-neorg/neorg/commit/9751e7d62af0b7e49ff788058154b966be205e2e))\n* **calendar:** make distance between each month uniform and support modifying the distance between each month ([746354d](https://github.com/nvim-neorg/neorg/commit/746354dea70e9657f61531375329e407e7f5a203))\n* **calendar:** make month rendering work again ([164028f](https://github.com/nvim-neorg/neorg/commit/164028fd621e3c5b56603d88d6d5e2ba5db51d42))\n* **calendar:** overlapping month names in the calendar view ([709cf78](https://github.com/nvim-neorg/neorg/commit/709cf78410b6ea631192ad004d3f2b83761f9953))\n* **calendar:** prevent the buffer from being modifiable after it has been filled ([351e103](https://github.com/nvim-neorg/neorg/commit/351e10326e0e2bb6166e165ddb6598e917e6d25c))\n* **calendar:** properly display \"today's day\" in the calendar view ([74ee71a](https://github.com/nvim-neorg/neorg/commit/74ee71a446662f92afa3cbd49f6c980bdf25ae92))\n* **calendar:** reversed namespace names ([77b214c](https://github.com/nvim-neorg/neorg/commit/77b214cef220580cdcf527265a15ef980e7bcaf3))\n* **core.ui.calendar:** logic error when parsing virt_text length for `set_logical_extmark` ([d5b29ee](https://github.com/nvim-neorg/neorg/commit/d5b29eea8e09d7bd0add778c6818539719914301))\n* **core.ui.calendar:** wrong extmark being queried in month render routine ([46624b9](https://github.com/nvim-neorg/neorg/commit/46624b9a02e0d0e928026a0fd4852c4dd3ca7e0d))\n\n## [4.0.1](https://github.com/nvim-neorg/neorg/compare/v4.0.0...v4.0.1) (2023-05-11)\n\n\n### Bug Fixes\n\n* **highlights.scm:** free form open/close chars would not be concealed ([5de014e](https://github.com/nvim-neorg/neorg/commit/5de014e7cc3dc6eed0a62854fe8ba58f664d97ea))\n* **qol.toc:** display headings with TODO statuses unless the status is \"cancelled\" ([2e44346](https://github.com/nvim-neorg/neorg/commit/2e44346813310de9afc411e2348cf2be8540f70c))\n* stop syntax processing if a buffer is already closed ([#859](https://github.com/nvim-neorg/neorg/issues/859)) ([cc2834a](https://github.com/nvim-neorg/neorg/commit/cc2834ae2beb2d5baa75d15848a94dae022faa2c))\n\n## [4.0.0](https://github.com/nvim-neorg/neorg/compare/v3.2.2...v4.0.0) (2023-05-05)\n\n\n### ⚠ BREAKING CHANGES\n\n* move all `gt*` keybinds to `<LocalLeader>t*`\n* remove `core.news`\n\n### Features\n\n* add basic cheatsheet (viewable via `:h neorg-cheatsheet`) ([d3e37a6](https://github.com/nvim-neorg/neorg/commit/d3e37a681743181a34dcfa7adb6ec61fb5aeb63c))\n* **keybinds:** warn when a deprecated keybind is used (will be removed with `5.0`) ([e20d3c3](https://github.com/nvim-neorg/neorg/commit/e20d3c324b091cac29ccd7ec8431d24aa9b792c8))\n\n\n### Bug Fixes\n\n* **concealer:** buggy debounce logic causing visual artifacts (especially on the first line of a buffer) ([45388fc](https://github.com/nvim-neorg/neorg/commit/45388fc0478e8d1273bd80789e7e1af1df76458f))\n* **concealer:** stop concealer if buffer is not loaded ([#836](https://github.com/nvim-neorg/neorg/issues/836)) ([6aa9fd3](https://github.com/nvim-neorg/neorg/commit/6aa9fd303c807ed1ca3fb15cdeab1e322d02fd31))\n* **dirman.expand_path:** search for both `$/` and `$\\` in links to support windows paths ([#830](https://github.com/nvim-neorg/neorg/issues/830)) ([160d40f](https://github.com/nvim-neorg/neorg/commit/160d40f5261be5149842942adbf260d6e359d9ec))\n* **esupports.hop:** anchors to files woul dresult in a \"link not found\" ([#688](https://github.com/nvim-neorg/neorg/issues/688)) ([3009adf](https://github.com/nvim-neorg/neorg/commit/3009adf2cf48aedcbb309d0765e0fbbb64a0fdf4))\n* **keybinds.lua:** remove dead `toc` keybinds ([06666f2](https://github.com/nvim-neorg/neorg/commit/06666f298e146d758d691366ca3465a3bd1e3f7f))\n\n\n### Code Refactoring\n\n* move all `gt*` keybinds to `&lt;LocalLeader&gt;t*` ([f67110d](https://github.com/nvim-neorg/neorg/commit/f67110d11d37fde09756eb2de8a1814d04a4a03b))\n* remove `core.news` ([4086d9f](https://github.com/nvim-neorg/neorg/commit/4086d9f17d823cfe5a13e7b12b30e13b5d3b796d))\n\n## [3.2.2](https://github.com/nvim-neorg/neorg/compare/v3.2.1...v3.2.2) (2023-04-27)\n\n\n### Bug Fixes\n\n* **core.ui:** clear the `winbar` option in Neorg popups to prevent \"not enough room\" errors ([fcebf9f](https://github.com/nvim-neorg/neorg/commit/fcebf9f6caf0667f99b1481e2c0a49f0eeb68fe9))\n* **esupports.hop:** broken definitions and footnotes ([#733](https://github.com/nvim-neorg/neorg/issues/733)) ([94cf7d2](https://github.com/nvim-neorg/neorg/commit/94cf7d2889b386ce1313e80c8c04adf18872c028))\n\n## [3.2.1](https://github.com/nvim-neorg/neorg/compare/v3.2.0...v3.2.1) (2023-04-27)\n\n\n### Bug Fixes\n\n* **export:** `gsub` export links that contain `#`, `?`. closes [#807](https://github.com/nvim-neorg/neorg/issues/807) ([#816](https://github.com/nvim-neorg/neorg/issues/816)) ([7f3a3b8](https://github.com/nvim-neorg/neorg/commit/7f3a3b850c8d4b73e7f85971aae2a96162bcb150))\n* **export:** markdown export for horizontal_line ([#820](https://github.com/nvim-neorg/neorg/issues/820)) ([2178447](https://github.com/nvim-neorg/neorg/commit/217844796e00a1cea7c051435f9c49bee25e7caa))\n\n## [3.2.0](https://github.com/nvim-neorg/neorg/compare/v3.1.0...v3.2.0) (2023-04-22)\n\n\n### Features\n\n* add `core.pivot` for toggling list types ([cbf383f](https://github.com/nvim-neorg/neorg/commit/cbf383ff4eca0e23a24d4244af20bed415ed400c))\n* **keybinds:** add default keybinds for `core.pivot` ([2f49628](https://github.com/nvim-neorg/neorg/commit/2f496283504dcfb30d9ee60101a8290e743c1753))\n* **pivot:** add `core.pivot.invert-list-type` keybind ([2d0446a](https://github.com/nvim-neorg/neorg/commit/2d0446a2d8e3789bbd17bbb3cb97e73befccb327))\n\n\n### Bug Fixes\n\n* **core.summary:** wrong module name in header, wrong internal command names ([a046900](https://github.com/nvim-neorg/neorg/commit/a0469001430a68f521d3292f8a8252655cfda941))\n* **docgen:** installation documentation link for wiki ([ba8b31d](https://github.com/nvim-neorg/neorg/commit/ba8b31dc2491f80b9f65fadbafdfd94d6ef26988)), closes [#548](https://github.com/nvim-neorg/neorg/issues/548)\n* **summary:** broken wiki entry ([69fbabf](https://github.com/nvim-neorg/neorg/commit/69fbabfb5764cd164453a764174cf5cfa813ae60))\n\n## [3.1.0](https://github.com/nvim-neorg/neorg/compare/v3.0.0...v3.1.0) (2023-04-19)\n\n\n### Features\n\n* warn access to `core.norg` modules instead of breaking ([ed761a5](https://github.com/nvim-neorg/neorg/commit/ed761a5c5a9100861034b31978049401444fd6fb))\n\n## [3.0.0](https://github.com/nvim-neorg/neorg/compare/v2.0.1...v3.0.0) (2023-04-19)\n\n\n### ⚠ BREAKING CHANGES\n\n* move all `core.norg.*` modules into `core.*`\n* **Makefile:** remove `install_pre_commit` target\n* move `core.norg.dirman.summary` -> `core.summary`\n* **summary:** refactor of the `core.norg.dirman.summary` module\n* **docgen:** wipe whole wiki on every reparse\n\n### Features\n\n* add `dirman.summary` module ([#750](https://github.com/nvim-neorg/neorg/issues/750)) ([93c40f2](https://github.com/nvim-neorg/neorg/commit/93c40f2e38a0770e9ce95787c8363320344a87c3))\n* add `Home.md` generation capability ([6bdf557](https://github.com/nvim-neorg/neorg/commit/6bdf557ece33850f9733dddc343369d743a51564))\n* **ci:** add `version_in_code.yml` workflow ([5746245](https://github.com/nvim-neorg/neorg/commit/5746245756bac83fcf02338c93bc87f6089e2bf3))\n* cleanup, add document comments to all modules, add more error checks ([81284c1](https://github.com/nvim-neorg/neorg/commit/81284c1e2f6e441f6532678b76ff5378396dda2c))\n* **config.lua:** add `norg_version`, bump `version` to `3.0.0` ([8d76723](https://github.com/nvim-neorg/neorg/commit/8d767232a571513a3ab8c5c14ddc6f26d09aa98a))\n* **core.integrations.treesitter:** Return all same attributes of a tag ([bedf13d](https://github.com/nvim-neorg/neorg/commit/bedf13dbcef63099a52dd4f160d90c46fc1de440))\n* **dirman:** add new `use_popup` option for `dirman` ([#743](https://github.com/nvim-neorg/neorg/issues/743)) ([6350254](https://github.com/nvim-neorg/neorg/commit/63502544afde1c15d79ce097ad1928314cb8c7cd))\n* **docgen:** add `module` page generator ([17496a8](https://github.com/nvim-neorg/neorg/commit/17496a8e975f1bd9d896d7cc78a6e61d1a131245))\n* **docgen:** add basic rendering skeleton logic ([215719e](https://github.com/nvim-neorg/neorg/commit/215719ece560400592c2fef2ed75ab57430baf9b))\n* **docgen:** add comment integrity checking logic ([799886f](https://github.com/nvim-neorg/neorg/commit/799886f7ba5a072a453d5a90708686109ce4fa21))\n* **docgen:** allow strings as table keys ([4adf04e](https://github.com/nvim-neorg/neorg/commit/4adf04e05d98b6bcb6a3aac4cab60d64fd0d86f9))\n* **docgen:** auto-open &lt;details&gt; tags that contain tables or lists ([1f2e0dc](https://github.com/nvim-neorg/neorg/commit/1f2e0dc23f6944bad660553ad4550847cf68e096))\n* **docgen:** differentiate between lists and tables ([c0062e5](https://github.com/nvim-neorg/neorg/commit/c0062e5a75226f063b59eb5ee8250cb4da6ea202))\n* **docgen:** differentiate empty and nonempty tables/lists ([0ab1a8d](https://github.com/nvim-neorg/neorg/commit/0ab1a8d469667d6b763e299c390a3e9bc7ea1a13))\n* **docgen:** implement `Required By` field ([7033c4b](https://github.com/nvim-neorg/neorg/commit/7033c4bd2dc4633d0874b2da05b0ae67928b1117))\n* **docgen:** implement `Required By` section ([15bf71b](https://github.com/nvim-neorg/neorg/commit/15bf71b15e07917e0f3a55de44e79fcdfbfc557d))\n* **docgen:** implement configuration_options parsing logic ([b34658a](https://github.com/nvim-neorg/neorg/commit/b34658a21602fdd286889c2e57bf2d68d63d4472))\n* **docgen:** implement function rendering, fix incorrect interpretation of function calls ([a023488](https://github.com/nvim-neorg/neorg/commit/a023488944473dfcd611308d5e21b7a9b2d7690d))\n* **docgen:** implement table rendering ([9074328](https://github.com/nvim-neorg/neorg/commit/907432885e40a16f064d4406cd45efeb895f0962))\n* **docgen:** indent nested table keys ([9cf679a](https://github.com/nvim-neorg/neorg/commit/9cf679a24f3bb9db145ae4dfbeb43878a49839e3))\n* **docgen:** massive structure changes, implement proper table rendering ([42b8728](https://github.com/nvim-neorg/neorg/commit/42b8728f291072b9d8a11bdf9c7e205ef15fb94d))\n* **docgen:** parse config tables ([93c41e1](https://github.com/nvim-neorg/neorg/commit/93c41e1f0aa290d0ad2e2590753312c71a782395))\n* **docgen:** perform `[@module](https://github.com/module)` lookups, pasre complex data structures like tables ([19f2381](https://github.com/nvim-neorg/neorg/commit/19f23811ba0366fe3ec9423d26aec33d9d34fcc2))\n* **docgen:** properly implement recursive table scanning ([33e06b8](https://github.com/nvim-neorg/neorg/commit/33e06b8d0fc2e7a9b386fc95e6bce4dfb714e56f))\n* **docgen:** sort entries when rendering ([b420e70](https://github.com/nvim-neorg/neorg/commit/b420e70532766475bce0bf1d34129e711a31e21a))\n* **docgen:** start generating true module pages ([5115d5c](https://github.com/nvim-neorg/neorg/commit/5115d5cd4bd1fefb11f82cb3e3da18b74d4e9b9e))\n* **helpers/lib:** add `read_files` and `title` functions ([d59f41b](https://github.com/nvim-neorg/neorg/commit/d59f41b78755b102a41d172d4e3f64d59cb86b8b))\n* **helpers:** add `ensure_nested` function ([2c4e8d0](https://github.com/nvim-neorg/neorg/commit/2c4e8d02feb7f1e6878307e7813a9f13ec000a73))\n* **helpers:** Add wrapper to vim.notify ([#778](https://github.com/nvim-neorg/neorg/issues/778)) ([c278f6f](https://github.com/nvim-neorg/neorg/commit/c278f6f895c6b2f9ef4fc217ed867675108d804e))\n* implement _Sidebar generation ([733b74c](https://github.com/nvim-neorg/neorg/commit/733b74c92481bc955f1f46594e50fb4931ab3cf5))\n* implement necessary APIs for complex data structure parsing ([b78f01c](https://github.com/nvim-neorg/neorg/commit/b78f01cd951b8ecfe7e842d31f609e6e4d5ac9db))\n* implement new docgen featuring top-comment validation ([b77fbd5](https://github.com/nvim-neorg/neorg/commit/b77fbd52f96db049687901ac1f0a8aea7ab4bdfa))\n* **indent:** adapt indentation of nestable detached modifiers when a detached modifier extension is found ([56e59da](https://github.com/nvim-neorg/neorg/commit/56e59daff56ba7f4d76b11ff3fc6dd70c4b54524))\n* **makefile:** add `local-documentation` option ([ed20f79](https://github.com/nvim-neorg/neorg/commit/ed20f796f5bb337d230f5d33a3f6ba420a8d30a4))\n* **qol.todo_items:** add new `create_todo_items` option ([d810aa4](https://github.com/nvim-neorg/neorg/commit/d810aa43c96301db35af351306eab54e35071d57))\n* **qol.todo_items:** add new `create_todo_parents` option (false by default) ([6b6ef04](https://github.com/nvim-neorg/neorg/commit/6b6ef04e5fb0a5b1c3ff59699a9371afd659d9ff))\n* **qol.todo_items:** when only done and uncertain items are present in ([1d6b0b0](https://github.com/nvim-neorg/neorg/commit/1d6b0b056b097e9f4bacf8877c49fdacbc445b2c))\n* strip leading `--` from comments ([ecea630](https://github.com/nvim-neorg/neorg/commit/ecea6305a82007b6c8c509fd594f8b52c3331021))\n* **summary:** implement `metadata` strategy and reimplement summary generation code ([f948288](https://github.com/nvim-neorg/neorg/commit/f9482881315d49d0a35206c7936a7f48c20dfcbf))\n* **toc:** add `close_after_use` configuration option ([#785](https://github.com/nvim-neorg/neorg/issues/785)) ([e5d7fbb](https://github.com/nvim-neorg/neorg/commit/e5d7fbb0291e658f78545c29318c9162cf505d15))\n\n\n### Bug Fixes\n\n* `:Neorg journal today` would fail on alternative path separators ([#749](https://github.com/nvim-neorg/neorg/issues/749)) ([e7a5054](https://github.com/nvim-neorg/neorg/commit/e7a50542ad9921a8c7d652eeca6a9006cc024b79))\n* **base.lua:** don't assign the `extension` flag to parent modules, only to the imports themselves ([fa5f561](https://github.com/nvim-neorg/neorg/commit/fa5f56163510eb00a0d75bec81d40d901c175d3b))\n* **clipboard.code-blocks:** don't cut off characters from non-visual-line selection ([744ae49](https://github.com/nvim-neorg/neorg/commit/744ae49fe5fab9e54de96282778a202f85a2f37b))\n* **code.looking-glass:** Use last attribute as start row of looking-glass (fix [#777](https://github.com/nvim-neorg/neorg/issues/777)) ([beef6fd](https://github.com/nvim-neorg/neorg/commit/beef6fd9420d6a798ddd796779b96f006b14ca12))\n* **commands.return:** don't override the workspace to `default` after running `:Neorg return` ([169c7be](https://github.com/nvim-neorg/neorg/commit/169c7bee8a5f101c63c3a473a577dce079f7ddec))\n* **concealer:** whenever running any scheduled command ensure that the buffer exists first ([b926416](https://github.com/nvim-neorg/neorg/commit/b9264161d0ef10ee61ace6ebeb0a55ca461b638a))\n* **core.clipboard.code-blocks:** module would not work past version `1.0.0` ([ac88283](https://github.com/nvim-neorg/neorg/commit/ac8828369cb2a4b2e1e17e6b495645585ed2a37b))\n* **core.clipboard.code-blocks:** visual selection would cut off one character too little ([87ed4bf](https://github.com/nvim-neorg/neorg/commit/87ed4bfde4a00a4cf4279298de02280bf11c7a74))\n* **core.export.markdown:** Update markdown exporter for new todo syntax (fix [#757](https://github.com/nvim-neorg/neorg/issues/757)) ([336416f](https://github.com/nvim-neorg/neorg/commit/336416f6c41777a4025cc80b8a085e21e758931f))\n* **core.itero:** preserve indentation on continued items ([92c31c4](https://github.com/nvim-neorg/neorg/commit/92c31c491caedd7d1a82b42d4ba6c2227c05d930))\n* **core.norg.esupports.hop:** Make hop on anchors work again ([#756](https://github.com/nvim-neorg/neorg/issues/756)) ([d38a229](https://github.com/nvim-neorg/neorg/commit/d38a22940aaa55351cd4dc106540fa302fad4f0d))\n* **core.norg.journal:** fixes [#736](https://github.com/nvim-neorg/neorg/issues/736) , now generates TOC correctly ([19c5558](https://github.com/nvim-neorg/neorg/commit/19c555836bc31f482e0ea42f08d150110754644f))\n* **core.promo:** don't error when the concealer is not loaded ([#767](https://github.com/nvim-neorg/neorg/issues/767)) ([3e09f69](https://github.com/nvim-neorg/neorg/commit/3e09f698b8a4151f2b4f77ee917e4b54388bc97a))\n* **dirman:** automatically create the index file if it exists when running `:Neorg index` ([7ce2db5](https://github.com/nvim-neorg/neorg/commit/7ce2db5d2eeec37b4a4c4bc43009c4741c3755da))\n* **dirman:** corrected win width and height calculation ([9766bef](https://github.com/nvim-neorg/neorg/commit/9766bef893ec993af9408ea0d44a8f13adbd1e80))\n* **dirman:** don't create `index.norg` files in the default workspace when running `:Neorg index` ([c60747f](https://github.com/nvim-neorg/neorg/commit/c60747fcc567d7eb50b16c2007bcfd3a81a934d1))\n* **docgen:** `&lt;h6&gt;` tags not being rendered properly ([d0a0da0](https://github.com/nvim-neorg/neorg/commit/d0a0da017135b48c5f4a325bfaedbcdf1ca79fe3))\n* **docgen:** could not find module `neorg` ([b68a945](https://github.com/nvim-neorg/neorg/commit/b68a945d6b1a8c2f8c57e0c366f224a162f391e3))\n* **docgen:** display listed modules in alphabetical order ([264b451](https://github.com/nvim-neorg/neorg/commit/264b451d74d3e4bc3f856d087070b9dac46f8e90))\n* **docgen:** don't double-render numeric values ([35df191](https://github.com/nvim-neorg/neorg/commit/35df1918de321617972f10edd88d9c32cc8102a2))\n* **docgen:** don't render description tags if no description is present ([64dc28d](https://github.com/nvim-neorg/neorg/commit/64dc28deea9a7f9c52c3ba5343f1ce3c754a566e))\n* **docgen:** don't unnecessarily copy parsers ([46e7936](https://github.com/nvim-neorg/neorg/commit/46e79366775136592540fbe5f0532c001012daa5))\n* **docgen:** incorrect wiki paths ([2dbead6](https://github.com/nvim-neorg/neorg/commit/2dbead687053610147161ecda1a908070720731f))\n* **docgen:** internal modules that were part of `core.defaults` would not be displayed in the developer modules section ([c3099eb](https://github.com/nvim-neorg/neorg/commit/c3099ebd595d3ec491613c297f4199e316f85853))\n* **docgen:** list items with no summary would break rendering ([b69ea57](https://github.com/nvim-neorg/neorg/commit/b69ea57029a2c62e72ea86c93d295102059c58ab))\n* **docgen:** lists within lists would never be rendered ([06894bb](https://github.com/nvim-neorg/neorg/commit/06894bb090ad6deb52ba7b19f7e13bbb41385a63))\n* **docgen:** make the spacing nicer to look at ([426ca24](https://github.com/nvim-neorg/neorg/commit/426ca246e310a212cef5f6bba367d7ebc84bf70b))\n* **docgen:** remove debug log ([8ffcaed](https://github.com/nvim-neorg/neorg/commit/8ffcaed1095743b8474a16f25855b809cf4fe65d))\n* **docgen:** this should work now i think (after 20 tries) ([72d3d49](https://github.com/nvim-neorg/neorg/commit/72d3d4981d85fd1114d3185e40cc135a7892a4a4))\n* **docgen:** use minimal_init.vim instead of custom_init.vim ([a7cb7ab](https://github.com/nvim-neorg/neorg/commit/a7cb7ab443c24fbd1d9f696abddd91c16f05d842))\n* **docgen:** wrong `require` order in `docgen.lua` ([7494b51](https://github.com/nvim-neorg/neorg/commit/7494b51a61cc31371514cb7b2e1ccdf4cef164e2))\n* finalize `version_in_code.yml` CI (it works yay) ([db9ed0b](https://github.com/nvim-neorg/neorg/commit/db9ed0b98ba30e2b783ea9da0fddda4cf6b2a47e))\n* **metagen:** use `norg_version` ([a5c2553](https://github.com/nvim-neorg/neorg/commit/a5c25531de2790133310ad874fcbbb976d082c78))\n* neovim 0.9 vim.treesitter.parse_query deprecation ([#784](https://github.com/nvim-neorg/neorg/issues/784)) ([f4a9759](https://github.com/nvim-neorg/neorg/commit/f4a9759e53fadaece9d93118a0471ddffd05d394))\n* **qol.todo_item:** `&lt;C-space&gt;` would not create a new TODO item with ([fc45beb](https://github.com/nvim-neorg/neorg/commit/fc45bebde0fc9811ca4e770e2ba29c791035c885))\n* **qol.todo_items:** `&lt;C-space&gt;` would not respect the `create_todo_items` option ([e764b92](https://github.com/nvim-neorg/neorg/commit/e764b92065ddd6bc206aaefd92837be8d0bd8419))\n* **qol.todo_items:** TODO attributes would be erroneously assigned multiple times ([1303097](https://github.com/nvim-neorg/neorg/commit/13030974acee5a49dd02c51bedeac104b4f33cb7))\n* **summary:** appropriately indent nested entries ([b725a58](https://github.com/nvim-neorg/neorg/commit/b725a58f25525efc1c39a13a09e6cec0c1c0ba4d))\n* **version_in_code.yml:** perform checkout in the current directory ([3d7ad5a](https://github.com/nvim-neorg/neorg/commit/3d7ad5aa4b1277ea0f3ebb93ea79179eda5f6e27))\n* **version_in_code.yml:** use `fetch-depth` of `0` ([2e8fa52](https://github.com/nvim-neorg/neorg/commit/2e8fa524d2cc73002378875970048d70ae70cc0b))\n\n\n### Performance Improvements\n\n* **concealer:** don't rerender the whole file on every single BufEnter ([7419cbb](https://github.com/nvim-neorg/neorg/commit/7419cbb7262200dd94df9d398ee1e7f5b9503a50))\n\n\n### Miscellaneous Chores\n\n* **docgen:** wipe whole wiki on every reparse ([09cb3e6](https://github.com/nvim-neorg/neorg/commit/09cb3e62022ff0f93965800a728ed698db540240))\n\n\n### ref\n\n* **Makefile:** remove `install_pre_commit` target ([9a497f5](https://github.com/nvim-neorg/neorg/commit/9a497f5e8195e5b974d520f937a946cb8819f320))\n* move `core.norg.dirman.summary` -&gt; `core.summary` ([254b6a6](https://github.com/nvim-neorg/neorg/commit/254b6a60b6c9f845400d2bcb0728ee7f38823781))\n* **summary:** refactor of the `core.norg.dirman.summary` module ([a2fe3ee](https://github.com/nvim-neorg/neorg/commit/a2fe3eea24c628fa15b7f854cef6c7acaf9ec3f9))\n\n\n### Code Refactoring\n\n* move all `core.norg.*` modules into `core.*` ([a5824ed](https://github.com/nvim-neorg/neorg/commit/a5824edf6893b8602e560ed7675c0d4174e263e4))\n\n## [2.0.1](https://github.com/nvim-neorg/neorg/compare/v2.0.0...v2.0.1) (2023-02-02)\n\n\n### Bug Fixes\n\n* completion for TODO items ([#711](https://github.com/nvim-neorg/neorg/issues/711)) ([9184027](https://github.com/nvim-neorg/neorg/commit/91840274112f1286ff5f4063ac6f515683b6dc67))\n* **core.norg.journal:** add proper error handling for `vim.loop.fs_scandir` ([4a9a5fe](https://github.com/nvim-neorg/neorg/commit/4a9a5fe13cd454692fc4db0b27783cd005e6be56))\n* **treesitter:** don't constantly log errors about erroneous document syntax trees ([9f8b0a1](https://github.com/nvim-neorg/neorg/commit/9f8b0a1759d883fae901579ea83b3ffbfc81a53b))\n\n## [2.0.0](https://github.com/nvim-neorg/neorg/compare/v1.1.1...v2.0.0) (2023-01-06)\n\n\n### ⚠ BREAKING CHANGES\n\n* **core.norg.qol.toc:** rewrite the table of contents implementation\n\n### Features\n\n* **core.export:** add `NeorgExportComplete` user autocommand ([8b10e61](https://github.com/nvim-neorg/neorg/commit/8b10e61d2f2c5e626849f9a6f8cb4399c28a1a47))\n* **core.norg.qol.toc:** add multiple buffer handling logic ([467e311](https://github.com/nvim-neorg/neorg/commit/467e3113c32b8b9f1950a9425aa7b74c13cd88b8))\n* **core.norg.qol.toc:** implement `qflist` generation option ([77c5149](https://github.com/nvim-neorg/neorg/commit/77c514970a9d4648b05b2334a060263666f588e2))\n* **treesitter:** add `execute_query` function ([310ebaa](https://github.com/nvim-neorg/neorg/commit/310ebaaef538dfd41d02a2903663be05fd38834b))\n\n\n### Bug Fixes\n\n* **core.ui:** do not modify the user's `scrolloffset` ([bd2e58c](https://github.com/nvim-neorg/neorg/commit/bd2e58cf6f9d42527aa2b692fb187eafa82bd91e))\n\n\n### Performance Improvements\n\n* further optimize `toc` infirm tag grabber ([5e8d059](https://github.com/nvim-neorg/neorg/commit/5e8d05968e04f7945576d50a6b1576cc722f96fc))\n* optimize the `toc` infirm tag grabber code ([a41bd4a](https://github.com/nvim-neorg/neorg/commit/a41bd4a92afefb7e2630b821b59f7707a054baac))\n\n\n### ref\n\n* **core.norg.qol.toc:** rewrite the table of contents implementation ([c0104fb](https://github.com/nvim-neorg/neorg/commit/c0104fb9faed3b3213e4e275a55a522a299a2d0e))\n\n## [1.1.1](https://github.com/nvim-neorg/neorg/compare/v1.1.0...v1.1.1) (2023-01-05)\n\n\n### Bug Fixes\n\n* **core.export:** incorrect exporting of code blocks with no parameters ([#701](https://github.com/nvim-neorg/neorg/issues/701)) ([0922815](https://github.com/nvim-neorg/neorg/commit/0922815837a374bd0b2a3cf0477b54e6668e133d))\n\n## [1.1.0](https://github.com/nvim-neorg/neorg/compare/v1.0.1...v1.1.0) (2023-01-05)\n\n\n### Features\n\n* keep checkboxes with `core.itero` ([#663](https://github.com/nvim-neorg/neorg/issues/663)) ([00532bd](https://github.com/nvim-neorg/neorg/commit/00532bd997d2aef0384ed8f11500d33d229a7e53))\n\n\n### Bug Fixes\n\n* **core.export.markdown:** incorrectly exported code blocks ([dd2750c](https://github.com/nvim-neorg/neorg/commit/dd2750c0e4d847b67a6ead79ff5043e671cac8bd))\n* **folds:** correctly fold document metadata ([adc000a](https://github.com/nvim-neorg/neorg/commit/adc000aadd41e68e4de8a2d1bb90b2e910ffef1b))\n\n## [1.0.1](https://github.com/nvim-neorg/neorg/compare/1.0.0...v1.0.1) (2022-12-23)\n\n\n### Bug Fixes\n\n* **core.looking-glass:** buffer being closed for no reason after leaving buffer ([828a37f](https://github.com/nvim-neorg/neorg/commit/828a37fe1f008dbfd70cd7fc0f7ba9d0bc75da2a))\n* do not run tests for nightly/neorg-main, as GTD is no longer existent ([37f1f9a](https://github.com/nvim-neorg/neorg/commit/37f1f9a44ba65603b5992fc36761c61d921fab78))\n"
  },
  {
    "path": "LICENSE",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n    <program>  Copyright (C) <year>  <name of author>\n    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, your program's commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n<https://www.gnu.org/licenses/>.\n\n  The GNU General Public License does not permit incorporating your program\ninto proprietary programs.  If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.  But first, please read\n<https://www.gnu.org/licenses/why-not-lgpl.html>.\n"
  },
  {
    "path": "Makefile",
    "content": "documentation:\n\tnvim --headless -u docgen/minimal_init.vim -c \"cd ./docgen\" -c \"source init.lua\" -c 'qa'\n\nlocal-documentation:\n\tnvim --headless -c \"cd ./docgen\" -c \"source init.lua\" -c 'qa'\n\nformat:\n\tstylua -v --verify .\n\ncheck:\n\tnix flake check\n\nshell:\n\tnix develop\n\nintegration-test:\n\tnix run \".#integration-test\"\n\ntest:\n\tLUA_PATH=\"$(shell luarocks path --lr-path --lua-version 5.1 --local)\" \\\n\tLUA_CPATH=\"$(shell luarocks path --lr-cpath --lua-version 5.1 --local)\" \\\n\tluarocks test --local --lua-version 5.1\n"
  },
  {
    "path": "README.md",
    "content": "<div align=\"center\">\n\n<img src=\"res/neorg.svg\" width=300>\n\n# Neorg - An Organized Future\n\n<a href=\"https://neovim.io\"> ![Neovim](https://img.shields.io/badge/Neovim%200.10+-brightgreen?style=for-the-badge) </a>\n<a href=\"https://chat.neorg.org\"> ![Zulip](https://img.shields.io/badge/zulip-join-6492fe?style=for-the-badge&logo=zulip) </a>\n<a href=\"https://discord.gg/T6EgTAX7ht\"> ![Discord](https://img.shields.io/badge/discord-join-7289da?style=for-the-badge&logo=discord) </a>\n<a href=\"/LICENSE\"> ![License](https://img.shields.io/badge/license-GPL%20v3-brightgreen?style=for-the-badge)</a>\n<a href=\"https://dotfyle.com/plugins/nvim-neorg/neorg\"> ![Usage](https://dotfyle.com/plugins/nvim-neorg/neorg/shield?style=for-the-badge) </a>\n\nYour New Life Organization Tool - All in Lua\n\n[Tutorial](#-tutorial)\n•\n[Roadmap](/ROADMAP.md)\n•\n[Installation](#-installation)\n•\n[Further Learning](#-further-learning)\n<br>\n[Credits](#credits)\n•\n[Support](#support)\n\n</div>\n\n<div align=\"center\">\n\n<br>\n\n**:warning: Neorg `9.0.0` has introduced some breaking changes! Please see [this blog post](https://vhyrro.github.io/posts/neorg-9-0-0/) on what changed.**\n\n## Summary\n\n</div>\n\nNeorg (_Neo_ - new, _org_ - organization) is a Neovim plugin designed to reimagine organization as you know it.\nGrab some coffee, start writing some notes, let your editor handle the rest.\n\n### What is Neorg?\n\nNeorg is an all-encompassing tool based around structured note taking, project and task management, time\ntracking, slideshows, writing typeset documents and much more. The premise is that all of these features are\nbuilt on top of a single base file format (`.norg`), which the user only has to learn once to gain access to\nall of Neorg's functionality.\n\nNot only does this yield a low barrier for entry for new users it also ensures that all features are integrated with each\nother and speak the same underlying language. The file format is built to be expressive and easy to parse,\nwhich also makes `.norg` files easily usable anywhere outside of Neorg itself.\n\nA good way of thinking about Neorg is as a plaintext environment which can be adapted to a variety of use cases.\nIf a problem can be represented using raw text, it can be solved using Neorg.\n\n###### :exclamation: **IMPORTANT**: Neorg is young software. We consider it stable however be prepared for occasional breaking workflow changes. Make sure to pin the version of Neorg you'd like to use and only update when you are ready.\n\n## 🌟 Tutorial\n\nA video tutorial may be found on Youtube:\n\n<div>\n\n<a href=\"https://www.youtube.com/watch?v=NnmRVY22Lq8&list=PLx2ksyallYzVI8CN1JMXhEf62j2AijeDa&index=1\">\n <img src=\"https://img.youtube.com/vi/NnmRVY22Lq8/0.jpg\" style=\"width:75%;\">\n</a>\n\n</div>\n\n## 📦 Installation\n\nNeorg's setup process is slightly more complex than average, so we encourage you to be patient :)\n\n**Neorg requires Neovim 0.10 or above to function. After you're done with the\ninstallation process, run `:checkhealth neorg` to see if everything's\ncorrect!**\n\n### `neorg-kickstart`\n\nNot bothered to set up Neovim on your own? Check out our [kickstart config](https://github.com/nvim-neorg/neorg/wiki/Kickstart)\nwhich will get you up and running with Neorg without any prior Neovim configuration knowledge.\n\n### `rocks.nvim`\n\nOne way of installing Neorg is via [rocks.nvim](https://github.com/nvim-neorocks/rocks.nvim).\n\n<details>\n<summary>Installation snippet.</summary>\n\n- Run `:Rocks install rocks-config.nvim` (if you don't have it already!).\n- Run `:Rocks install neorg`.\n- From the root of your configuration (`~/.config/nvim/` on unix-like systems), create a `lua/plugins/neorg.lua` file and place the following content inside:\n  ```lua\n  require(\"neorg\").setup()\n  ```\n\n<!-- TODO: rewrite this section once a new luarocks release is made, as it will automatically download nvim-treesitter-legacy-api as a dependency -->\nFor the time being you also need `nvim-treesitter` installed.\n- `:Rocks install nvim-treesitter-legacy-api`\n- Just like the `neorg.lua` file, create a `lua/plugins/treesitter.lua` file and place the following content inside:\n  ```lua\n  require(\"nvim-treesitter.configs\").setup({\n    highlight = {\n      enable = true,\n    },\n  })\n  ```\n\nThe last three steps will eventually not be required to run Neorg.\n\n</details>\n\n### `lazy.nvim`\n\nTo install Neorg via lazy, first ensure that you have `luarocks` installed on your system.\nOn Linux/Mac, this involves installing using your system's package manager. On Windows, consider\nthe [Lua for Windows](https://github.com/rjpcomputing/luaforwindows) all-in-one package.\n\n<details>\n<summary>Click for installation snippet.</summary>\n\n```lua\n{\n    \"nvim-neorg/neorg\",\n    lazy = false, -- Disable lazy loading as some `lazy.nvim` distributions set `lazy = true` by default\n    version = \"*\", -- Pin Neorg to the latest stable release\n    config = true,\n}\n```\n\nWhile lazy [supports lazy-loading upon specific commands and\nfiletypes](https://lazy.folke.io/spec#spec-lazy-loading), it can cause neorg to load incorrectly,\nleading to a 'broken' plugin. Lazy load at your own risk, and disable lazy loading as your first\ndebugging step.\n\n</details>\n\n### `packer.nvim`\n\nNeorg can be installed purely via luarocks on packer, pulling in all required dependencies in the process.\n\nIt is not recommended to use packer as it is now unmaintained.\n\n<details>\n<summary>Click for installation snippet.</summary>\n\n```lua\nuse {\n  \"nvim-neorg/neorg\",\n  rocks = { \"lua-utils.nvim\", \"nvim-nio\", \"nui.nvim\", \"plenary.nvim\", \"pathlib.nvim\", \"nvim-treesitter-legacy-api\" },\n  tag = \"*\", -- Pin Neorg to the latest stable release\n  config = function()\n      require(\"neorg\").setup()\n  end,\n}\n```\n\n</details>\n\n### Other Plugin Managers\n\nBecause of the complexities of `luarocks`, we are choosing not to support other plugin managers for the time\nbeing. It is actively on our TODO list, however!\n\n## 📚 Further Learning\n\nAfter you have installed Neorg, we recommend you head over to either the Youtube tutorial series or to the [wiki](https://github.com/nvim-neorg/neorg/wiki)!\n\n## Credits\n\nMassive shoutouts go to all the contributors actively working on the project together to form a fantastic\nintegrated workflow:\n\n- [mrossinek](https://github.com/mrossinek) - for basically being my second brain when it comes to developing new features\n  and adding new syntax elements\n- [danymat](https://github.com/danymat) - for creating the excellent foundations for the up and coming GTD system\n\nAnd an extra thank you to:\n\n- [Binx](https://github.com/dvchoudh) - for making that gorgeous logo for free!\n- [bandithedoge](https://github.com/bandithedoge) - for converting the PNG version of the logo into SVG form\n\n## Support\n\nLove what I do? Want to see more get done faster? Want to support future projects? Any sort of support is always\nheartwarming and fuels the urge to keep going :heart:. You can show support here:\n\n- [Buy me a coffee!](https://buymeacoffee.com/vhyrro)\n- [Support me via Github Sponsors](https://github.com/sponsors/vhyrro)\n- [Support me on Patreon](https://patreon.com/vhyrro)\n\nImmense thank you to all of the sponsors of my work!\n\n<div align=\"center\">\n\n<!-- sponsors --><a href=\"https://github.com/vsedov\"><img src=\"https://github.com/vsedov.png\" width=\"60px\" alt=\"vsedov\" /></a>&nbsp;&nbsp;&nbsp;<a href=\"https://github.com/molleweide\"><img src=\"https://github.com/molleweide.png\" width=\"60px\" alt=\"molleweide\" /></a>&nbsp;&nbsp;&nbsp;<a href=\"https://github.com/danymat\"><img src=\"https://github.com/danymat.png\" width=\"60px\" alt=\"danymat\" /></a>&nbsp;&nbsp;&nbsp;<a href=\"https://github.com/jgregoire\"><img src=\"https://github.com/jgregoire.png\" width=\"60px\" alt=\"jgregoire\" /></a>&nbsp;&nbsp;&nbsp;<a href=\"https://github.com/bottd\"><img src=\"https://github.com/bottd.png\" width=\"60px\" alt=\"bottd\" /></a>&nbsp;&nbsp;&nbsp;<a href=\"https://github.com/kvodenicharov\"><img src=\"https://github.com/kvodenicharov.png\" width=\"60px\" alt=\"kvodenicharov\" /></a>&nbsp;&nbsp;&nbsp;<!-- sponsors -->\n\n</div>\n"
  },
  {
    "path": "ROADMAP.md",
    "content": "<div align=\"center\">\n\n# Neorg Roadmap\n\n</div>\n\nGiven the rapid growth and adoption of Neorg it's critical that a development plan is\nestablished to not only help the development team keep track of progress but to also\nfacilitate and encourage contributions from the flourishing community.\n\nThis file is written and maintained in Markdown for easy viewing on GitHub.\nIt will be switched to a `.norg` file when possible.\n\n# Neorg\n\n## Miscellaneous\n\n- [ ] Make `core.clipboard.code-blocks` work with a visual selection.\n- [x] Reimplement the `core.maneouvre` module, which has been deprecated since `1.0`.\n- [ ] The `a` and `b` commands in the hop module are not implemented.\n- [ ] Readd colouring to TODO items.\n\n## Workflow\n\n- [x] [The Calendar UI](#calendar)\n- [ ] The GTD Methodology\n- [ ] Zettelkasten\n- [ ] The `sc-im`esque Table Editor\n\nTo achieve the above, a set of cross-platform tools must be initially developed.\nFor the motivation behind this, see the [external tools](#external-tooling) section.\n\n## Documentation\n\n- [ ] Develop and ship a Neorg landing page with documentation, presumably with docasaurus.\n    - [x] Provide a written tutorial on how to get started with Neorg.\n    - [ ] Provided a tutorial on how to develop for Neorg (modules, events, etc.).\n- [ ] Provide a dropdown in the wiki which will display a module's entire configuration as a lua snippet.\n\n### Calendar\n\nThe calendar is a planned sophisticated and flexible tool for selecting a date.\nWhile it sounds rather trivial, the calendar is the main user interaction for any\ndate related operation. Because of this, there are many components to a calendar.\n\nAt its core the calendar is simply supposed to allow the user to select a date.\nThis is such a vast concept however that the calendar should be able to handle a variety\nof situations/contexts.\n\n#### Context Switching\n\nDepending on the preferred context, the calendar should be able to work in four\nmajor views - `DAILY`, `WEEKLY`, `MONTHLY` and `YEARLY`. Each mode has a\ndifferent layout fit for the task at hand, and displays varying amounts of\ninformation based on said view.\n\n- The daily view displays only the current day, but allows you to quickly select an hour in that day.\n  Moving left or right moves one hour forward or backward, respectively.\n  The view is vertical, meaning an hour takes up a horizontal bar and they are stacked top to bottom starting\n  from midnight.\n- The weekly view displays all 7 days of the week. Moving forwards or backwards moves one day forward\n  or backward, respectively. This view is also vertical, where each day takes up a single bar\n  of the view.\n- The monthly view shows all days of the month in a horizontally rendered fashion.\n  It is the default mode if none is specified.\n- The yearly view is meant to summarize more than it is supposed to serve any function.\n  The details of this view have not yet been fully drawn out.\n\nAs with all things Neorg, other modules should be free to create their own\nviews via an API. Apart from just creating views, any module should be free to\nadd \"custom data\" to the view, which the view should be able to handle\nappropriately through a set of default API functions. An example of this may be\nthe GTD module, which could display all the tasks for a day in the daily view, or\nit could highlight a day as red in the monthly view if there are urgent tasks\nthat have not been yet completed.\n\n#### Keybinds\n\nThe user experience comes first - the keybinds should be as close as possible to vim, with\nslight deviations if the keybinds hinder the \"mnemonic\" keybind model, where a sequence of words\n(e.g. `delete around word`) can be converted to a set of keybinds (`daw`).\n\n# Cross-Compatibility\n\nWalled gardens? No thanks. Conversion from (and to) `.norg` and Neorg workflows should be\nhassle-free.\n\n- [ ] [Pandoc](https://pandoc.org/) integration. A pandoc parser (or lua filter) would be super\n      useful to convert to and from the `.norg` file format. Work has begun (but is currently stalled)\n      in the following [PR](https://github.com/nvim-neorg/neorg/pull/684). There is also an incomplete\n      pandoc parser written in Haskell [here](https://github.com/Simre1/neorg-haskell-parser), so if you\n      have some Haskell skills then this is your time to help out! :)\n- [ ] Inbuilt upgraders from `.org` to `.norg`, and vice versa.\n      Org and markdown are the most common formats to convert to `.norg`, and whereas markdown can\n      be handled just fine by pandoc, org may need some extra fine tuning to work effectively. We'd\n      like for Neorg to be able to \"understand\" workflows within emacs's `org-mode`, such as the\n      inbuilt agenda view and others, and convert them into the Neorg-appropriate counterpart.\n\n# External Tooling\n\nExternal tooling is a great way to help Neorg see adoption in spaces other than Neovim. While the\nbest features will remain within the Neorg plugin for Neovim, writing tools for `.norg` and for\nNeorg should not be difficult nor discouraging. As a result, we'll create and maintain a set of\nembeddable-anywhere tools. These include:\n\n- [ ] Parsers in several languages - the first step of supporting Neorg is supporting its underlying\n  file format, `.norg`. Because Norg is a well-standardized format, many parsers can be created\n  for it without forming annoying \"dialects\". Currently, the most well supported parser is the\n  `tree-sitter` parser, which is embeddable essentially anywhere thanks to its library being written\n  in C.\n  Apart from [`tree-sitter`](https://github.com/nvim-neorg/tree-sitter-norg), work-in-progress\n  parsers can be found for many languages like [`rust`](https://github.com/max397574/rust-norg),\n  [`zig`](https://github.com/vhyrro/zig-norg),\n  [`haskell`](https://github.com/Simre1/neorg-haskell-parser) and\n  [`julia`](https://github.com/klafyvel/Norg.jl)!\n- [x] Directory Manager and File Aggregator - workspaces are a pretty fundamental concept within\n  Neorg, and writing a tool that can efficiently manage and manipulate enormous collections\n  of notes will be critical for any Neorg-like tool.\n- [x] Multithreaded parsing library - note collections can get big, like really big. Parsing\n      all of these on a single thread could take even minutes. Having a good multithreaded\n      parsing library will help a lot.\n- [x] Norgopolis and its related modules - norgopolis servers as a router for all server side\n      logic, including the database, for the multithreaded Treesitter parser, as well as for\n      managing active workspaces. Many clients may connect to this server, establishing a single\n      source of truth for any `n` amount of clients.\n- [x] SurrealDB - Neorg's preferred backend for execution is SurrealDB, a modern multi-model\n      database. It was specifically chosen because, apart from just being able to store data in a relational\n      format (like sqlite), it also has the ability of creating and operating on nodes like a graph database.\n      This allows for lighting fast lookups of e.g. links, tasks and/or inline metadata in the file.\n- [ ] GTD - this library would form the backend for the \"Getting Things Done\" methodology.\n      It would do all the heavy lifting, including managing notes, contexts and a bit more.\n      Afterwards all it takes is to write a frontend (the UI) in the application of your choice to\n      fully support Neorg's GTD capabilities.\n- [ ] Zettelkasten - similarly to GTD, the backend for zettelkasten will be implemented as a\n      library. This library would primarily handle backlinks, which are painfully slow to track natively\n      in Lua, which runs on a single thread.\n\n\nA logistics question - how do these libraries play with Neorg in Neovim itself?\nAll of the libaries we mentioned will be written in Rust, and via `cbindgen` we will\ngenerate `.o`/`.so` library files that may be used anywhere, including Neovim.\nLua has great support for importing shared library objects, so all that it takes\nis a Github action that compiles e.g. the `GTD` backend library, pulling in all dependencies,\npacking it into a small `.so`, and then shipping it directly with Neorg!\n\n# Mobile Application\n\nA mobile tool for Neorg would greatly increase it's adoption range thanks to portability. Such a\nmobile application could utilize all of the [external tools](#external-tooling) written by us\nto ensure that it's consistent in behaviour with all of the other Neorg tools in the ecosystem.\n\nFeatures of a mobile application would include:\n- [ ] A synchronisation mechanism between the mobile device and any other end devices.\n- [ ] A fast note capturing mechanism - if you have a random idea while e.g. going on a walk,\n      writing it down should only be two clicks away.\n      This note capturing mechanism should ideally be powered by the [GTD](#external-tooling)\n      library.\n- [ ] A pleasant UX - phone screens are fairly small, so UI fluidness and the way notes/tasks\n      are displayed should be as effective as possible.\n\n# Artificial Intelligence\n\nDo not worry, we're not a new startup overhyping AI thinking it will change the world. Also don't fret, we\nwill not use AI like most companies as a tactic to collect and send information to servers for data\nanalysis.\n\nWe strongly believe that for a specific and specialized subset of tasks artificial intelligence (or,\nmore specifically, natural language processing tools) are a novel way to aid in note taking. All\nmodels shipped by Neorg would not be in the main Neorg package, but as addon plugins. These addon\nplugins would ship with the model placed locally on your machine, and no contact with an external\nserver would be required to use these models.\n\nThere are currently only three use cases that we would like to use natural language processing for, and these are:\n- [ ] Speech to text recognition for the [mobile application](#mobile-application).\n- [ ] Automatic detection of contexts for a given GTD task. This takes away the manual process of having to\n      assign contexts for each of your tasks.\n- [ ] Zettelkasten sorters - because an NLP model has a good understanding of loosely similar\n      information and topics, it could be much more effective at categorizing, reordering and linking\n      zettels than a traditional programmatic algorithm.\n\nAll of these plans are for the very far future, long after [GTD and Zettelkasten](#workflow) are complete. However,\nif you have experience in training such models or would like to help, then do not hesitate!\n"
  },
  {
    "path": "doc/cheatsheet.norg",
    "content": "* Your Quick Neorg Guide\n  This is a cheatsheet that quickly lets you get a grip on the Norg syntax.\n  It is by no means exhaustive, but quickly gets you up to speed.\n\n  *Press `gO` for a Table of Contents*.\n\n* Structure\n\n** Headings\n\n   You've already seen them in action in this guide.\n\n   @code norg\n\n   * Heading 1\n   ** Heading 2\n   *** Heading 3\n   **** And so on...\n\n   @end\n\n   Norg supports infinite heading levels, but you'll find that Neorg caps out at six.\n   You can still go higher, but the parser will treat it as if it were a level six heading.\n\n** Reverse Headings\n\n   Need to go back a level? `---` takes you back a single level, while `===` closes all headings.\n\n   @code norg\n\n   * Heading 1\n   ** Heading 2\n      Part of heading 2.\n      ---\n     Part of heading 1.\n\n   *** Heading 3\n   **** Heading 4\n        Part of heading 4.\n        ===\n\n   Not part of any heading.\n\n   @end\n\n** Lists\n\n*** Unordered Lists\n\n    @code norg\n\n    - List item.\n    -- Nested list item\n    -- Also nested.\n    - Next list item.\n\n    @end\n\n*** Ordered Lists\n\n    @code norg\n\n    ~ List item.\n    ~~ Nested list item.\n    ~~ Also nested.\n    ~ Next list item.\n\n    @end\n\n** Code Blocks\n\n   Run `:Neorg toggle-concealer` to see the raw syntax.\n\n   @code\n   This is literally an empty code block.\n   @end\n\n   @code lua\n   print(\"If you have the lua treesitter parser installed, this will be highlighted as lua.\")\n   print(\"If not, run `:TSInstall lua`.\")\n   @end\n\n* Inline Markup\n\n** Styling\n\n   To see the syntax raw, run `:setlocal conceallevel=0`.\n   Set it back to `2` later for a more concise reading experience :)\n\n   @code norg\n\n   This is *bold*.\n   This is /italic/.\n   This is _underline_.\n   This is -strikethrough-.\n   This is a spoiler: !org-mode is a bozo!, to see the content you either enter insert mode on this line (good luck) or toggle the concealer off.\n   This is a `verbatim object, you can't put *markup* in here!`.\n\n   %This is a comment, and you never see it in your exported output.%\n\n   This is _underline, /italic/ and *-strikethrough bold-*_. Mix and match how you see fit, just make sure\n   you're not trying to put markup inside of a verbatim object!\n\n   @end\n\n   For the mathematical nerds:\n\n   @code norg\n\n   This is ^superscript^ (it won't render properly in Neovim).\n   This is ,subscript,   (same as above).\n   This is inline maths: $f(x) = supports \\\\LaTeX$, but beware! You have to escape backslashes like in the example.\n   Because of this, you should always use the above syntax for very simple things, and the \"rigid\"\n   syntax for more complicated expressions: $|\\int_0^t d(\\ln{S}) = ...|$ (notice the `\\|` and the use of single backslashes).\n\n   @end\n\n*** Styling with a Pipe\n\n    As shown in the maths example above, you can use the pipe `\\|` to allow arbitrary whitespace within the\n    markup. You also don't have to escape backslashes!\n\n    @code norg\n\n    /   this is not allowed  /\n    /|  but this is fine!!   |/\n\n    @end\n\n** Links\n\n   *NOTE: attempting to hit enter on the links will not work.*\n\n*** Links to URLs\n\n    @code norg\n\n    {https://github.com/nvim-neorg/neorg}\n    {file:///home/vhyrro/top-secret.txt} - as long as your OS supports your URI (`file://`) it'll work in Neorg\n\n    @end\n\n*** Links to Objects\n\n    To link to an object, you use its prefix and its name. The name part is\n    *only word and punctuation sensitive*. Case and whitespace are ignored.\n\n    @code norg\n\n    {*** styling, but extended} - valid\n    {*** styling,butextended} - valid\n    {*** styling but extended} - invalid, no `,` punctuation\n\n    @end\n\n*** Links to non-norg Files\n\n    @code norg\n\n    {/ myfile.txt}\n    {/ myfile.txt:43} - line 43 of `myfile.txt`\n\n    @end\n\n*** Links with Descriptions\n\n    @code norg\n\n    {* some heading}[my custom description]\n\n    @end\n\n*** Links to Anything\n\n    Don't care about what object type you're linking to?\n    Just use the `#` character.\n\n    @code norg\n\n    {# anything with this title}\n\n    @end\n\n*** Links across Files\n\n    Take your link but prefix it with `:filename:` (without the `.norg` extension).\n\n    @code norg\n\n    {:dir/file:* Heading in that file}\n    {:$/index.norg:* Heading in that file} - `$` signifies the path of the current workspace\n    {:$notes/index.norg:* Heading in that file} - you can also link to other workspaces\n\n    @end\n\n** Anchors\n\n   It's like links, but the syntax is reversed. Anchors can save you from writing the same thing countless times.\n\n   @code norg\n\n   This is a link to [my site]{https://very.long.link.to.my.boring.site}. `<- Works just like a normal link with a description`\n\n   Be sure to reference [my site] if you're interested! `<- Takes you to the correct site`\n\n   @end\n\n   As long as the name inbetween the square brackets is the same you'll be linked to the correct place.\n\n* Tasks\n\n  You can assign a task status to anything by adding a `|( )|`.\n\n  @code norg\n\n  - ( ) Here's a classical TODO item.\n\n  * (x) Here's a heading with a done TODO state. Curious.\n\n  @end\n\n  All supported states:\n\n  @code norg\n\n  ( ) Undone -> not done yet\n  (x) Done -> done with that\n  (?) Needs further input\n\n  (!) Urgent -> high priority task\n  (+) Recurring task with children\n\n  (-) Pending -> currently in progress\n  (=) Task put on hold\n  (_) Task cancelled (put down)\n\n  @end\n\n  ===\n\n#comment\nvim:tw=100:ft=norg:norl:conceallevel=2:concealcursor=nv:\n"
  },
  {
    "path": "doc/neorg.norg",
    "content": "@document.meta\ntitle: Neorg help-page\ndescription: The Neorg vim help-page\nauthor: The Neorg Community\ncategories: docs\ncreated: 2021-09-05\nversion: 0.1\n@end\n\n.toc\n\n* The `.norg` File Format\n\n  If you find the {https://github.com/nvim-neorg/norg-specs/blob/main/1.0-specification.norg}[spec]\n  too long and want to jump-start your Neorg skills, you've come to the right place!\n\n  NOTE: If you have `core.concealer` enabled, then be sure to run `:Neorg toggle-concealer`\n  any time you want to see the raw markup.\n\n  This document automatically disables the hiding of characters for clarity.\n  If you would like to reenable this, run `:set conceallevel=2`.\n\n** Basic Markup\n\n   Here is how you can do very basic markup. First you see it raw, then rendered:\n   - *bold*\n   - /italic/\n   - _underline_\n   - -strikethrough-\n   - !spoiler!\n   - `inline code`\n   - ^superscript^  (when nested into `subscript`, will highlight as an error)\n   - ,subscript,    (when nested into `superscript`, will highlight as an error)\n   - $f(x) = y$     (see also {# Math})\n   - &variable&     (see also {# Variables})\n   - %inline comment%\n\n   This also immediately shows you how to escape a special character using the backslash, \\\\.\n\n   Adding the [pipe]{:cheatsheet:*** Styling with a Pipe} modifier inside any markup\n   characters allows you to include white space before and after the markup characters, as well as\n   ignore escape characters inside.\n   Example: *|   look ma, white space!   |*\n\n** Nesting\n\n   Neorg generally does *NOT* care about indentation! 🎉\n   Thus, nesting is done via repeating modifiers like you are used to from Markdown headings.\n   Note, that this allows you to start at an arbitrary nesting level if you so desire!\n\n*** Unordered lists\n\n    - Unordered list level 1\n    -- Unordered list level 2\n    --- Unordered list level 3\n    ---- Unordered list level 4\n    ----- Unordered list level 5\n    ------ Unordered list level 6\n\n*** Ordered lists\n\n    ~ Ordered list level 1\n    ~~ Ordered list level 2\n    ~~~ Ordered list level 3\n    ~~~~ Ordered list level 4\n    ~~~~~ Ordered list level 5\n    ~~~~~~ Ordered list level 6\n\n*** Tasks\n\n    - ( ) Undone -> not done yet\n    - (x) Done -> done with that\n    - (?) Needs further input\n\n    - (!) Urgent -> high priority task\n    - (+) Recurring task with children\n\n    - (-) Pending -> currently in progress\n    - (=) Task put on hold\n    - (_) Task cancelled (put down)\n\n    The task modifier can be placed on any detached modifier, including\n    headings, definitions, footnotes etc. to track their state.\n\n*** Quotes\n\n    > 1. level quote\n    >> 2. level quote\n    >>> 3. level quote\n    >>>> 4. level quote\n    >>>>> 5. level quote\n    >>>>>> 6. level quote\n\n*** Headings\n\n    You already saw headings up to the third out of six levels. I assume by now you know how they\n    work. But now...\n\n**** ... prepare to have your mind blown!\n\n     Because here is something very special and unique to Neorg:\n\n***** Indentation reversion\n\n      As you would expect, this paragraph belongs to the fifth level heading.\n\n****** Final heading level\n\n       And this paragraph belongs to the sixth level. But by using the following modifier:\n       ---\n\n      We can move this text to the fifth level again! 🤯\n      ---\n\n     So using 3 or more `-` signs not followed by anything, you move *one* level backwards in the\n     indentation (*NOTE:* this should actually be `2` or more signs as per the latest spec revision,\n     but the parser is in the process of being updated).\n\n     Doing the same but with `=` characters instead all heading levels will be closed and\n     you will be dropped back to the root level of the document!\n\n** Horizontal Lines\n\n   You can also place horizontal lines using three or more underscores like so:\n   ___\n   This will never affect the indentation level of the following text, but it will immediately\n   terminate the paragraph which is why this is a new paragraph despite the absence of two (or more)\n   consecutive new lines.\n\n** Links\n\n   For more info on links check the\n   {https://github.com/nvim-neorg/norg-specs/blob/main/1.0-specification.norg#L1340}[spec].\n\n*** Link Targets\n\n    The following things can be used as link /targets/:\n    - `* Heading1` (+ nesting levels)\n    - `^ Footnote`\n    - `$ Definition`\n    - `# magic` (any of the above)\n    - `:path:# magic` (target in another norg file at a given path)\n    - `:path:` (another norg file at a given path without specific target)\n    - `/ path` (a non-norg file at a given path)\n    - `https://github.com` (a URL)\n    - `file:///some/path` (any file, opened via `(xdg-)open`)\n\n    Any of the paths used in `:path:` or `/ path` can be formatted in either of the following ways:\n    - `:path/to/norg/file:` relative to the file which contains this link\n    - `:/path/from/root:` absolute w.r.t. the entire filesystem\n    - `:~/path/from/user/home:` relative to the user's home directory (e.g. `/home/user` on Linux machines)\n    - `:../path/to/norg/file:` these paths also understand `../`\n    - `:$/path/from/current/workspace:` relative to current workspace root\n    - `:$gtd/path/in/gtd/workspace:` relative to the root of the workspace called `gtd`.\n\n** There are multiple ways of using links:\n\n*** Pure link location\n\n    An inline link to {* my heading}.\n\n    The text used for this link can be inferred from the /target/.\n    If it is not a norg-target, this falls back to the URL or filename, etc.\n\n*** Custom link text\n\n    An inline link {* my heading}[with custom text].\n\n    This links to the same marker but uses a custom link text.\n\n*** Anchors\n\n    A link to [our website].\n\n    Be sure to check out [our website]{https://github.com/nvim-neorg/neorg}!\n\n    The standalone link /text/ is called an *anchor declaration*.\n    It requires an *anchor definition* (last line in the code block) which defines where an anchored link points to.\n    This is very useful when you find yourself refering to the same target often.\n\n*** Examples:\n\n    {* Heading 1}\n    {** Heading 2}\n    {*** Heading 3}\n    {**** Heading 4}\n    {***** Heading 5}\n    {****** Heading 6}\n    {******* Heading level above 6}\n    {# Generic}\n    {$ Definition}\n    {^ Footnote}\n    {:norg_file:}\n    {:norg_file:* Heading 1}\n    {:norg_file:** Heading 2}\n    {:norg_file:*** Heading 3}\n    {:norg_file:**** Heading 4}\n    {:norg_file:***** Heading 5}\n    {:norg_file:****** Heading 6}\n    {:norg_file:******* Heading level above 6}\n    {:norg_file:# Generic}\n    {:norg_file:* Marker}\n    {:norg_file:$ Definition}\n    {:norg_file:^ Footnote}\n    {https://github.com/}\n    {file:///dev/null}\n    {/ external_file.txt}\n\n    Note that the following links are malformed:\n    {:norg_file:/ external_file.txt}\n    {:norg_file:https://github.com/}\n\n** Definitions\n\n   There are two kinds of definitions:\n\n*** Single-paragraph definitions\n\n    $ Term\n    The definition of the object in a single paragraph.\n\n    This is not considered part of the definition.\n\n*** Multi-paragraph definitions\n\n    $$ Term\n    Here, I can place any number of paragraphs or other format objects.\n\n    Even a code example:\n    @code lua\n    print(\"Hello world!\")\n    @end\n    $$\n\n    This is no longer part of the definition because the `$$` on the previous line marked its end.\n\n** Footnotes\n\n   There are also two kinds of footnotes:\n\n*** Single-paragraph footnotes\n\n    ^ This is the title of my footnote. I can use this as a link target.\n    This is the actual footnote content.\n\n    This is no longer part of the footnote.\n\n*** Multi-paragraph footnotes\n\n    ^^ This is a multi-paragraph footnote.\n    Here go the actual contents...\n\n    ... which I can even continue down here.\n    ^^\n\n    Now, the footnote has ended.\n\n** Data Tags\n\n   Neorg supports a number of tags. The general format is:\n\n   @data possible parameters\n   contents\n   @end\n\n*** Carryover Tags\n\n    Carryover tags are a variant of tags that apply some data to the next object.\n    The general syntax looks like this:\n\n    #tagname parameter1 parameter2\n    The object to apply the tag to.\n\n    Apart from just `#` you may also use the `+` character. It's best to use `#`\n    most of the time, as it applies recursively to the element you're tagging.\n    Use `+` only if you don't want your tag to apply recursively to the children\n    of an object.\n\n**** Name\n\n    #name awesome quotes\n    > This is a quote.\n    > We can talk about anything we like\n\n    This quote now has a /name/! You can link to it with the magic char: {# awesome quotes}!\n\n*** Code Blocks\n\n    @code\n    console.log(\"But I want syntax highlighting...\")\n    @end\n\n    @code javascript\n    console.log(\"Thank you!\")\n    @end\n\n*** Media\n\n    *NOTE: Media is currently supported only via {https://github.com/3rd/image.nvim}[image.nvim].*\n\n    You can embed images directly in base64 format like so:\n    @image png svg jpeg jfif exif\n    <base64-encoded image data>\n    @end\n\n    Obviously you need to pick one of the available formats.\n\n    You can embed external image or video files like so:\n    .image https://raw.githubusercontent.com/nvim-neorg/neorg/main/res/neorg.svg\n\n*** Math\n\n    There are two ways of typesetting mathematics:\n    ~ Inline mathematics using the `$` attached modifier like so: $f(x) = y$.\n       To access LaTeX-typeset math within inline mathematics, use the [pipe] modifier:\n       $|\\theta = \\frac{\\pi}{3}|$\n    ~ Multi-line mathematics using the `math` ranged tag which supports any LaTeX-typeset math.\n      @math\n      f(x) = y\n      @end\n\n** Advanced markup\n\n   There are some more advanced markup features:\n\n*** The Link modifier\n\n    If you want to mark-up text which is not surrounded by punctuation or whitespace, you need to\n    use the *link* modifier, `:`, like so:\n\n    W:*h*:y w:/oul/:d a:_nyon_:e w:-an-:t to do t:`hi`:s?\n\n*** Nested markup\n\n    You can nest multiple markup groups to combine their effect. Some examples:\n    - *Text can be bold _and underlined_!*\n    - You can create ,/italic subscripts/,.\n    - If you want to shout on the internet, use */_DOUBLY EMPHASIZED AND UNDERLINED CAPS_/*\n\n    Note: You can:*not* combine sub- and superscripts like so:\n\n    ^,This should be super- and subscript.,^ - gets highlighted as an error.\n\n*** Variables\n\n    *NOTE: Variables are not yet implemented in Neorg*\n\n    You can define variables which you can access later in your document like so:\n\n    @code norg\n    =variable-name\n    value\n    =end\n    @end\n\n    You can refer to this variable later in your document using the `&` attached modifier like so:\n\n    @code norg\n    Insert my &variable&.\n    @end\n\n    #comment\n    vim:tw=100:ft=norg:norl:conceallevel=0:concealcursor=:\n"
  },
  {
    "path": "doc/tags",
    "content": "neorg\tneorg.norg\t/* The `.norg` File Format\nneorg-advanced-markup\tneorg.norg\t/** Advanced Markup\nneorg-anchors\tneorg.norg\t/*** Anchors\nneorg-basic-markup\tneorg.norg\t/** Basic Markup\nneorg-breaking-changes\tbreaking-changes.norg\t/*NOTE:\nneorg-carryover-tags\tneorg.norg\t/*** Carryover\nneorg-code-blocks\tneorg.norg\t/*** Code Blocks\nneorg-data-tags\tneorg.norg\t/** Data Tags\nneorg-definitions\tneorg.norg\t/** Definitions\nneorg-footnotes\tneorg.norg\t/** Footnotes\nneorg-headings\tneorg.norg\t/*** Headings\nneorg-indentation-reversal\tneorg.norg\t/***** Indentation reversion\nneorg-link-descriptions\tneorg.norg\t/*** Custom link text\nneorg-link-examples\tneorg.norg\t/*** Examples\nneorg-link-locations\tneorg.norg\t/*** Pure link location\nneorg-link-modifier\tneorg.norg\t/*** The Link modifier\nneorg-link-targets\tneorg.norg\t/*** Link Targets\nneorg-links\tneorg.norg\t/** Links\nneorg-math\tneorg.norg\t/*** Math\nneorg-media\tneorg.norg\t/*** Media\nneorg-name-tag\tneorg.norg\t/*** Name\nneorg-nested-markup\tneorg.norg\t/*** Nested markup\nneorg-ordered-lists\tneorg.norg\t/*** Ordered lists\nneorg-quotes\tneorg.norg\t/*** Quotes\nneorg-tasks\tneorg.norg\t/*** Tasks\nneorg-unordered-lists\tneorg.norg\t/*** Unordered lists\nneorg-variables\tneorg.norg\t/*** Variables\n\nneorg-cheatsheet\tcheatsheet.norg\t/* Your Quick Neorg Guide\nneorg-cheatsheet-headings\tcheatsheet.norg\t/** Headings\nneorg-cheatsheet-reverse-headings\tcheatsheet.norg\t/** Reverse Headings\nneorg-cheatsheet-lists\tcheatsheet.norg\t/** Lists\nneorg-cheatsheet-code-blocks\tcheatsheet.norg\t/** Code Blocks\nneorg-cheatsheet-styling\tcheatsheet.norg\t/** Styling\nneorg-cheatsheet-links-urls\tcheatsheet.norg\t/*** Links to URLs\nneorg-cheatsheet-links-objects\tcheatsheet.norg\t/*** Links to Objects\nneorg-cheatsheet-links-non-norg\tcheatsheet.norg\t/*** Links to non-norg Files\nneorg-cheatsheet-links-descriptions\tcheatsheet.norg\t/*** Links with Descriptions\nneorg-cheatsheet-links-anything\tcheatsheet.norg\t/*** Links to Anything\nneorg-cheatsheet-links-files\tcheatsheet.norg\t/*** Links across Files\nneorg-cheatsheet-anchors\tcheatsheet.norg\t/** Anchors\nneorg-cheatsheet-tasks\tcheatsheet.norg\t/* Tasks\n"
  },
  {
    "path": "docgen/docgen.lua",
    "content": "local neorg = require(\"neorg.core\")\n\nlocal docgen = {}\n\n-- Create the directory if it does not exist\ndocgen.output_dir = \"../wiki\"\ndocgen.static_dir = \"../res/wiki/static\"\npcall(vim.fn.mkdir, docgen.output_dir)\n\n-- Copy static wiki resources into the wiki\nvim.loop.fs_scandir(docgen.static_dir, function(err, handle)\n    assert(handle, err) -- will not kill docgen on fail, because it is within async callback\n\n    local name, type = vim.loop.fs_scandir_next(handle)\n    while name do\n        if type == \"file\" then\n            assert(vim.loop.fs_copyfile(docgen.static_dir .. \"/\" .. name, docgen.output_dir .. \"/\" .. name))\n        end\n        name, type = vim.loop.fs_scandir_next(handle)\n    end\nend)\n\nrequire(\"neorg\").setup({\n    load = {\n        [\"core.defaults\"] = {},\n        [\"core.integrations.treesitter\"] = {\n            config = {\n                configure_parsers = false,\n            },\n        },\n    },\n})\n\nlocal lib, modules, utils, log = neorg.lib, neorg.modules, neorg.utils, neorg.log\n\n-- Start neorg\nneorg.org_file_entered(false)\n\n-- Extract treesitter utility functions provided by Neorg\n---@type core.integrations.treesitter\nlocal ts = modules.get_module(\"core.integrations.treesitter\")\nassert(ts, \"treesitter not available\")\n\n--- Aggregates all the available modules.\n---@return table #A list of paths to every module's `module.lua` file\ndocgen.aggregate_module_files = function()\n    return vim.fs.find(\"module.lua\", {\n        path = \"..\",\n        type = \"file\",\n        limit = math.huge,\n    })\nend\n\n--- Opens a file from a given path in a new buffer\n---@param path string #The path of the file to open\n---@return number #The buffer ID of the opened file\ndocgen.open_file = function(path)\n    local uri = vim.uri_from_fname(path)\n    local buf = vim.uri_to_bufnr(uri)\n    vim.fn.bufload(buf)\n\n    return buf\nend\n\n--- Get the first comment (at line 0) from a module and get it's content\n--- @param buf number #The buffer number to read from\n--- @return table? #A table of lines\ndocgen.get_module_top_comment = function(buf)\n    local node = ts.get_first_node_recursive(\"comment\", { buf = buf, ft = \"lua\" })\n\n    if not node then\n        return\n    end\n\n    -- Verify if it's the first line\n    local start_row = node:range()\n    if start_row ~= 0 then\n        return\n    end\n\n    local comment = vim.split(ts.get_node_text(node, buf), \"\\n\")\n\n    -- Stops execution if it's not a multiline comment\n    if comment[1] ~= [[--[[]] or comment[#comment] ~= \"--]]\" then\n        return\n    end\n\n    -- Removes first and last braces\n    table.remove(comment, 1)\n    table.remove(comment, #comment)\n\n    return comment\nend\n\n---@alias TopComment { file: string, title: string, summary: string, description: string, embed: string, markdown: string[], internal: boolean }\n\n--- Parses the top comment\n---@param comment string[] #The comment\n---@return TopComment #The parsed comment\ndocgen.parse_top_comment = function(comment)\n    ---@type TopComment\n    local result = {\n        -- file = \"\",\n        -- title = \"\",\n        -- summary = \"\",\n        markdown = {},\n    }\n    local can_have_options = true\n\n    for _, line in ipairs(comment) do\n        if line:match(\"^%s*%-%-%-%s*$\") then\n            can_have_options = false\n        else\n            local option_name, value = line:match(\"^%s*(%w+):%s*(.+)\")\n\n            if vim.tbl_contains({ \"true\", \"false\" }, value) then\n                value = (value == \"true\")\n            end\n\n            if option_name and can_have_options then\n                result[option_name:lower()] = value\n            else\n                table.insert(result.markdown, line)\n            end\n        end\n    end\n\n    return result\nend\n\n--- Ensures that the top comment of each Neorg module follows a certain set of rules.\n---@param top_comment TopComment #The comment to check for errors\n---@return string|TopComment #An error string or the comment itself\ndocgen.check_top_comment_integrity = function(top_comment)\n    local tc = vim.tbl_deep_extend(\"keep\", top_comment, {\n        title = \"\",\n        summary = \"\",\n        markdown = {},\n    })\n\n    if not tc.file then\n        return \"no `File:` field provided.\"\n    elseif tc.summary:sub(1, 1):upper() ~= tc.summary:sub(1, 1) then\n        return \"summary does not begin with a capital letter.\"\n    elseif tc.summary:sub(tc.summary:len()) ~= \".\" then\n        return \"summary does not end with a full stop.\"\n    elseif tc.title:find(\"neorg\") then\n        return \"`neorg` written with lowercase letter. Use uppercase instead.\"\n        -- elseif vim.tbl_isempty(tc.markdown) then\n        --     return \"no overview provided.\"\n    end\n\n    return top_comment\nend\n\n--- Retrieves the TS node corresponding to the `module.config.public` treesitter node\n---@param buffer number #Buffer ID\n---@param root TSNode #The root node\n---@return TSNode? #The `module.config.public` node\ndocgen.get_module_config_node = function(buffer, root)\n    local query = utils.ts_parse_query(\n        \"lua\",\n        [[\n        (assignment_statement\n          (variable_list) @_name\n          (#eq? @_name \"module.config.public\")) @declaration\n    ]]\n    )\n\n    local _, declaration_node = query:iter_captures(root, buffer)()\n\n    return declaration_node and declaration_node:named_child(1):named_child(0) or nil\nend\n\n---@alias ConfigOptionData { node: TSNode, name: string, value: userdata, parents: string[] }\n\n--- Recursively maps over each item in the `module.config.public` table,\n--  invoking a callback on each run. Also descends down recursive tables.\n---@param buffer number #Buffer ID\n---@param start_node table #The node to start parsing\n---@param callback fun(ConfigOptionData, table) #Invoked on each node with the corresponding data\n---@param parents string[]? #Used internally to track nesting levels\ndocgen.map_config = function(buffer, start_node, callback, parents)\n    parents = parents or {}\n\n    ---@type string[]\n    local comments = {}\n    local index = 1\n\n    for node in start_node:iter_children() do\n        if node:type() == \"comment\" then\n            table.insert(comments, ts.get_node_text(node, buffer))\n        elseif node:type() == \"field\" then\n            local name_node = node:field(\"name\")[1]\n\n            -- If a node does not have an associated name, then\n            -- it's part of a list\n            if name_node and name_node:type() == \"string\" then\n                name_node = name_node:field(\"content\")[1]\n            end\n\n            local name = name_node and ts.get_node_text(name_node, buffer) or nil\n            local value = node:field(\"value\")[1]\n\n            -- If the right hand side of the expression is a table\n            -- then go down it recursively\n            if value:type() == \"table_constructor\" then\n                callback({\n                    node = node,\n                    name = name,\n                    value = value,\n                    parents = parents,\n                }, comments)\n\n                -- The deepcopy is necessary or else\n                -- the parents table would be overwritten in-place\n                local copy = vim.deepcopy(parents)\n                table.insert(copy, name or index)\n                docgen.map_config(buffer, value, callback, copy)\n            else\n                callback({\n                    node = node,\n                    name = name,\n                    value = value,\n                    parents = parents,\n                }, comments)\n            end\n\n            comments = {}\n            index = index + 1\n        else\n            comments = {}\n        end\n    end\nend\n\n--- Goes through a table and evaluates all functions in that table, merging the\n--  return values back into the original table.\n---@param tbl table #Input table\n---@return table #The new table\ndocgen.evaluate_functions = function(tbl)\n    local new = {}\n\n    lib.map(tbl, function(_, value)\n        if type(value) == \"function\" then\n            vim.list_extend(new, value())\n        else\n            table.insert(new, value)\n        end\n    end)\n\n    return new\nend\n\n---@alias Module { top_comment_data: TopComment, buffer: number, parsed: table }\n---@alias Modules { [string]: Module }\n\n--- Returns a function which itself returns a table of links to modules\n--  in a markdown-like unordered list.\n---@param mods Modules #A table of modules to enumerate\n---@param predicate fun(Module):boolean #A predicate that determines whether or not to render this object.\n--- If the predicate returns false, then the object is dismissed.\n---@return fun():string[] #An array of markdown strings with the enumerated modules\nlocal function list_modules_with_predicate(mods, predicate)\n    local sorted = lib.unroll(mods)\n\n    table.sort(sorted, function(x, y)\n        return x[1] < y[1]\n    end)\n\n    return function()\n        local res = {}\n\n        for _, kv_pair in ipairs(sorted) do\n            local mod = kv_pair[1]\n            local data = kv_pair[2]\n\n            if predicate and predicate(data) then\n                local insert\n\n                if data.top_comment_data.file then\n                    insert = \"- [`\"\n                        .. data.parsed.name\n                        .. \"`](https://github.com/nvim-neorg/neorg/wiki/\"\n                        .. data.top_comment_data.file\n                        .. \")\"\n                else\n                    insert = \"- `\" .. mod .. \"`\"\n                end\n\n                if data.top_comment_data.summary then\n                    insert = insert .. \" - \" .. data.top_comment_data.summary\n                else\n                    insert = insert .. \" - undocumented module\"\n                end\n\n                table.insert(res, insert)\n            end\n        end\n\n        return res\n    end\nend\n\ndocgen.generators = {\n    --- Generates the Home.md file\n    ---@param mods Modules #A table of modules\n    homepage = function(mods)\n        local core_defaults = mods[\"core.defaults\"]\n        assert(core_defaults, \"core.defaults module not loaded!\")\n\n        local structure = {\n            '<div align=\"center\">',\n            \"\",\n            \"# Welcome to the Neorg wiki!\",\n            \"Want to know how to properly use Neorg? Your answers are contained here.\",\n            \"\",\n            \"</div>\",\n            \"\",\n            \"# Kickstart\",\n            \"\",\n            \"If you would like a Neovim setup that has Neorg configured out of the box look no further than the [kickstart guide](https://github.com/nvim-neorg/neorg/wiki/Kickstart)!\",\n            \"\",\n            \"# Using Neorg\",\n            \"\",\n            \"Neorg depends on a number of other technologies, all of which have to be correctly configured to keep Neorg running smoothly.\",\n            \"For some help on understanding how your terminal, Neovim, colourschemes, tree-sitter and more come together to produce your Neorg experience (or Neorg problems), see [this document on understanding Neorg dependencies](Dependencies).\",\n            \"\",\n            \"At first configuring Neorg might be rather scary. I have to define what modules I want to use in the `require('neorg').setup()` function?\",\n            \"I don't even know what the default available values are!\",\n            \"Don't worry, there are guides you are free to check out. The [tutorial](https://github.com/nvim-neorg/neorg/wiki/Tutorial) guides you through what Neorg is and its basics.\",\n            \"Afterwards, feel free to check out the [configuration guide](https://github.com/nvim-neorg/neorg/wiki/Setup-Guide) as well as the [cookbook](https://github.com/nvim-neorg/neorg/wiki/Cookbook).\",\n            \"\",\n            \"# Broken Installation\",\n            \"\",\n            \"Having issues when installing Neorg, specifically past the `8.0.0` version? Check out the [following page](https://github.com/pysan3/Norg-Tutorial/blob/main/MIGRATION-v8.md) where you can troubleshoot your issue from start to finish.\",\n            \"\",\n            \"# Contributing to Neorg\",\n            \"\",\n            \"Neorg is a very big and powerful tool behind the scenes - way bigger than it may initially seem.\",\n            \"Modules are its core foundation, and building modules is like building lego bricks to form a massive structure.\",\n            \"There's an in-the-works tutorial dedicated to making modules [right here](https://github.com/andreadev-it/neorg-module-tutorials/blob/main/introduction.md)!\",\n            \"\",\n            \"# Module naming convention\",\n            \"\",\n            \"Neorg provides default modules, and users can extend Neorg by creating community modules.\",\n            \"We agreed on a module naming convention, and it should be used as is.\",\n            \"This convention should help users know at a glance what function the module serves in the grand scheme of things.\",\n            \"- Core modules: `core.*`\",\n            \"- Integrations with 3rd party software that are embedded in neorg: `core.integrations.*`\",\n            \"- External modules: `external.*`\",\n            \"- Integrations with 3rd party software that aren't embedded in neorg: `external.integrations.*`\",\n            \"\",\n            \"# Default Modules\",\n            \"\",\n            function()\n                local link = \"[`core.defaults`](https://github.com/nvim-neorg/neorg/wiki/\"\n                    .. core_defaults.top_comment_data.file\n                    .. \")\"\n                return {\n                    \"Neorg comes with some default modules that will be automatically loaded if you require the \"\n                        .. link\n                        .. \" module:\",\n                }\n            end,\n            \"\",\n            list_modules_with_predicate(mods, function(data)\n                return vim.tbl_contains(core_defaults.parsed.config.public.enable, data.parsed.name)\n                    and not data.top_comment_data.internal\n            end),\n            \"\",\n            \"# Other Modules\",\n            \"\",\n            \"Some modules are not included by default as they require some manual configuration or are merely extra bells and whistles\",\n            \"and are not critical to editing `.norg` files. Below is a list of all modules that are not required by default:\",\n            \"\",\n            list_modules_with_predicate(mods, function(data)\n                return not data.parsed.extension\n                    and not vim.tbl_contains(core_defaults.parsed.config.public.enable, data.parsed.name)\n                    and not data.top_comment_data.internal\n            end),\n            \"\",\n            \"# Developer modules\",\n            \"\",\n            \"These are modules that are only meant for developers. They are generally required in other modules:\",\n            \"\",\n            list_modules_with_predicate(mods, function(data)\n                return not data.parsed.extension and data.top_comment_data.internal\n            end),\n        }\n\n        return docgen.evaluate_functions(structure)\n    end,\n\n    --- Generates the _Sidebar.md file\n    ---@param mods Modules #A table of modules\n    sidebar = function(mods)\n        local structure = {\n            \"<div align='center'>\",\n            \"\",\n            \"# :star2: Neorg\",\n            \"</div>\",\n            \"\",\n            \"\",\n            \"- [Setup Guide](https://github.com/nvim-neorg/neorg/wiki/Setup-Guide)\",\n            \"- [Tutorial](https://github.com/nvim-neorg/neorg/wiki/Tutorial)\",\n            \"- [Default Keybinds](https://github.com/nvim-neorg/neorg/wiki/Default-Keybinds)\",\n            \"\",\n            \"<details>\",\n            \"<summary><h3>Inbuilt modules:</h3></summary>\",\n            \"\",\n            function()\n                local res = {}\n                local names = {}\n\n                for n, data in pairs(mods) do\n                    if data.parsed.extension ~= true then\n                        table.insert(names, n)\n                    end\n                end\n\n                table.sort(names)\n\n                for _, name in ipairs(names) do\n                    local data = mods[name]\n                    if not data.parsed.internal then\n                        local insert = \"\"\n                        if data.top_comment_data.file then\n                            insert = insert\n                                .. \"- [`\"\n                                .. data.parsed.name\n                                .. \"`](https://github.com/nvim-neorg/neorg/wiki/\"\n                                .. data.top_comment_data.file\n                                .. \")\"\n                        else\n                            insert = insert .. \"- `\" .. name .. \"`\"\n                        end\n\n                        table.insert(res, insert)\n                    end\n                end\n\n                return res\n            end,\n            \"\",\n            \"</details>\",\n        }\n\n        return docgen.evaluate_functions(structure)\n    end,\n\n    --- Generates the page for any Neorg module\n    ---@param mods Modules #The list of currently loaded modules\n    ---@param module Module #The module we want to generate the page for\n    ---@param configuration string[] #An array of markdown strings detailing the configuration options for the module\n    ---@return string[] #A table of markdown strings representing the page\n    module = function(mods, module, configuration)\n        local structure = {\n            '<div align=\"center\">',\n            \"\",\n            \"# `\" .. module.parsed.name .. \"`\",\n            \"\",\n            \"### \" .. (module.top_comment_data.title or \"\"),\n            \"\",\n            module.top_comment_data.description or \"\",\n            \"\",\n            module.top_comment_data.embed and (\"![module-showcase](\" .. module.top_comment_data.embed .. \")\") or \"\",\n            \"\",\n            \"</div>\",\n            \"\",\n            function()\n                if module.top_comment_data.markdown and not vim.tbl_isempty(module.top_comment_data.markdown) then\n                    return vim.list_extend({\n                        \"# Overview\",\n                        \"\",\n                    }, module.top_comment_data.markdown)\n                end\n\n                return {}\n            end,\n            \"\",\n            \"# Configuration\",\n            \"\",\n            function()\n                if vim.tbl_isempty(configuration) then\n                    return {\n                        \"This module provides no configuration options!\",\n                    }\n                else\n                    return configuration\n                end\n            end,\n            \"\",\n            function()\n                local required_modules = module.parsed.setup().requires or {}\n\n                if vim.tbl_isempty(required_modules) then\n                    return {}\n                end\n\n                local module_list = {}\n\n                for _, module_name in ipairs(required_modules) do\n                    module_list[module_name] = mods[module_name]\n                end\n\n                return docgen.evaluate_functions({\n                    \"# Dependencies\",\n                    \"\",\n                    list_modules_with_predicate(module_list, function()\n                        return true\n                    end),\n                })\n            end,\n            \"\",\n            function()\n                local required_by = {}\n\n                for mod, data in pairs(mods) do\n                    local required_modules = data.parsed.setup().requires or {}\n\n                    if vim.tbl_contains(required_modules, module.parsed.name) then\n                        required_by[mod] = data\n                    end\n                end\n\n                if vim.tbl_isempty(required_by) then\n                    return {}\n                end\n\n                return docgen.evaluate_functions({\n                    \"# Required By\",\n                    \"\",\n                    list_modules_with_predicate(required_by, function()\n                        return true\n                    end),\n                })\n            end,\n        }\n\n        return docgen.evaluate_functions(structure)\n    end,\n\n    keybinds = function(mods, buffer)\n        local keybind_data = docgen.parse_keybind_data(buffer)\n\n        local layout = {\n            '<div align=\"center\">',\n            \"\",\n            \"# :keyboard: Neorg Keybinds :keyboard:\",\n            \"A comprehensive list of all keys available in Neorg.\",\n            \"\",\n            \"</div>\",\n            \"\",\n            \"### Further Reading\",\n            \"\",\n            docgen.lookup_modules(\n                mods,\n                \"To find out how to rebind the available keys consult the [`core.keybinds`](@core.keybinds) wiki entry.\"\n            ),\n            \"\",\n        }\n\n        for preset_name, preset_data in vim.spairs(keybind_data) do\n            for neorg_mode_name, neorg_mode_data in vim.spairs(preset_data) do\n                if neorg_mode_name == \"all\" then\n                    table.insert(layout, string.format(\"## Preset `%s` // All Files\", preset_name))\n                    table.insert(layout, \"\")\n                elseif neorg_mode_name == \"norg\" then\n                    table.insert(layout, string.format(\"## Preset `%s` // Norg Only\", preset_name))\n                    table.insert(layout, \"\")\n                end\n\n                for mode_name, mode_data in vim.spairs(neorg_mode_data) do\n                    mode_name = lib.match(mode_name)({\n                        n = \"Normal Mode\",\n                        i = \"Insert Mode\",\n                        v = \"Visual Mode\",\n                    })\n\n                    table.insert(layout, \"### \" .. mode_name)\n                    table.insert(layout, \"\")\n\n                    for key, data in vim.spairs(mode_data) do\n                        if not vim.tbl_isempty(data.comments) then\n                            local comments = vim.iter(data.comments)\n                                :map(function(comment)\n                                    return (comment:gsub(\"^%s*%-%-%s*\", \"\"))\n                                end)\n                                :totable()\n\n                            local mnemonic = docgen.extract_mnemonic(comments)\n\n                            local summary = comments[1]:sub(1, 1):lower() .. comments[1]:sub(2)\n                            local description = vim.list_slice(comments, 2)\n                            local err = docgen.check_comment_integrity(summary)\n\n                            if err then\n                                log.error(\"Invalid keybind description:\", err)\n                            end\n\n                            table.insert(layout, \"<details>\")\n                            table.insert(layout, \"<summary>\")\n                            table.insert(layout, \"\")\n                            table.insert(layout, string.format(\"#### `%s` - %s\", key, summary))\n                            table.insert(layout, \"\")\n                            table.insert(layout, \"</summary>\")\n                            table.insert(layout, \"\")\n                            vim.list_extend(layout, description)\n                            table.insert(layout, string.format(\"- Maps to: `%s`\", data.rhs))\n                            if mnemonic then\n                                table.insert(\n                                    layout,\n                                    string.format(\"- Mnemonic: <code>%s</code>\", docgen.format_mnemonic(mnemonic))\n                                )\n                            end\n                            table.insert(layout, \"\")\n                            table.insert(layout, \"</details>\")\n                            table.insert(layout, \"\")\n                        end\n                    end\n                end\n            end\n        end\n\n        return layout\n    end,\n}\n\n--- Check the integrity of the description comments found in configuration blocks\n---@param comment string #The comment to check the integrity of\n---@return nil|string #`nil` for success, `string` if there was an error\ndocgen.check_comment_integrity = function(comment)\n    if comment:match(\"^%s*%-%-+%s*\") then\n        return \"found leading `--` comment text.\"\n    elseif comment:sub(1, 1):upper() ~= comment:sub(1, 1) then\n        return \"comment does not begin with a capital letter.\"\n    elseif comment:find(\" neorg \") then\n        return \"`neorg` written with lowercase letter. Use uppercase instead.\"\n    end\nend\n\n--- Replaces all instances of a module reference (e.g. `@core.concealer`) with a link in the wiki\n---@param mods Modules #The list of loaded modules\n---@param str string #The string to perform the lookup in\n---@return string #The original `str` parameter with all `@` references replaced with links\ndocgen.lookup_modules = function(mods, str)\n    return (\n        str:gsub(\"@([%-%.%w]+)\", function(target_module_name)\n            if not mods[target_module_name] then\n                return table.concat({ \"@\", target_module_name })\n            else\n                return table.concat({\n                    \"https://github.com/nvim-neorg/neorg/wiki/\",\n                    mods[target_module_name].top_comment_data.file,\n                })\n            end\n        end)\n    )\nend\n\n--- Renders a treesitter node to a lua object\n---@param node userdata #The node to render\n---@param chunkname string? #The custom name to give to the chunk\n---@return any #The converted object\ndocgen.to_lua_object = function(module, buffer, node, chunkname)\n    local loaded = loadstring(table.concat({ \"return \", ts.get_node_text(node, buffer) }), chunkname)\n\n    if loaded then\n        return setfenv(loaded, vim.tbl_extend(\"force\", getfenv(0), { module = module }))()\n    end\nend\n\n---@alias ConfigOptionArray { [string|number]: ConfigOptionArray, self: ConfigOption }\n---@alias ConfigOption { buffer: number, data: ConfigOptionData, comments: string[], object: any }\n\n--- Converts a ConfigOptionData struct to a html node in the resulting HTML document\n---@param configuration_option ConfigOptionArray #The config option to render\n---@param open boolean? #Whether to auto-open the generated `<details>` tag. Defaults to false.\n---@return string[] #A list of markdown tables corresponding to the rendered element.\ndocgen.render = function(configuration_option, open)\n    open = open or false\n\n    local self = configuration_option.self\n\n    local type_of_object = (function()\n        local t = type(self.object)\n\n        if t == \"table\" then\n            return (vim.tbl_isempty(self.object) and \"empty \" or \"\") .. (vim.islist(self.object) and \"list\" or \"table\")\n        else\n            return t\n        end\n    end)()\n\n    local basis = {\n        \"* <details\" .. (open and \" open>\" or \">\"),\n        \"\",\n        ((self.data.name or \"\"):match(\"^%s*$\") and \"<summary>\" or table.concat({\n            \"<summary><h6><code>\",\n            self.data.name,\n            \"</h6></code>\",\n        })) .. \" (\" .. type_of_object .. \")</summary>\",\n        \"\",\n    }\n\n    if not vim.tbl_isempty(self.comments) then\n        vim.list_extend(basis, {\n            \"<div>\",\n            \"\",\n        })\n\n        vim.list_extend(basis, self.comments)\n        vim.list_extend(basis, {\n            \"\",\n            \"</div>\",\n            \"\",\n        })\n    else\n        vim.list_extend(basis, {\n            \"<br>\",\n            \"\",\n        })\n    end\n\n    vim.list_extend(basis, docgen.htmlify(configuration_option))\n    vim.list_extend(basis, {\n        \"\",\n        \"</details>\",\n    })\n\n    for i, str in ipairs(basis) do\n        basis[i] = string.rep(\" \", 2 - (i == 1 and 2 or 0)) .. str\n    end\n\n    return basis\nend\n\n--- Converts an object directly into HTML, with no extra fluff.\n---@param configuration_option ConfigOptionArray\n---@return string[] #An array of markdown strings with the rendered HTML inside\ndocgen.htmlify = function(configuration_option)\n    local self = configuration_option.self\n\n    local result = {}\n    local code_block = true\n\n    lib.match(self.data.value:type())({\n        string = function()\n            table.insert(result, table.concat({ '\"', self.object, '\"' }))\n        end,\n        table_constructor = function()\n            table.insert(result, \"\")\n\n            local unrolled = lib.unroll(self.object)\n\n            table.sort(unrolled, function(x, y)\n                return tostring(x[1]) < tostring(y[1])\n            end)\n\n            for _, data in ipairs(unrolled) do\n                ---@type number|string\n                local name_or_index = data[1]\n\n                local subitem = configuration_option[name_or_index]\n\n                if subitem then\n                    vim.list_extend(result, docgen.render(subitem))\n                end\n            end\n\n            table.insert(result, \"\")\n\n            code_block = false\n        end,\n        function_definition = function()\n            local text = ts.get_node_text(self.data.value, self.buffer):match(\"^function%s*(%b())\")\n\n            if not text then\n                log.error(string.format(\"Unable to parse function, perhaps some wrong formatting?\"))\n                table.insert(result, \"<error: incorrect formatting>\")\n                return\n            end\n\n            table.insert(result, \"function\" .. text)\n        end,\n        _ = function()\n            table.insert(result, ts.get_node_text(self.data.value, self.buffer))\n        end,\n    })\n\n    if code_block then\n        table.insert(result, 1, \"```lua\")\n        table.insert(result, \"```\")\n    end\n\n    return result\nend\n\n--- Parses keybind data and returns it in a readable format.\n---@param buffer number The buffer ID to extract information from.\n---@return table<string, table>\ndocgen.parse_keybind_data = function(buffer)\n    local query = utils.ts_parse_query(\n        \"lua\",\n        [[\n        (field\n          name: (identifier) @_ident\n          (#eq? @_ident \"presets\")) @presets\n        ]]\n    )\n\n    local root = assert(vim.treesitter.get_parser(buffer, \"lua\"):parse()[1]:root(), \"unable to parse keybinds!\")\n\n    local _, presets = query:iter_captures(root, buffer)()\n    assert(presets, \"could not find presets\")\n\n    local available_keys = neorg.modules.loaded_modules[\"core.keybinds\"].private.presets\n\n    local output = vim.defaulttable()\n\n    for preset in presets:named_child(1):iter_children() do\n        if preset:type() == \"field\" then\n            local preset_name, preset_data =\n                vim.treesitter.get_node_text(assert(preset:named_child(0)), buffer), preset:named_child(1)\n\n            for neorg_mode in assert(preset_data):iter_children() do\n                if neorg_mode:type() == \"field\" then\n                    local neorg_mode_name, neorg_mode_data =\n                        vim.treesitter.get_node_text(assert(neorg_mode:named_child(0)), buffer),\n                        neorg_mode:named_child(1)\n\n                    for neovim_mode in assert(neorg_mode_data):iter_children() do\n                        if neovim_mode:type() == \"field\" then\n                            local mode_name, mode_data =\n                                vim.treesitter.get_node_text(assert(neovim_mode:named_child(0)), buffer),\n                                neovim_mode:named_child(1)\n\n                            local comments = {}\n                            local i, keybind_data\n\n                            for comment_or_data in assert(mode_data):iter_children() do\n                                if comment_or_data:type() == \"comment\" then\n                                    table.insert(\n                                        comments,\n                                        vim.trim(vim.treesitter.get_node_text(comment_or_data, buffer))\n                                    )\n                                elseif comment_or_data:type() == \"field\" then\n                                    i, keybind_data = next(available_keys[preset_name][neorg_mode_name][mode_name], i)\n                                    output[preset_name][neorg_mode_name][mode_name][keybind_data[1]] = {\n                                        comments = comments,\n                                        rhs = keybind_data[2],\n                                    }\n                                    comments = {}\n                                end\n                            end\n                        end\n                    end\n                end\n            end\n        end\n    end\n    return output\nend\n\ndocgen.format_mnemonic = function(str)\n    return str:gsub(\"([A-Z])\", \"<strong>%1</strong>\"):lower()\nend\n\ndocgen.extract_mnemonic = function(comments)\n    for i, comment in ipairs(comments) do\n        local mnemonic = comment:match(\"^%s*%^(.+)\")\n\n        if mnemonic then\n            table.remove(comments, i)\n            return mnemonic\n        end\n    end\nend\n\nreturn docgen\n"
  },
  {
    "path": "docgen/fileio.lua",
    "content": "---@author vhyrro\n---@license GPLv3\n\nlocal io = {}\n\nio.write_to_wiki = function(filename, content)\n    vim.fn.writefile(content, \"../wiki/\" .. filename .. \".md\")\nend\n\nreturn io\n"
  },
  {
    "path": "docgen/init.lua",
    "content": "local docgen = require(\"docgen\")\nlocal fileio = require(\"fileio\")\n\nlocal neorg = require(\"neorg.core\")\nlocal lib, modules = neorg.lib, neorg.modules\n\n--- CONFIGURABLE DOCGEN BEHAVIOUR\n--- Tweak as you see fit.\nlocal config = {\n    --- When true, will auto-unfold the top-level <details>\n    --- tags generated when rendering module configuration options.\n    auto_open_first_level_tags = true,\n}\n\n---@type Modules\nlocal doc_modules = {\n    --[[\n    [name] = {\n        top_comment_data...\n        buffer = id,\n        parsed = `ret value from sourcing the file`,\n    }\n    --]]\n}\n\n--- Fully renders a large set of configuration options\n---@param configuration_options ConfigOptionArray[] An array of ConfigOptionArrays\n---@return string[] #An array of markdown strings corresponding to all of the rendered configuration options\nlocal function concat_configuration_options(configuration_options)\n    local result = {}\n\n    local unrolled = lib.unroll(configuration_options)\n\n    table.sort(unrolled, function(x, y)\n        return x[1] < y[1]\n    end)\n\n    for _, values in pairs(unrolled) do\n        vim.list_extend(result, docgen.render(values[2], config.auto_open_first_level_tags))\n        table.insert(result, \"\")\n    end\n\n    return result\nend\n\nfor _, file in ipairs(docgen.aggregate_module_files()) do\n    local fullpath = vim.fn.fnamemodify(file, \":p\")\n\n    local buffer = docgen.open_file(fullpath)\n\n    local top_comment = docgen.get_module_top_comment(buffer)\n\n    if not top_comment then\n        vim.notify(\"no top comment found for module \" .. file)\n        goto continue\n    end\n\n    local top_comment_data = docgen.check_top_comment_integrity(docgen.parse_top_comment(top_comment))\n\n    if type(top_comment_data) == \"string\" then\n        vim.notify(\"Error when parsing module '\" .. file .. \"': \" .. top_comment_data)\n        goto continue\n    end\n\n    -- Source the module file to retrieve some basic information like its name\n    local ok, parsed_module = pcall(dofile, fullpath)\n\n    if not ok then\n        vim.notify(\"Error when sourcing module '\" .. file .. \"': \" .. parsed_module)\n        goto continue\n    end\n\n    -- Make Neorg load the module, which also evaluates dependencies\n    local _ok, err = pcall(modules.load_module, parsed_module.name)\n\n    if not _ok then\n        vim.notify(\"Error when loading module '\" .. file .. \"': \" .. err)\n        goto continue\n    end\n\n    -- Retrieve the module from the `loaded_modules` table.\n    parsed_module = modules.loaded_modules[parsed_module.name]\n\n    if parsed_module then\n        doc_modules[parsed_module.name] = {\n            top_comment_data = top_comment_data,\n            buffer = buffer,\n            parsed = parsed_module,\n        }\n    end\n\n    ::continue::\nend\n\n-- Non-module pages have their own dedicated generators\nfileio.write_to_wiki(\"Home\", docgen.generators.homepage(doc_modules))\nfileio.write_to_wiki(\n    \"Default-Keybinds\",\n    docgen.generators.keybinds(\n        doc_modules,\n        docgen.open_file(vim.fn.fnamemodify(\"../lua/neorg/modules/core/keybinds/module.lua\", \":p\"))\n    )\n)\nfileio.write_to_wiki(\"_Sidebar\", docgen.generators.sidebar(doc_modules))\n\n-- Loop through all modules and generate their respective wiki files\nfor module_name, module in pairs(doc_modules) do\n    local buffer = module.buffer\n\n    -- Query the root node and try to find a `module.config.public` table\n    local root = vim.treesitter.get_parser(buffer, \"lua\"):parse()[1]:root()\n    local config_node = docgen.get_module_config_node(buffer, root)\n\n    -- A collection of data about all the configuration options for the current module\n    ---@type ConfigOptionArray[]\n    local configuration_options = {}\n\n    if config_node then\n        docgen.map_config(buffer, config_node, function(data, comments)\n            for i, comment in ipairs(comments) do\n                comments[i] = docgen.lookup_modules(doc_modules, comment:gsub(\"^%s*%-%-+%s*\", \"\"))\n            end\n\n            do\n                local error = docgen.check_comment_integrity(table.concat(comments, \"\\n\"))\n\n                if type(error) == \"string\" then\n                    -- Get the exact location of the error with data.node and the file it was contained in\n                    local start_row, start_col = data.node:start()\n\n                    vim.notify(\n                        (\"Error when parsing annotation in module '%s' on line (%d, %d): %s\"):format(\n                            module_name,\n                            start_row,\n                            start_col,\n                            error\n                        )\n                    )\n                    return\n                end\n            end\n\n            if not data.value then\n                return\n            end\n\n            local object = docgen.to_lua_object(module.parsed, buffer, data.value, module_name)\n\n            do\n                lib.ensure_nested(configuration_options, unpack(data.parents))\n                local ref = vim.tbl_get(configuration_options, unpack(data.parents)) or configuration_options\n                if data.name then\n                    ref[data.name] = {\n                        self = {\n                            buffer = buffer,\n                            data = data,\n                            comments = comments,\n                            object = object,\n                        },\n                    }\n                else\n                    table.insert(ref, {\n                        self = {\n                            buffer = buffer,\n                            data = data,\n                            comments = comments,\n                            object = object,\n                        },\n                    })\n                end\n            end\n        end)\n    end\n\n    -- Perform module lookups in the module's top comment markdown data.\n    -- This cannot be done earlier because then there would be no guarantee\n    -- that all the modules have been properly indexed and parsed.\n    for i, line in ipairs(module.top_comment_data.markdown) do\n        module.top_comment_data.markdown[i] = docgen.lookup_modules(doc_modules, line)\n    end\n\n    fileio.write_to_wiki(\n        module.top_comment_data.file,\n        docgen.generators.module(doc_modules, module, concat_configuration_options(configuration_options))\n    )\nend\n"
  },
  {
    "path": "docgen/minimal_init.vim",
    "content": "\" Copied from: https://github.com/ThePrimeagen/refactoring.nvim/blob/master/scripts/minimal.vim\n\n\" Current neorg code\nset rtp+=.\n\n\" For test suites\nset rtp+=./plenary.nvim\nset rtp+=./nvim-treesitter\n\nset noswapfile\n\nruntime! plugin/plenary.vim\nruntime! plugin/nvim-treesitter.vim\n\nlua << EOF\nrequire(\"nvim-treesitter\").setup({})\n\nlocal ok, module = pcall(require,'nvim-treesitter.configs')\nif ok then\n    module.setup({})\nend\n\npackage.path = \"../lua/?.lua;\" .. \"../lua/?/init.lua;\" .. package.path\npackage.path = \"../plenary.nvim/lua/?.lua;\" .. package.path\npackage.path = \"../nvim-treesitter/lua/?.lua;\" .. package.path\npackage.path = \"/usr/share/lua/5.1/?.lua;/usr/share/lua/5.1/?/init.lua;\" .. package.path\npackage.path = \"/usr/share/lua/5.1/?.so;\" .. package.path\n\nvim.cmd.TSInstallSync({\n    bang = true,\n    args = { \"lua\", \"norg\" },\n})\nEOF\n"
  },
  {
    "path": "docs/CONTRIBUTING.md",
    "content": "# Contributions welcome!\r\nWhilst any help is very special, please be considerate of certain rules in order to make the codebase uniform/coherent.\r\n\r\n---\r\n### Table of Contents\r\n\r\n- When you need help:\r\n  - [What to do when you encounter a bug](#reporting-a-bug-or-issue)\r\n\r\n- When contributing:\r\n  - [Formatting rules](#formatting-rules)\r\n  - [Coding style](#coding-style)\r\n  - [Adding extra functionality](#adding-functionality)\r\n  - [Helping with documentation](#helping-with-documentation)\r\n---\r\n\r\n### Reporting a bug or issue\r\nWhenever something happens the way it's not supposed to, [file an issue!](https://github.com/nvim-neorg/neorg/issues/new/choose), but before you do, try the following:\r\n- If you haven't already, always try updating the plugin first. Issues may be fixed before you can even file one, so always make sure you are up to date beforehand.\r\n- Make sure you are running the latest neovim version. Whenever I test neorg, I test it on the latest compiled neovim from source which I recompile everyday. While you don't have to be *that* extreme with your updates, make sure you're at least running the latest neovim nightly.\r\n\r\nIf you're certain it's a fault of the plugin, not your configuration, in the issue please provide the following:\r\n- The neovim version you're running (`nvim --version`)\r\n- The neorg log file (you'll find it at `stdpath('data') .. '/neorg.log'`). This file will contain the necessary info for me to effectively debug.\r\n  You can run `:echo stdpath('data')` if you're unsure where that path resides.\r\n- The branch of Neorg you are using (unstable/main/some other experimental branch)\r\n- The list of modules you have loaded (you can run `:Neorg module list` to see a comprehensive list)\r\n- Other plugins you are using which you think could potentially be conflicting with neorg.\r\n- Steps to reproduce the bug, if any - sometimes bugs get triggered only on certain configurations, which can be a pain. If you're aware that the bug requires a specific config, be sure to include that information as well!\r\n\r\n---\r\n\r\n### Formatting rules\r\nFormatting is done in the project via `stylua`. You should install it with lua 5.2 support, as that\r\nversion allows for the formatting of `goto` blocks. You can install it via cargo: `cargo install stylua --features lua52`.\r\nYou can then run `make format` in the project root to automatically format all your lua code. Good stuff.\r\n\r\n### Coding style\r\n- I use snake_case for everything, and you should too :P\r\n- **Please** comment all your code! Annotations for functions are generated by [neogen](https://github.com/danymat/neogen).\r\n\r\n<!--### Modules\r\n- When creating a module, add a comment up top as seen [here](/lua/neorg/modules/core/autocommands/module.lua) and [here](/lua/neorg/modules/core/keybinds/module.lua)\r\n- Add a general description of what the module does at the top and provide a `USAGE:` block describing how to use the module\r\n- Try to only access neorg data through functions, not through tables (e.g. don't access parts of `neorg.modules.loaded_modules` you're not supposed to - if anything only access the public fields exposed by other modules). Use the API as much as possible.\r\nTODO: Make this reference our new \"top comment\" style.\r\n-->\r\n\r\n## Adding functionality\r\nWhenever you are planning on extending neorg, try your best to add *all* extra functionality through modules and modules only. Make changes to the Neorg core only if absolutely necessary.\r\nWhen adding stuff, use lua only. `vim.cmd` in extreme cases :)\r\n\r\n## Helping with documentation\r\nThe documentations (wiki pages) are generated based on the comments in the code. All the logic for generating the wiki is in [`docgen/`](../docgen/).\r\nTo generate documentations locally, run `make local-documentation` (see [`Makefile`](../Makefile) for more details). If there's no error, you should then see a `wiki/` folder generated.\r\n\r\nIf you feel like improving the documentation, please make changes to the corresponding comments in the correct file. For example, to make changes to the `core.qol.todo_items` documentation, please make changes to the comments in [`./lua/neorg/modules/core/qol/todo_items/module.lua`](../lua/neorg/modules/core/qol/todo_items/module.lua)\r\n\r\n> Tips: `grep` for the string you want to find.\r\n"
  },
  {
    "path": "docs/README.md",
    "content": "<div align=\"center\">\r\n\r\n# Neorg Documentation\r\nThe place where you'll find all the goodies regarding Neorg docs.\r\n\r\n</div>\r\n\r\n---\r\nTable of contents:\r\n  - [A bug has occurred, help!](#bug-reports)\r\n  - [I wanna know how to contribute! What do I need to know?](#contribution)\r\n  - [Alright, I've read the contribution file, what now?](#creating-modules)\r\n  - [What's the plan for the future?](#the-future)\r\n---\r\n\r\n# Bug Reports\r\nOh no, did something break? See [here](/docs/CONTRIBUTING.md#reporting-a-bug-or-issue) to find out how to report it and the steps to take\r\n\r\n# Contribution\r\nAll the things you will need to know are described [right here](/docs/CONTRIBUTING.md#formatting-rules). So go ahead and make something already! :P\r\n\r\n# Creating Modules\r\nNow's the fun part, you gotta learn how the module system works and how to program for it! A mini walkthrough is available in the [Creating Modules](https://github.com/vhyrro/neorg/wiki/Creating-Modules) document\r\nin the wiki.\r\n\r\n# The Future\r\nWant to know what's brewing up behind the scenes? Take a look at the [written roadmap](/doc/roadmap.norg) (or do `:h neorg-roadmap` in neovim) to see where we're headed!\r\n"
  },
  {
    "path": "flake.nix",
    "content": "# General TODOS:\n# - Add comments explaining the more terse parts of the flake.\n{\n  description = \"Flake for Neorg development and testing\";\n\n  inputs = {\n    nixpkgs.url = \"github:NixOS/nixpkgs/nixpkgs-unstable\";\n    flake-parts.url = \"github:hercules-ci/flake-parts\";\n\n    neorocks.url = \"github:nvim-neorocks/neorocks\";\n    neorocks.inputs.nixpkgs.follows = \"nixpkgs\";\n\n    gen-luarc.url = \"github:mrcjkb/nix-gen-luarc-json\";\n    gen-luarc.inputs.nixpkgs.follows = \"nixpkgs\";\n\n    git-hooks.url = \"github:cachix/git-hooks.nix\";\n    git-hooks.inputs.nixpkgs.follows = \"nixpkgs\";\n  };\n\n  outputs = inputs @ {\n    self,\n    nixpkgs,\n    flake-parts,\n    neorocks,\n    gen-luarc,\n    git-hooks,\n    ...\n  }:\n    flake-parts.lib.mkFlake {inherit inputs;} {\n      systems = [\n        \"x86_64-linux\"\n        \"x86_64-darwin\"\n        \"aarch64-darwin\"\n      ];\n\n      _module.args = {inherit gen-luarc neorocks git-hooks;};\n\n      imports = [\n        ./nix/overlays\n        ./nix/checks\n      ];\n\n      perSystem = {pkgs, ...}: {\n        formatter = pkgs.alejandra;\n\n        imports = [\n          ./nix/packages\n          ./nix/shells\n        ];\n      };\n    };\n}\n"
  },
  {
    "path": "lua/neorg/core/callbacks.lua",
    "content": "--- @brief [[\n--- Defines user callbacks - ways for the user to directly interact with Neorg and respond on certain events.\n--- @brief ]]\n\n--- @module \"neorg.core.modules\"\n\n--- @class neorg.callbacks\nlocal callbacks = {\n    ---@type table<string, { [1]: fun(event: neorg.event, content: table|any), [2]?: fun(event: neorg.event): boolean }>\n    callback_list = {},\n}\n\n--- Triggers a new callback to execute whenever an event of the requested type is executed.\n--- @param event_name string The full path to the event we want to listen on.\n--- @param callback fun(event: neorg.event, content: table|any) The function to call whenever our event gets triggered.\n--- @param content_filter? fun(event: neorg.event): boolean # A filtering function to test if a certain event meets our expectations.\nfunction callbacks.on_event(event_name, callback, content_filter)\n    -- If the table doesn't exist then create it\n    callbacks.callback_list[event_name] = callbacks.callback_list[event_name] or {}\n    -- Insert the callback and content filter\n    table.insert(callbacks.callback_list[event_name], { callback, content_filter })\nend\n\n--- Used internally by Neorg to call all callbacks with an event.\n--- @param event neorg.event An event as returned by `modules.create_event()`\n--- @see modules.create_event\nfunction callbacks.handle_callbacks(event)\n    -- Query the list of registered callbacks\n    local callback_entry = callbacks.callback_list[event.type]\n\n    -- If the callbacks exist then\n    if callback_entry then\n        -- Loop through every callback\n        for _, callback in ipairs(callback_entry) do\n            -- If the filter event has not been defined or if the filter returned true then\n            if not callback[2] or callback[2](event) then\n                -- Execute the callback\n                callback[1](event, event.content)\n            end\n        end\n    end\nend\n\nreturn callbacks\n"
  },
  {
    "path": "lua/neorg/core/config.lua",
    "content": "--- @brief [[\n--- Defines the configuration table for use throughout Neorg.\n--- @brief ]]\n\n-- TODO(vhyrro): Make `norg_version` and `version` a `Version` class.\n\n--- @alias OperatingSystem\n--- | \"windows\"\n--- | \"wsl\"\n--- | \"wsl2\"\n--- | \"mac\"\n--- | \"linux\"\n--- | \"bsd\"\n\n--- @alias neorg.configuration.module { config?: table }\n\n--- @class (exact) neorg.configuration.user\n--- @field hook? fun(manual: boolean, arguments?: string)    A user-defined function that is invoked whenever Neorg starts up. May be used to e.g. set custom keybindings.\n--- @field lazy_loading? boolean                             Whether to defer loading the Neorg core until after the user has entered a `.norg` file.\n--- @field load table<string, neorg.configuration.module>    A list of modules to load, alongside their configurations.\n--- @field logger? neorg.log.configuration                   A configuration table for the logger.\n\n--- @class (exact) neorg.configuration\n--- @field arguments table<string, string>                   A list of arguments provided to the `:NeorgStart` function in the form of `key=value` pairs. Only applicable when `user_config.lazy_loading` is `true`.\n--- @field manual boolean?                                   Used if Neorg was manually loaded via `:NeorgStart`. Only applicable when `user_config.lazy_loading` is `true`.\n--- @field modules table<string, neorg.configuration.module> Acts as a copy of the user's configuration that may be modified at runtime.\n--- @field norg_version string                               The version of the file format to be used throughout Neorg. Used internally.\n--- @field os_info OperatingSystem                           The operating system that Neorg is currently running under.\n--- @field pathsep \"\\\\\"|\"/\"                                  The operating system that Neorg is currently running under.\n--- @field started boolean                                   Set to `true` when Neorg is fully initialized.\n--- @field user_config neorg.configuration.user              Stores the configuration provided by the user.\n--- @field version string                                    The version of Neorg that is currently active. Automatically updated by CI on every release.\n\n--- Gets the current operating system.\n--- @return OperatingSystem\nlocal function get_os_info()\n    local os = vim.loop.os_uname().sysname:lower()\n\n    if os:find(\"windows_nt\") or os:find(\"mingw32_nt\") then\n        return \"windows\"\n    elseif os == \"darwin\" then\n        return \"mac\"\n    elseif os == \"linux\" then\n        local f = io.open(\"/proc/version\", \"r\")\n        if f ~= nil then\n            local version = f:read(\"*all\")\n            f:close()\n            if version:find(\"WSL2\") then\n                return \"wsl2\"\n            elseif version:find(\"microsoft\") then\n                return \"wsl\"\n            end\n        end\n        return \"linux\"\n    elseif os:find(\"bsd\") then\n        return \"bsd\"\n    end\n\n    error(\"[neorg]: Unable to determine the currently active operating system!\")\nend\n\nlocal os_info = get_os_info()\n\n--- Stores the configuration for the entirety of Neorg.\n--- This includes not only the user configuration (passed to `setup()`), but also internal\n--- variables that describe something specific about the user's hardware.\n--- @see neorg.setup\n---\n--- @type neorg.configuration\nlocal config = {\n    user_config = {\n        lazy_loading = false,\n        load = {\n            --[[\n                [\"name\"] = { config = { ... } }\n            --]]\n        },\n    },\n\n    modules = {},\n    manual = nil,\n    arguments = {},\n\n    norg_version = \"1.1.1\",\n    version = \"9.4.0\",\n\n    os_info = os_info,\n    pathsep = os_info == \"windows\" and \"\\\\\" or \"/\",\n\n    hook = nil,\n    started = false,\n}\n\nreturn config\n"
  },
  {
    "path": "lua/neorg/core/init.lua",
    "content": "local neorg = {\n    callbacks = require(\"neorg.core.callbacks\"),\n    config = require(\"neorg.core.config\"),\n    log = require(\"neorg.core.log\"),\n    modules = require(\"neorg.core.modules\"),\n    utils = require(\"neorg.core.utils\"),\n    lib = require(\"lua-utils\"),\n}\n\nreturn neorg\n"
  },
  {
    "path": "lua/neorg/core/log.lua",
    "content": "-- log.lua\n--\n-- Inspired by rxi/log.lua\n-- Modified by tjdevries and can be found at github.com/tjdevries/vlog.nvim\n-- Modified again by Vhyrro for use with neorg :)\n--\n-- This library is free software; you can redistribute it and/or modify it\n-- under the terms of the MIT license. See LICENSE for details.\n\n--- @alias LogLevel\n--- | \"trace\"\n--- | \"debug\"\n--- | \"info\"\n--- | \"warn\"\n--- | \"error\"\n--- | \"fatal\"\n\n--- @class (exact) neorg.log.configuration\n--- @field plugin string                                           Name of the plugin. Prepended to log messages.\n--- @field use_console boolean                                     Whether to print the output to Neovim while running.\n--- @field highlights boolean                                      Whether highlighting should be used in console (using `:echohl`).\n--- @field use_file boolean                                        Whether to write output to a file.\n--- @field level LogLevel                                          Any messages above this level will be logged.\n--- @field modes ({ name: LogLevel, hl: string, level: number })[] Level configuration.\n--- @field float_precision number                                  Can limit the number of decimals displayed for floats.\n\n--- User configuration section\n--- @type neorg.log.configuration\nlocal default_config = {\n    plugin = \"neorg\",\n\n    use_console = true,\n\n    highlights = true,\n\n    use_file = true,\n\n    level = \"warn\",\n\n    modes = {\n        { name = \"trace\", hl = \"Comment\", level = vim.log.levels.TRACE },\n        { name = \"debug\", hl = \"Comment\", level = vim.log.levels.DEBUG },\n        { name = \"info\", hl = \"None\", level = vim.log.levels.INFO },\n        { name = \"warn\", hl = \"WarningMsg\", level = vim.log.levels.WARN },\n        { name = \"error\", hl = \"ErrorMsg\", level = vim.log.levels.ERROR },\n        { name = \"fatal\", hl = \"ErrorMsg\", level = 5 },\n    },\n\n    float_precision = 0.01,\n}\n\n-- {{{ NO NEED TO CHANGE\nlocal log = {}\n\nlog.get_default_config = function()\n    return default_config\nend\n\nlocal unpack = unpack or table.unpack\n\n--- @param config neorg.log.configuration\n--- @param standalone boolean\nlog.new = function(config, standalone)\n    config = vim.tbl_deep_extend(\"force\", default_config, config)\n    config.plugin = \"neorg\" -- Force the plugin name to be neorg\n\n    local outfile = string.format(\"%s/%s.log\", vim.api.nvim_call_function(\"stdpath\", { \"data\" }), config.plugin)\n\n    local obj = standalone ~= nil and log or {}\n\n    local levels = {}\n    for _, v in ipairs(config.modes) do\n        levels[v.name] = v.level\n    end\n\n    local round = function(x, increment)\n        increment = increment or 1\n        x = x / increment\n        return (x > 0 and math.floor(x + 0.5) or math.ceil(x - 0.5)) * increment\n    end\n\n    local make_string = function(...)\n        local t = {}\n        for i = 1, select(\"#\", ...) do\n            local x = select(i, ...)\n\n            if type(x) == \"number\" and config.float_precision then\n                x = tostring(round(x, config.float_precision))\n            elseif type(x) == \"table\" then\n                x = vim.inspect(x)\n            else\n                x = tostring(x)\n            end\n\n            t[#t + 1] = x\n        end\n        return table.concat(t, \" \")\n    end\n\n    local log_at_level = function(level_config, message_maker, ...)\n        -- Return early if we\"re below the config.level\n        if levels[level_config.name] < levels[config.level] then\n            return\n        end\n        local nameupper = level_config.name:upper()\n\n        local msg = message_maker(...)\n        local info = debug.getinfo(2, \"Sl\")\n        local lineinfo = info.short_src .. \":\" .. info.currentline\n\n        -- Output to console\n        if config.use_console then\n            local v = string.format(\"(%s)\\n%s\\n%s\", os.date(\"%H:%M:%S\"), lineinfo, msg)\n\n            if config.highlights and level_config.hl then\n                (vim.schedule_wrap(function()\n                    vim.cmd(string.format(\"echohl %s\", level_config.hl))\n                end))()\n            end\n\n            (vim.schedule_wrap(function()\n                vim.notify(string.format(\"[%s] %s\", config.plugin, vim.fn.escape(v, '\"')), level_config.level)\n                -- vim.cmd(string.format([[echom \"[%s] %s\"]], config.plugin, vim.fn.escape(v, '\"')))\n            end))()\n\n            if config.highlights and level_config.hl then\n                (vim.schedule_wrap(function()\n                    vim.cmd(\"echohl NONE\")\n                end))()\n            end\n        end\n\n        -- Output to log file\n        if config.use_file then\n            local fp = assert(io.open(outfile, \"a\"))\n            local str = string.format(\"[%-6s%s] %s: %s\\n\", nameupper, os.date(), lineinfo, msg)\n            fp:write(str)\n            fp:close()\n        end\n    end\n\n    for _, x in ipairs(config.modes) do\n        obj[x.name] = function(...)\n            return log_at_level(x, make_string, ...)\n        end\n\n        obj[(\"fmt_%s\"):format(x.name)] = function()\n            return log_at_level(x, function(...)\n                local passed = { ... }\n                local fmt = table.remove(passed, 1)\n                local inspected = {}\n                for _, v in ipairs(passed) do\n                    table.insert(inspected, vim.inspect(v))\n                end\n                return string.format(fmt, unpack(inspected))\n            end)\n        end\n    end\nend\n\n-- }}}\n\nreturn log\n"
  },
  {
    "path": "lua/neorg/core/modules.lua",
    "content": "--- @brief [[\n--- Base file for modules.\n--- This file contains the base implementation for \"modules\", building blocks of the Neorg environment.\n--- @brief ]]\n\n-- TODO: What goes below this line until the next notice used to belong to modules.base\n-- We need to find a way to make these constructors easier to maintain and more efficient\n\nlocal callbacks = require(\"neorg.core.callbacks\")\nlocal config = require(\"neorg.core.config\")\nlocal log = require(\"neorg.core.log\")\nlocal utils = require(\"neorg.core.utils\")\n\n--- @alias neorg.module.public { version: string, [any]: any }\n\n--- @class (exact) neorg.module.resolver\n--- @field [\"core.autocommands\"] core.autocommands\n--- @field [\"core.clipboard\"] core.clipboard\n--- @field [\"core.completion\"] core.completion\n--- @field [\"core.concealer\"] core.concealer\n--- @field [\"core.dirman\"] core.dirman\n--- @field [\"core.esupports.hop\"] core.esupports.hop\n--- @field [\"core.esupports.indent\"] core.esupports.indent\n--- @field [\"core.esupports.metagen\"] core.esupports.metagen\n--- @field [\"core.export\"] core.export\n--- @field [\"core.export.markdown\"] core.export.markdown\n--- @field [\"core.fs\"] core.fs\n--- @field [\"core.highlights\"] core.highlights\n--- @field [\"core.integrations.treesitter\"] core.integrations.treesitter\n--- @field [\"core.itero\"] core.itero\n--- @field [\"core.journal\"] core.journal\n--- @field [\"core.keybinds\"] core.keybinds\n--- @field [\"core.latex.renderer\"] core.latex.renderer\n--- @field [\"core.links\"] core.links\n--- @field [\"core.looking-glass\"] core.looking-glass\n--- @field [\"core.neorgcmd\"] core.neorgcmd\n--- @field [\"core.pivot\"] core.pivot\n--- @field [\"core.presenter\"] core.presenter\n--- @field [\"core.promo\"] core.promo\n--- @field [\"core.qol.toc\"] core.qol.toc\n--- @field [\"core.qol.todo_items\"] core.qol.todo_items\n--- @field [\"core.queries.native\"] core.queries.native\n--- @field [\"core.scanner\"] core.scanner\n--- @field [\"core.storage\"] core.storage\n--- @field [\"core.summary\"] core.summary\n--- @field [\"core.syntax\"] core.syntax\n--- @field [\"core.tangle\"] core.tangle\n--- @field [\"core.tempus\"] core.tempus\n--- @field [\"core.text-objects\"] core.text-objects\n--- @field [\"core.todo-introspector\"] core.todo-introspector\n--- @field [\"core.ui\"] core.ui\n--- @field [\"core.ui.calendar\"] core.ui.calendar\n--- @field [\"core.ui.calendar.views.monthly\"] core.ui.calendar.views.monthly\n--- @field [\"core.ui.selection_popup\"] core.ui.selection_popup\n--- @field [\"core.ui.text_popup\"] core.ui.text_popup\n\n--- Defines both a public and private configuration for a Neorg module.\n--- Public configurations may be tweaked by the user from the `neorg.setup()` function,\n--- whereas private configurations are for internal use only.\n--- @class (exact) neorg.module.configuration\n--- @field custom? table         Internal table that tracks the differences (changes) between the default `public` table and the new (altered) `public` table. It contains only the tables that the user has altered in their own configuration.\n--- @field public private? table Internal configuration variables that may be tweaked by the developer.\n--- @field public public? table  Configuration variables that may be tweaked by the user.\n\n--- @class (exact) neorg.module.events\n--- @field defined? { [string]: neorg.event }              Lists all events defined by this module.\n--- @field subscribed? { [string]: { [string]: boolean } } Lists the events that the module is subscribed to.\n\n--- @alias neorg.module.setup { success: boolean, requires?: string[], replaces?: string, replace_merge?: boolean, wants?: string[] }\n\n--- Defines a module.\n--- A module is an object that contains a set of hooks which are invoked by Neorg whenever something in the\n--- environment occurs. This can be an event, a simple act of the module being loaded or anything else.\n--- @class (exact) neorg.module\n--- @field config? neorg.module.configuration The configuration for the module.\n--- @field events? neorg.module.events Describes all information related to events for this module.\n--- @field examples? table<string, function> Contains examples of how to use the modules that users or developers may sift through.\n--- @field imported? table<string, neorg.module> Imported submodules of the given module. Contrary to `required`, which only exposes the public API of a module, imported modules can be accessed in their entirety.\n--- @field load? fun() Function that is invoked once the module is considered \"stable\", i.e. after all dependencies are loaded. Perform your main loading routine here.\n--- @field name string The name of the module.\n--- @field neorg_post_load? fun() Function that is invoked after all modules are loaded. Useful if you want the Neorg environment to be fully set up before performing some task.\n--- @field path string The full path to the module (a more verbose version of `name`). May be used in lua's `require()` statements.\n--- @field public private? table A convenience table to place all of your private variables that you don't want to expose.\n--- @field public public? neorg.module.public Every module can expose any set of information it sees fit through this field. All functions and variables declared in this table will be visiable to any other module loaded.\n--- @field required? neorg.module.resolver Contains the public tables of all modules that were required via the `requires` array provided in the `setup()` function of this module.\n--- @field setup? fun(): neorg.module.setup? Function that is invoked before any other loading occurs. Should perform preliminary startup tasks.\n--- @field replaced? boolean If `true`, this means the module is a replacement for a core module. This flag is set automatically whenever `setup().replaces` is set to a value.\n--- @field on_event fun(event: neorg.event) A callback that is invoked any time an event the module has subscribed to has fired.\n\n---@class neorg.modules\nlocal modules = {}\n\n--- Returns a new Neorg module, exposing all the necessary function and variables.\n--- @param name string The name of the new module. Make sure this is unique. The recommended naming convention is `category.module_name` or `category.subcategory.module_name`.\n--- @param imports? string[] A list of imports to attach to the module. Import data is requestable via `module.required`. Use paths relative to the current module.\n--- @return neorg.module\nfunction modules.create(name, imports)\n    ---@type neorg.module\n    local new_module = {\n        setup = function()\n            return { success = true, requires = {}, replaces = nil, replace_merge = false }\n        end,\n\n        load = function() end,\n\n        on_event = function() end,\n\n        neorg_post_load = function() end,\n\n        name = \"core.default\",\n\n        path = \"modules.core.default.module\",\n\n        private = {},\n\n        public = {\n            version = config.norg_version,\n        },\n\n        config = {\n            private = {\n                --[[\n                config_option = false,\n\n                [\"option_group\"] = {\n                    sub_option = true\n                }\n                --]]\n            },\n\n            public = {\n                --[[\n                config_option = false,\n\n                [\"option_group\"] = {\n                    sub_option = true\n                }\n                --]]\n            },\n\n            custom = {},\n        },\n\n        events = {\n            subscribed = { -- The events that the module is subscribed to\n                --[[\n                [\"core.test\"] = { -- The name of the module that has events bound to it\n                    [\"test_event\"]  = true, -- Subscribes to event core.test.events.test_event\n\n                    [\"other_event\"] = true -- Subscribes to event core.test.events.other_event\n                }\n                --]]\n            },\n            defined = { -- The events that the module itself has defined\n                --[[\n                [\"my_event\"] = { event_data } -- Creates an event of type category.module.events.my_event\n                --]]\n            },\n        },\n\n        required = {\n            --[[\n            [\"core.test\"] = {\n                -- Their public API here...\n            },\n\n            [\"core.some_other_plugin\"] = {\n                -- Their public API here...\n            }\n\n            --]]\n        },\n\n        examples = {\n            --[[\n            a_cool_test = function()\n                print(\"Some code!\")\n            end\n            --]]\n        },\n\n        imported = {\n            --[[\n                [\"my.module.submodule\"] = { ... },\n            --]]\n        },\n\n        tests = function() end,\n    }\n\n    if imports then\n        for _, import in ipairs(imports) do\n            local fullpath = table.concat({ name, import }, \".\")\n\n            if not modules.load_module(fullpath) then\n                log.error(\"Unable to load import '\" .. fullpath .. \"'! An error occured (see traceback below):\")\n                assert(false) -- Halt execution, no recovering from this error...\n            end\n\n            new_module.imported[fullpath] = modules.loaded_modules[fullpath]\n        end\n    end\n\n    if name then\n        new_module.name = name\n        new_module.path = \"modules.\" .. name\n    end\n\n    return new_module\nend\n\n--- Constructs a metamodule from a list of submodules. Metamodules are modules that can autoload batches of modules at once.\n--- @param name string The name of the new metamodule. Make sure this is unique. The recommended naming convention is `category.module_name` or `category.subcategory.module_name`.\n--- @param ... string A list of module names to load.\n--- @return neorg.module\nfunction modules.create_meta(name, ...)\n    local module = modules.create(name)\n\n    module.config.public.enable = { ... }\n\n    module.setup = function()\n        return { success = true }\n    end\n\n    module.load = function()\n        module.config.public.enable = (function()\n            -- If we haven't define any modules to disable then just return all enabled modules\n            if not module.config.public.disable then\n                return module.config.public.enable\n            end\n\n            local ret = {}\n\n            -- For every enabled module\n            for _, mod in ipairs(module.config.public.enable) do\n                -- If that module does not exist in the disable table (ie. it is enabled) then add it to the `ret` table\n                if not vim.tbl_contains(module.config.public.disable, mod) then\n                    table.insert(ret, mod)\n                end\n            end\n\n            -- Return the table containing all the modules we would like to enable\n            return ret\n        end)()\n\n        -- Go through every module that we have defined in the metamodule and load it!\n        for _, mod in ipairs(module.config.public.enable) do\n            modules.load_module(mod)\n        end\n    end\n\n    return module\nend\n\n-- TODO: What goes below this line until the next notice used to belong to modules\n-- We need to find a way to make these functions easier to maintain\n\n--- Tracks the amount of currently loaded modules.\nmodules.loaded_module_count = 0\n\n--- The table of currently loaded modules\n--- @type { [string]: neorg.module }\nmodules.loaded_modules = {}\n\n--- Loads and enables a module\n--- Loads a specified module. If the module subscribes to any events then they will be activated too.\n--- @param module neorg.module The actual module to load.\n--- @return boolean # Whether the module successfully loaded.\nfunction modules.load_module_from_table(module)\n    log.info(\"Loading module with name\", module.name)\n\n    -- If our module is already loaded don't try loading it again\n    if modules.loaded_modules[module.name] then\n        log.trace(\"Module\", module.name, \"already loaded. Omitting...\")\n        return true\n    end\n\n    -- Invoke the setup function. This function returns whether or not the loading of the module was successful and some metadata.\n    ---@type neorg.module.setup\n    local loaded_module = module.setup and module.setup()\n        or {\n            success = true,\n            replaces = {},\n            replace_merge = false,\n            requires = {},\n            wants = {},\n        }\n\n    -- We do not expect module.setup() to ever return nil, that's why this check is in place\n    if not loaded_module then\n        log.error(\n            \"Module\",\n            module.name,\n            \"does not handle module loading correctly; module.setup() returned nil. Omitting...\"\n        )\n        return false\n    end\n\n    -- A part of the table returned by module.setup() tells us whether or not the module initialization was successful\n    if loaded_module.success == false then\n        log.trace(\"Module\", module.name, \"did not load properly.\")\n        return false\n    end\n\n    --[[\n    --    This small snippet of code creates a copy of an already loaded module with the same name.\n    --    If the module wants to replace an already loaded module then we need to create a deepcopy of that old module\n    --    in order to stop it from getting overwritten.\n    --]]\n    ---@type neorg.module\n    local module_to_replace\n\n    -- If the return value of module.setup() tells us to hotswap with another module then cache the module we want to replace with\n    if loaded_module.replaces and loaded_module.replaces ~= \"\" then\n        module_to_replace = vim.deepcopy(modules.loaded_modules[loaded_module.replaces])\n    end\n\n    -- Add the module into the list of loaded modules\n    -- The reason we do this here is so other modules don't recursively require each other in the dependency loading loop below\n    modules.loaded_modules[module.name] = module\n\n    -- If the module \"wants\" any other modules then verify they are loaded\n    if loaded_module.wants and not vim.tbl_isempty(loaded_module.wants) then\n        log.info(\"Module\", module.name, \"wants certain modules. Ensuring they are loaded...\")\n\n        -- Loop through each dependency and ensure it's loaded\n        for _, required_module in ipairs(loaded_module.wants) do\n            log.trace(\"Verifying\", required_module)\n\n            -- This would've always returned false had we not added the current module to the loaded module list earlier above\n            if not modules.is_module_loaded(required_module) then\n                if config.user_config.load[required_module] then\n                    log.trace(\n                        \"Wanted module\",\n                        required_module,\n                        \"isn't loaded but can be as it's defined in the user's config. Loading...\"\n                    )\n\n                    if not modules.load_module(required_module) then\n                        log.error(\n                            \"Unable to load wanted module for\",\n                            module.name,\n                            \"- the module didn't load successfully\"\n                        )\n\n                        -- Make sure to clean up after ourselves if the module failed to load\n                        modules.loaded_modules[module.name] = nil\n                        return false\n                    end\n                else\n                    log.error(\n                        (\"Unable to load module %s, wanted dependency %s was not satisfied. Be sure to load the module and its appropriate config too!\"):format(\n                            module.name,\n                            required_module\n                        )\n                    )\n\n                    -- Make sure to clean up after ourselves if the module failed to load\n                    modules.loaded_modules[module.name] = nil\n                    return false\n                end\n            end\n\n            -- Create a reference to the dependency's public table\n            module.required[required_module] = modules.loaded_modules[required_module].public\n        end\n    end\n\n    -- If any dependencies have been defined, handle them\n    if loaded_module.requires and vim.tbl_count(loaded_module.requires) > 0 then\n        log.info(\"Module\", module.name, \"has dependencies. Loading dependencies first...\")\n\n        -- Loop through each dependency and load it one by one\n        for _, required_module in pairs(loaded_module.requires) do\n            log.trace(\"Loading submodule\", required_module)\n\n            -- This would've always returned false had we not added the current module to the loaded module list earlier above\n            if not modules.is_module_loaded(required_module) then\n                if not modules.load_module(required_module) then\n                    log.error(\n                        (\"Unable to load module %s, required dependency %s did not load successfully\"):format(\n                            module.name,\n                            required_module\n                        )\n                    )\n\n                    -- Make sure to clean up after ourselves if the module failed to load\n                    modules.loaded_modules[module.name] = nil\n                    return false\n                end\n            else\n                log.trace(\"Module\", required_module, \"already loaded, skipping...\")\n            end\n\n            -- Create a reference to the dependency's public table\n            module.required[required_module] = modules.loaded_modules[required_module].public\n        end\n    end\n\n    -- After loading all our dependencies, see if we need to hotswap another module with ourselves\n    if module_to_replace then\n        -- Make sure the names of both modules match\n        module.name = module_to_replace.name\n\n        -- Whenever a module gets hotswapped, a special flag is set inside the module in order to signalize that it has been hotswapped before\n        -- If this flag has already been set before, then throw an error - there is no way for us to know which hotswapped module should take priority.\n        if module_to_replace.replaced then\n            log.error(\n                (\"Unable to replace module %s - module replacement clashing detected. This error triggers when a module tries to be replaced more than two times - neorg doesn't know which replacement to prioritize.\"):format(\n                    module_to_replace.name\n                )\n            )\n\n            -- Make sure to clean up after ourselves if the module failed to load\n            modules.loaded_modules[module.name] = nil\n\n            return false\n        end\n\n        -- If the replace_merge flag is set to true in the setup() return value then recursively merge the data from the\n        -- previous module into our new one. This allows for practically seamless hotswapping, as it allows you to retain the data\n        -- of the previous module.\n        if loaded_module.replace_merge then\n            module = vim.tbl_deep_extend(\"force\", module, {\n                private = module_to_replace.private,\n                config = module_to_replace.config,\n                public = module_to_replace.public,\n                events = module_to_replace.events,\n            })\n        end\n\n        -- Set the special module.replaced flag to let everyone know we've been hotswapped before\n        module.replaced = true\n    end\n\n    log.info(\"Successfully loaded module\", module.name)\n\n    -- Keep track of the number of loaded modules\n    modules.loaded_module_count = modules.loaded_module_count + 1\n\n    -- NOTE(vhyrro): Left here for debugging.\n    -- Maybe make controllable with a switch in the future.\n    -- local start = vim.loop.hrtime()\n\n    -- Call the load function\n    if module.load then\n        module.load()\n    end\n\n    -- local msg = (\"%fms\"):format((vim.loop.hrtime() - start) / 1e6)\n    -- vim.notify(msg .. \" \" .. module.name)\n\n    modules.broadcast_event({\n        type = \"core.module_loaded\",\n        split_type = { \"core\", \"module_loaded\" },\n        filename = \"\",\n        filehead = \"\",\n        cursor_position = { 0, 0 },\n        referrer = \"core\",\n        line_content = \"\",\n        content = module,\n        broadcast = true,\n        buffer = vim.api.nvim_get_current_buf(),\n        window = vim.api.nvim_get_current_win(),\n        mode = vim.fn.mode(),\n    })\n\n    return true\nend\n\n--- Unlike `load_module_from_table()`, which loads a module from memory, `load_module()` tries to find the corresponding module file on disk and loads it into memory.\n--- If the module cannot not be found, attempt to load it off of github (unimplemented). This function also applies user-defined config and keymaps to the modules themselves.\n--- This is the recommended way of loading modules - `load_module_from_table()` should only really be used by neorg itself.\n--- @param module_name string A path to a module on disk. A path seperator in neorg is '.', not '/'.\n--- @param cfg table? A config that reflects the structure of `neorg.config.user_config.load[\"module.name\"].config`.\n--- @return boolean # Whether the module was successfully loaded.\nfunction modules.load_module(module_name, cfg)\n    -- Don't bother loading the module from disk if it's already loaded\n    if modules.is_module_loaded(module_name) then\n        return true\n    end\n\n    -- Attempt to require the module, does not throw an error if the module doesn't exist\n    local module = require(\"neorg.modules.\" .. module_name .. \".module\")\n\n    -- If the module is nil for some reason return false\n    if not module then\n        log.error(\n            \"Unable to load module\",\n            module_name,\n            \"- loaded file returned nil. Be sure to return the table created by modules.create() at the end of your module.lua file!\"\n        )\n        return false\n    end\n\n    -- If the value of `module` is strictly true then it means the required file returned nothing\n    -- We obviously can't do anything meaningful with that!\n    if module == true then\n        log.error(\n            \"An error has occurred when loading\",\n            module_name,\n            \"- loaded file didn't return anything meaningful. Be sure to return the table created by modules.create() at the end of your module.lua file!\"\n        )\n        return false\n    end\n\n    -- Load the user-defined config\n    if cfg and not vim.tbl_isempty(cfg) then\n        module.config.custom = cfg\n        module.config.public = vim.tbl_deep_extend(\"force\", module.config.public, cfg)\n    else\n        module.config.custom = config.modules[module_name]\n        module.config.public = vim.tbl_deep_extend(\"force\", module.config.public, module.config.custom or {})\n    end\n\n    -- Pass execution onto load_module_from_table() and let it handle the rest\n    return modules.load_module_from_table(module)\nend\n\n--- Has the same principle of operation as load_module_from_table(), except it then sets up the parent module's \"required\" table, allowing the parent to access the child as if it were a dependency.\n--- @param module neorg.module A valid table as returned by modules.create()\n--- @param parent_module string|neorg.module If a string, then the parent is searched for in the loaded modules. If a table, then the module is treated as a valid module as returned by modules.create()\nfunction modules.load_module_as_dependency_from_table(module, parent_module)\n    if modules.load_module_from_table(module) then\n        if type(parent_module) == \"string\" then\n            modules.loaded_modules[parent_module].required[module.name] = module.public\n        elseif type(parent_module) == \"table\" then\n            parent_module.required[module.name] = module.public\n        end\n    end\nend\n\n--- Normally loads a module, but then sets up the parent module's \"required\" table, allowing the parent module to access the child as if it were a dependency.\n--- @param module_name string A path to a module on disk. A path seperator in neorg is '.', not '/'\n--- @param parent_module string The name of the parent module. This is the module which the dependency will be attached to.\n--- @param cfg? table A config that reflects the structure of neorg.config.user_config.load[\"module.name\"].config\nfunction modules.load_module_as_dependency(module_name, parent_module, cfg)\n    if modules.load_module(module_name, cfg) and modules.is_module_loaded(parent_module) then\n        modules.loaded_modules[parent_module].required[module_name] = modules.get_module_config(module_name)\n    end\nend\n\n--- Retrieves the public API exposed by the module.\n--- @generic T\n--- @param module_name `T` The name of the module to retrieve.\n--- @return T?\nfunction modules.get_module(module_name)\n    if not modules.is_module_loaded(module_name) then\n        log.trace(\"Attempt to get module with name\", module_name, \"failed - module is not loaded.\")\n        return\n    end\n\n    return modules.loaded_modules[module_name].public\nend\n\n--- Returns the module.config.public table if the module is loaded\n--- @param module_name string The name of the module to retrieve (module must be loaded)\n--- @return table?\nfunction modules.get_module_config(module_name)\n    if not modules.is_module_loaded(module_name) then\n        log.trace(\"Attempt to get module config with name\", module_name, \"failed - module is not loaded.\")\n        return\n    end\n\n    return modules.loaded_modules[module_name].config.public\nend\n\n--- Returns true if module with name module_name is loaded, false otherwise\n--- @param module_name string The name of an arbitrary module\n--- @return boolean\nfunction modules.is_module_loaded(module_name)\n    return modules.loaded_modules[module_name] ~= nil\nend\n\n--- Reads the module's public table and looks for a version variable, then converts it from a string into a table, like so: `{ major = <number>, minor = <number>, patch = <number> }`.\n--- @param module_name string The name of a valid, loaded module.\n--- @return table? parsed_version\nfunction modules.get_module_version(module_name)\n    -- If the module isn't loaded then don't bother retrieving its version\n    if not modules.is_module_loaded(module_name) then\n        log.trace(\"Attempt to get module version with name\", module_name, \"failed - module is not loaded.\")\n        return\n    end\n\n    -- Grab the version of the module\n    local version = modules.get_module(module_name).version\n\n    -- If it can't be found then error out\n    if not version then\n        log.trace(\"Attempt to get module version with name\", module_name, \"failed - version variable not present.\")\n        return\n    end\n\n    return utils.parse_version_string(version)\nend\n\n--- Executes `callback` once `module` is a valid and loaded module, else the callback gets instantly executed.\n--- @param module_name string The name of the module to listen for.\n--- @param callback fun(module_public_table: neorg.module.public) The callback to execute.\nfunction modules.await(module_name, callback)\n    if modules.is_module_loaded(module_name) then\n        callback(assert(modules.get_module(module_name)))\n        return\n    end\n\n    callbacks.on_event(\"core.module_loaded\", function(_, module)\n        callback(module.public)\n    end, function(event)\n        return event.content.name == module_name\n    end)\nend\n\n--- @alias Mode\n--- | \"n\"\n--- | \"no\"\n--- | \"nov\"\n--- | \"noV\"\n--- | \"noCTRL-V\"\n--- | \"CTRL-V\"\n--- | \"niI\"\n--- | \"niR\"\n--- | \"niV\"\n--- | \"nt\"\n--- | \"Terminal\"\n--- | \"ntT\"\n--- | \"v\"\n--- | \"vs\"\n--- | \"V\"\n--- | \"Vs\"\n--- | \"CTRL-V\"\n--- | \"CTRL-Vs\"\n--- | \"s\"\n--- | \"S\"\n--- | \"CTRL-S\"\n--- | \"i\"\n--- | \"ic\"\n--- | \"ix\"\n--- | \"R\"\n--- | \"Rc\"\n--- | \"Rx\"\n--- | \"Rv\"\n--- | \"Rvc\"\n--- | \"Rvx\"\n--- | \"c\"\n--- | \"cr\"\n--- | \"cv\"\n--- | \"cvr\"\n--- | \"r\"\n--- | \"rm\"\n--- | \"r?\"\n--- | \"!\"\n--- | \"t\"\n\n--- @class (exact) neorg.event\n--- @field type string The type of the event. Exists in the format of `category.name`.\n--- @field split_type string[] The event type, just split on every `.` character, e.g. `{ \"category\", \"name\" }`.\n--- @field content? table|any The content of the event. The data found here is specific to each individual event. Can be thought of as the payload.\n--- @field referrer string The name of the module that triggered the event.\n--- @field broadcast boolean Whether the event was broadcast to all modules. `true` is so, `false` if the event was specifically sent to a single recipient.\n--- @field cursor_position { [1]: number, [2]: number } The position of the cursor at the moment of broadcasting the event.\n--- @field filename string The name of the file that the user was in at the moment of broadcasting the event.\n--- @field filehead string The directory the user was in at the moment of broadcasting the event.\n--- @field line_content string The content of the line the user was editing at the moment of broadcasting the event.\n--- @field buffer number The buffer ID of the buffer the user was in at the moment of broadcasting the event.\n--- @field window number The window ID of the window the user was in at the moment of broadcasting the event.\n--- @field mode Mode The mode Neovim was in at the moment of broadcasting the event.\n\n-- TODO: What goes below this line until the next notice used to belong to modules\n-- We need to find a way to make these functions easier to maintain\n\n--[[\n--    NEORG EVENT FILE\n--    This file is responsible for dealing with event handling and broadcasting.\n--    All modules that subscribe to an event will receive it once it is triggered.\n--]]\n\n--- The working of this function is best illustrated with an example:\n--        If type == 'core.some_plugin.events.my_event', this function will return { 'core.some_plugin', 'my_event' }\n--- @param type string The full path of a module event\n--- @return string[]?\nfunction modules.split_event_type(type)\n    local start_str, end_str = type:find(\"%.events%.\")\n\n    local split_event_type = { type:sub(0, start_str - 1), type:sub(end_str + 1) }\n\n    if #split_event_type ~= 2 then\n        log.warn(\"Invalid type name:\", type)\n        return\n    end\n\n    return split_event_type\nend\n\n--- Returns an event template defined in `module.events.defined`.\n--- @param module neorg.module A reference to the module invoking the function\n--- @param type string A full path to a valid event type (e.g. `core.module.events.some_event`)\n--- @return neorg.event?\nfunction modules.get_event_template(module, type)\n    -- You can't get the event template of a type if the type isn't loaded\n    if not modules.is_module_loaded(module.name) then\n        log.info(\"Unable to get event of type\", type, \"with module\", module.name)\n        return\n    end\n\n    -- Split the event type into two\n    local split_type = modules.split_event_type(type)\n\n    if not split_type then\n        log.warn(\"Unable to get event template for event\", type, \"and module\", module.name)\n        return\n    end\n\n    log.trace(\"Returning\", split_type[2], \"for module\", split_type[1])\n\n    -- Return the defined event from the specific module\n    return modules.loaded_modules[module.name].events.defined[split_type[2]]\nend\n\n--- Creates a deep copy of the `modules.base_event` event and returns it with a custom type and referrer.\n--- @param module neorg.module A reference to the module invoking the function.\n--- @param name string A relative path to a valid event template.\n--- @return neorg.event\nfunction modules.define_event(module, name)\n    -- Create a copy of the base event and override the values with ones specified by the user\n\n    local new_event = {\n        type = \"core.base_event\",\n        split_type = {},\n        content = nil,\n        referrer = nil,\n        broadcast = true,\n\n        cursor_position = {},\n        filename = \"\",\n        filehead = \"\",\n        line_content = \"\",\n        buffer = 0,\n        window = 0,\n        mode = \"\",\n    }\n\n    if name then\n        new_event.type = module.name .. \".events.\" .. name\n    end\n\n    new_event.referrer = module.name\n\n    return new_event\nend\n\n--- Returns a copy of the event template provided by a module.\n--- @param module neorg.module A reference to the module invoking the function\n--- @param type string A full path to a valid event type (e.g. `core.module.events.some_event`)\n--- @param content table|any? The content of the event, can be anything from a string to a table to whatever you please.\n--- @param ev? table The original event data.\n--- @return neorg.event? # New event.\nfunction modules.create_event(module, type, content, ev)\n    -- Get the module that contains the event\n    local module_name = modules.split_event_type(type)[1]\n\n    -- Retrieve the template from module.events.defined\n    local event_template = modules.get_event_template(modules.loaded_modules[module_name] or { name = \"\" }, type)\n\n    if not event_template then\n        log.warn(\"Unable to create event of type\", type, \". Returning nil...\")\n        return\n    end\n\n    -- Make a deep copy here - we don't want to override the actual base table!\n    local new_event = vim.deepcopy(event_template)\n\n    new_event.type = type\n    new_event.content = content\n    new_event.referrer = module.name\n\n    -- Override all the important values\n    new_event.split_type = assert(modules.split_event_type(type))\n    new_event.filename = vim.fn.expand(\"%:t\") --[[@as string]]\n    new_event.filehead = vim.fn.expand(\"%:p:h\") --[[@as string]]\n\n    local bufid = ev and ev.buf or vim.api.nvim_get_current_buf()\n    local winid = assert(vim.fn.bufwinid(bufid))\n\n    if winid == -1 then\n        winid = vim.api.nvim_get_current_win()\n    end\n\n    new_event.cursor_position = vim.api.nvim_win_get_cursor(winid)\n\n    local row_1b = new_event.cursor_position[1]\n    new_event.line_content = vim.api.nvim_buf_get_lines(bufid, row_1b - 1, row_1b, true)[1]\n    new_event.referrer = module.name\n    new_event.broadcast = true\n    new_event.buffer = bufid\n    new_event.window = winid\n    new_event.mode = vim.api.nvim_get_mode().mode\n\n    return new_event\nend\n\n--- Sends an event to all subscribed modules. The event contains the filename, filehead, cursor position and line content as a bonus.\n--- @param event neorg.event An event, usually created by `modules.create_event()`.\n--- @param callback function? A callback to be invoked after all events have been asynchronously broadcast\nfunction modules.broadcast_event(event, callback)\n    -- Broadcast the event to all modules\n    if not event.split_type then\n        log.error(\"Unable to broadcast event of type\", event.type, \"- invalid event name\")\n        return\n    end\n\n    -- Let the callback handler know of the event\n    callbacks.handle_callbacks(event)\n\n    -- Loop through all the modules\n    for _, current_module in pairs(modules.loaded_modules) do\n        -- If the current module has any subscribed events and if it has a subscription bound to the event's module name then\n        if current_module.events.subscribed and current_module.events.subscribed[event.split_type[1]] then\n            -- Check whether we are subscribed to the event type\n            local evt = current_module.events.subscribed[event.split_type[1]][event.split_type[2]]\n\n            if evt ~= nil and evt == true then\n                -- Run the on_event() for that module\n                current_module.on_event(event)\n            end\n        end\n    end\n\n    -- Because the broadcasting of events is async we allow the event broadcaster to provide a callback\n    -- TODO: deprecate\n    if callback then\n        callback()\n    end\nend\n\n--- Instead of broadcasting to all loaded modules, `send_event()` only sends to one module.\n--- @param recipient string The name of a loaded module that will be the recipient of the event.\n--- @param event neorg.event An event, usually created by `modules.create_event()`.\nfunction modules.send_event(recipient, event)\n    -- If the recipient is not loaded then there's no reason to send an event to it\n    if not modules.is_module_loaded(recipient) then\n        log.warn(\"Unable to send event to module\", recipient, \"- the module is not loaded.\")\n        return\n    end\n\n    -- Set the broadcast variable to false since we're not invoking broadcast_event()\n    event.broadcast = false\n\n    -- Let the callback handler know of the event\n    callbacks.handle_callbacks(event)\n\n    -- Get the recipient module and check whether it's subscribed to our event\n    local mod = modules.loaded_modules[recipient]\n\n    if mod.events.subscribed and mod.events.subscribed[event.split_type[1]] then\n        local evt = mod.events.subscribed[event.split_type[1]][event.split_type[2]]\n\n        -- If it is then trigger the module's on_event() function\n        if evt ~= nil and evt == true then\n            mod.on_event(event)\n        end\n    end\nend\n\nreturn modules\n"
  },
  {
    "path": "lua/neorg/core/utils.lua",
    "content": "local configuration = require(\"neorg.core.config\")\nlocal log = require(\"neorg.core.log\")\n\n---@class neorg.core.utils\nlocal utils = {}\nlocal version = vim.version() -- TODO: Move to a more local scope\n\n--- A version agnostic way to call the neovim treesitter query parser\n--- @param language string # Language to use for the query\n--- @param query_string string # Query in s-expr syntax\n--- @return vim.treesitter.Query # Parsed query\nfunction utils.ts_parse_query(language, query_string)\n    if vim.treesitter.query.parse then\n        return vim.treesitter.query.parse(language, query_string)\n    else\n        ---@diagnostic disable-next-line\n        return vim.treesitter.parse_query(language, query_string)\n    end\nend\n\n--- An OS agnostic way of querying the current user\n--- @return string username\nfunction utils.get_username()\n    local current_os = configuration.os_info\n\n    if not current_os then\n        return \"\"\n    end\n\n    if current_os == \"linux\" or current_os == \"mac\" or current_os == \"wsl\" then\n        return os.getenv(\"USER\") or \"\"\n    elseif current_os == \"windows\" then\n        return os.getenv(\"username\") or \"\"\n    end\n\n    return \"\"\nend\n\n--- Returns an array of strings, the array being a list of languages that Neorg can inject.\n---@param values boolean If set to true will return an array of strings, if false will return a key-value table.\n---@return string[]|table<string, { type: \"treesitter\"|\"syntax\"|\"null\" }>\nfunction utils.get_language_list(values)\n    local regex_files = {}\n    local ts_files = {}\n\n    -- Search for regex files in syntax and after/syntax.\n    -- Its best if we strip out anything but the ft name.\n    for _, lang in pairs(vim.api.nvim_get_runtime_file(\"syntax/*.vim\", true)) do\n        local lang_name = vim.fn.fnamemodify(lang, \":t:r\")\n        table.insert(regex_files, lang_name)\n    end\n\n    for _, lang in pairs(vim.api.nvim_get_runtime_file(\"after/syntax/*.vim\", true)) do\n        local lang_name = vim.fn.fnamemodify(lang, \":t:r\")\n        table.insert(regex_files, lang_name)\n    end\n\n    -- Search for available parsers\n    for _, parser in pairs(vim.api.nvim_get_runtime_file(\"parser/*.so\", true)) do\n        local parser_name = assert(vim.fn.fnamemodify(parser, \":t:r\"))\n        ts_files[parser_name] = true\n    end\n\n    local ret = {}\n\n    for _, syntax in pairs(regex_files) do\n        if ts_files[syntax] then\n            ret[syntax] = { type = \"treesitter\" }\n        else\n            ret[syntax] = { type = \"syntax\" }\n        end\n    end\n\n    return values and vim.tbl_keys(ret) or ret\nend\n\n--- Gets a list of shorthands for a given language.\n--- @param reverse_lookup boolean Whether to create a reverse lookup for the table.\n--- @return LanguageList\nfunction utils.get_language_shorthands(reverse_lookup)\n    ---@class LanguageList\n    local langs = {\n        [\"bash\"] = { \"sh\", \"zsh\" },\n        [\"c_sharp\"] = { \"csharp\", \"cs\" },\n        [\"clojure\"] = { \"clj\" },\n        [\"cmake\"] = { \"cmake.in\" },\n        [\"commonlisp\"] = { \"cl\" },\n        [\"cpp\"] = { \"hpp\", \"cc\", \"hh\", \"c++\", \"h++\", \"cxx\", \"hxx\" },\n        [\"dockerfile\"] = { \"docker\" },\n        [\"erlang\"] = { \"erl\" },\n        [\"fennel\"] = { \"fnl\" },\n        [\"fortran\"] = { \"f90\", \"f95\" },\n        [\"go\"] = { \"golang\" },\n        [\"godot\"] = { \"gdscript\" },\n        [\"gomod\"] = { \"gm\" },\n        [\"haskell\"] = { \"hs\" },\n        [\"java\"] = { \"jsp\" },\n        [\"javascript\"] = { \"js\", \"jsx\" },\n        [\"julia\"] = { \"julia-repl\" },\n        [\"kotlin\"] = { \"kt\" },\n        [\"python\"] = { \"py\", \"gyp\" },\n        [\"ruby\"] = { \"rb\", \"gemspec\", \"podspec\", \"thor\", \"irb\" },\n        [\"rust\"] = { \"rs\" },\n        [\"supercollider\"] = { \"sc\" },\n        [\"typescript\"] = { \"ts\" },\n        [\"verilog\"] = { \"v\" },\n        [\"yaml\"] = { \"yml\" },\n    }\n\n    -- TODO: `vim.tbl_add_reverse_lookup` deprecated: NO ALTERNATIVES\n    -- GOOD JOB CORE DEVS\n    -- <https://github.com/neovim/neovim/pull/27639>\n    return reverse_lookup and vim.tbl_add_reverse_lookup(langs) or langs ---@diagnostic disable-line\nend\n\n--- Checks whether Neovim is running at least at a specific version.\n--- @param major number The major release of Neovim.\n--- @param minor number The minor release of Neovim.\n--- @param patch number The patch number (in case you need it).\n--- @return boolean # Whether Neovim is running at the same or a higher version than the one given.\nfunction utils.is_minimum_version(major, minor, patch)\n    if major ~= version.major then\n        return major < version.major\n    end\n    if minor ~= version.minor then\n        return minor < version.minor\n    end\n    if patch ~= version.patch then\n        return patch < version.patch\n    end\n    return true\nend\n\n--- Parses a version string like \"0.4.2\" and provides back a table like { major = <number>, minor = <number>, patch = <number> }\n--- @param version_string string The input string.\n--- @return table? # The parsed version string, or `nil` if a failure occurred during parsing.\nfunction utils.parse_version_string(version_string)\n    if not version_string then\n        return\n    end\n\n    -- Define variables that split the version up into 3 slices\n    local split_version, versions, ret =\n        vim.split(version_string, \".\", { plain = true }),\n        { \"major\", \"minor\", \"patch\" },\n        { major = 0, minor = 0, patch = 0 }\n\n    -- If the sliced version string has more than 3 elements error out\n    if #split_version > 3 then\n        log.warn(\n            \"Attempt to parse version:\",\n            version_string,\n            \"failed - too many version numbers provided. Version should follow this layout: <major>.<minor>.<patch>\"\n        )\n        return\n    end\n\n    -- Loop through all the versions and check whether they are valid numbers. If they are, add them to the return table\n    for i, ver in ipairs(versions) do\n        if split_version[i] then\n            local num = tonumber(split_version[i])\n\n            if not num then\n                log.warn(\"Invalid version provided, string cannot be converted to integral type.\")\n                return\n            end\n\n            ret[ver] = num\n        end\n    end\n\n    return ret\nend\n\n--- Custom Neorg notifications. Wrapper around `vim.notify`.\n--- @param msg string Message to send.\n--- @param log_level integer? Log level in `vim.log.levels`.\nfunction utils.notify(msg, log_level)\n    vim.notify(msg, log_level, { title = \"Neorg\" })\nend\n\n--- Opens up an array of files and runs a callback for each opened file.\n--- @param files (string|PathlibPath)[] An array of files to open.\n--- @param callback fun(buffer: integer, filename: string) The callback to invoke for each file.\nfunction utils.read_files(files, callback)\n    for _, file in ipairs(files) do\n        file = tostring(file)\n        local bufnr = vim.uri_to_bufnr(vim.uri_from_fname(file))\n\n        local should_delete = not vim.api.nvim_buf_is_loaded(bufnr)\n\n        vim.fn.bufload(bufnr)\n        callback(bufnr, file)\n        if should_delete then\n            vim.api.nvim_buf_delete(bufnr, { force = true })\n        end\n    end\nend\n\n-- following https://gist.github.com/kylechui/a5c1258cd2d86755f97b10fc921315c3\nfunction utils.set_operatorfunc(f)\n    utils._neorg_operatorfunc = f\n    vim.go.operatorfunc = \"v:lua.require'neorg'.utils._neorg_operatorfunc\"\nend\n\nfunction utils.wrap_dotrepeat(callback)\n    return function(...)\n        if vim.api.nvim_get_mode().mode == \"i\" then\n            callback(...)\n            return\n        end\n\n        local args = { ... }\n        utils.set_operatorfunc(function()\n            callback(unpack(args))\n        end)\n        vim.cmd(\"normal! g@l\")\n    end\nend\n\n--- Truncate input string to fit inside the `col_limit` when displayed. Takes non-ascii chars into account.\n--- @param str string The string to limit.\n--- @param col_limit integer `str` will be cut so that when displayed, the display length does not exceed this limit.\n--- @return string # Substring of input str\nfunction utils.truncate_by_cell(str, col_limit)\n    if str and str:len() == vim.api.nvim_strwidth(str) then\n        return vim.fn.strcharpart(str, 0, col_limit)\n    end\n    local short = vim.fn.strcharpart(str, 0, col_limit)\n    if vim.api.nvim_strwidth(short) > col_limit then\n        while vim.api.nvim_strwidth(short) > col_limit do\n            short = vim.fn.strcharpart(short, 0, vim.fn.strchars(short) - 1)\n        end\n    end\n    return short\nend\n\nreturn utils\n"
  },
  {
    "path": "lua/neorg/external/log.lua",
    "content": ""
  },
  {
    "path": "lua/neorg/health.lua",
    "content": "return {\n    check = function()\n        local config = require(\"neorg.core\").config.user_config\n        local modules = require(\"neorg.core.modules\")\n\n        vim.health.start(\"Neorg Configuration\")\n\n        if config.load == nil or vim.tbl_isempty(config.load) then\n            vim.health.ok(\"Empty configuration provided: Neorg will load `core.defaults` by default.\")\n        elseif type(config.load) ~= \"table\" then\n            vim.health.error(\"Invalid data type provided. `load` table should be a dictionary of modules!\")\n        else\n            vim.health.info(\"Checking `load` table...\")\n\n            for key, value in pairs(config.load) do\n                if type(key) ~= \"string\" then\n                    vim.health.error(\n                        string.format(\n                            \"Invalid data type provided within `load` table! Expected a module name (e.g. `core.defaults`), got a %s instead.\",\n                            type(key)\n                        )\n                    )\n                elseif not modules.load_module(key) then\n                    vim.health.warn(\n                        string.format(\n                            \"You are attempting to load a module `%s` which is not recognized by Neorg at this time. You may receive an error upon launching Neorg.\",\n                            key\n                        )\n                    )\n                elseif type(value) ~= \"table\" then\n                    vim.health.error(\n                        string.format(\n                            \"Invalid data type provided within `load` table for module `%s`! Expected module data (e.g. `{ config = { ... } }`), got a %s instead.\",\n                            key,\n                            type(key)\n                        )\n                    )\n                elseif value.config and type(value.config) ~= \"table\" then\n                    vim.health.error(\n                        string.format(\n                            \"Invalid data type provided within data table for module `%s`! Expected configuration data (e.g. `config = { ... }`), but `config` was set to a %s instead.\",\n                            key,\n                            type(key)\n                        )\n                    )\n                elseif #vim.tbl_keys(value) > 1 and value.config ~= nil then\n                    vim.health.warn(\n                        string.format(\n                            \"Unexpected extra data provided to module `%s` - each module only expects a `config` table to be provided, nothing else.\",\n                            key\n                        )\n                    )\n                elseif (#vim.tbl_keys(value) > 0 and value.config == nil) or #vim.tbl_keys(value) > 1 then\n                    vim.health.warn(\n                        string.format(\n                            \"Misplaced configuration data for module `%s` - it seems like you forgot to put your module configuration inside a `config = {}` table?\",\n                            key\n                        )\n                    )\n                else\n                    vim.health.ok(string.format(\"Module declaration for `%s` is well-formed\", key))\n                end\n            end\n\n            -- TODO(vhyrro): Check the correctness of the logger table too\n            if config.logger == nil or vim.tbl_isempty(config.logger) then\n                vim.health.ok(\"Default configuration for logger provided, Neorg will not output debug info.\")\n            end\n        end\n\n        vim.health.start(\"Neorg Dependencies\")\n\n        if vim.fn.executable(\"luarocks\") then\n            vim.health.ok(\"`luarocks` is installed.\")\n        else\n            vim.health.error(\n                \"`luarocks` not installed on your system! Please consult the Neorg README for installation instructions.\"\n            )\n        end\n\n        vim.health.start(\"Neorg Keybinds\")\n\n        modules.load_module(\"core.keybinds\")\n        local keybinds = modules.get_module(\"core.keybinds\")\n        local keybinds_config = modules.get_module_config(\"core.keybinds\")\n        assert(keybinds and keybinds_config, \"keybinds module missing\")\n\n        if keybinds_config.default_keybinds then\n            local key_healthcheck = keybinds.health()\n\n            if key_healthcheck.preset_exists then\n                vim.health.info(string.format(\"Neorg is configured to use keybind preset `%s`\", keybinds_config.preset))\n            else\n                vim.health.error(\n                    string.format(\n                        \"Invalid configuration found: preset `%s` does not exist! Did you perhaps make a typo?\",\n                        keybinds_config.preset\n                    )\n                )\n                return\n            end\n\n            for remap_key, remap_rhs in vim.spairs(key_healthcheck.remaps) do\n                vim.health.ok(\n                    string.format(\n                        \"Action `%s` (bound to `%s` by default) has been remapped to something else in your configuration.\",\n                        remap_rhs,\n                        remap_key\n                    )\n                )\n            end\n\n            local ok = true\n\n            for conflict_key, rhs in vim.spairs(key_healthcheck.conflicts) do\n                vim.health.warn(\n                    string.format(\n                        \"Key `%s` conflicts with a key bound by the user. Neorg will not bind this key.\",\n                        conflict_key\n                    ),\n                    string.format(\"consider mapping `%s` to a different key than the one bound by Neorg.\", rhs)\n                )\n                ok = false\n            end\n\n            if ok then\n                vim.health.ok(\"No keybind conflicts found.\")\n            end\n        else\n            vim.health.ok(\"Neorg is not configured to set any default keybinds.\")\n        end\n    end,\n}\n"
  },
  {
    "path": "lua/neorg/init.lua",
    "content": "--- @brief [[\n--- This file marks the beginning of the entire plugin. It's here that everything fires up and starts pumping.\n--- @brief ]]\n\nlocal neorg = require(\"neorg.core\")\nlocal config, log, modules, utils = neorg.config, neorg.log, neorg.modules, neorg.utils\n\n--- @module \"neorg.core.config\"\n\n--- Initializes Neorg. Parses the supplied user configuration, initializes all selected modules and adds filetype checking for `.norg`.\n--- @param cfg neorg.configuration.user? A table that reflects the structure of `config.user_config`.\n--- @see config.user_config\n--- @see neorg.configuration.user\nfunction neorg.setup(cfg)\n    -- Ensure that we are running Neovim 0.10+\n    assert(utils.is_minimum_version(0, 10, 0), \"Neorg requires at least Neovim version 0.10 to operate!\")\n\n    -- If the user supplied no configuration then generate a default one (assume the user wants the defaults)\n    cfg = cfg or {\n        load = {\n            [\"core.defaults\"] = {},\n        },\n    }\n\n    -- If no `load` table was passed whatsoever then assume the user wants the default ones.\n    -- If the user explicitly sets `load = {}` in their configs then that means they do not want\n    -- any modules loaded.\n    --\n    -- We check for nil specifically because some users might think `load = false` is a valid thing.\n    -- With the explicit check `load = false` will issue an error.\n    if cfg.load == nil then\n        cfg.load = {\n            [\"core.defaults\"] = {},\n        }\n    end\n\n    config.user_config = vim.tbl_deep_extend(\"force\", config.user_config, cfg)\n\n    -- Create a new global instance of the neorg logger.\n    log.new(config.user_config.logger or log.get_default_config(), true)\n\n    -- If the file we have entered has a `.norg` extension:\n    if vim.fn.expand(\"%:e\") == \"norg\" or not config.user_config.lazy_loading then\n        -- Then boot up the environment.\n        neorg.org_file_entered(false)\n    else\n        -- Else listen for a BufAdd event for `.norg` files and fire up the Neorg environment.\n        vim.api.nvim_create_user_command(\"NeorgStart\", function()\n            vim.cmd.delcommand(\"NeorgStart\")\n            neorg.org_file_entered(true)\n        end, {})\n\n        vim.api.nvim_create_autocmd(\"BufAdd\", {\n            pattern = \"norg\",\n            callback = function()\n                neorg.org_file_entered(false)\n            end,\n        })\n    end\nend\n\n--- This function gets called upon entering a .norg file and loads all of the user-defined modules.\n--- @param manual boolean If true then the environment was kickstarted manually by the user.\n--- @param arguments string? A list of arguments in the format of \"key=value other_key=other_value\".\nfunction neorg.org_file_entered(manual, arguments)\n    -- Extract the module list from the user config\n    local module_list = config.user_config and config.user_config.load or {}\n\n    -- If we have already started Neorg or if we haven't defined any modules to load then bail\n    if config.started or not module_list or vim.tbl_isempty(module_list) then\n        return\n    end\n\n    -- If the user has defined a post-load hook then execute it\n    if config.user_config.hook then\n        config.user_config.hook(manual, arguments)\n    end\n\n    -- If Neorg was loaded manually (through `:NeorgStart`) then set this flag to true\n    config.manual = manual\n\n    -- If the user has supplied any Neorg environment variables\n    -- then parse those here\n    if arguments and arguments:len() > 0 then\n        for key, value in arguments:gmatch(\"([%w%W]+)=([%w%W]+)\") do\n            config.arguments[key] = value\n        end\n    end\n\n    -- Go through each defined module and grab its config\n    for name, module in pairs(module_list) do\n        -- Apply the config\n        config.modules[name] = vim.tbl_deep_extend(\"force\", config.modules[name] or {}, module.config or {})\n    end\n\n    -- After all config are merged proceed to actually load the modules\n    local load_module = modules.load_module\n    for name, _ in pairs(module_list) do\n        -- If it could not be loaded then halt\n        if not load_module(name) then\n            log.warn(\"Recovering from error...\")\n            modules.loaded_modules[name] = nil\n        end\n    end\n\n    -- Goes through each loaded module and invokes neorg_post_load()\n    for _, module in pairs(modules.loaded_modules) do\n        module.neorg_post_load()\n    end\n\n    -- Set this variable to prevent Neorg from loading twice\n    config.started = true\n\n    -- Lets the entire Neorg environment know that Neorg has started!\n    modules.broadcast_event({\n        type = \"core.started\",\n        split_type = { \"core\", \"started\" },\n        filename = \"\",\n        filehead = \"\",\n        cursor_position = { 0, 0 },\n        referrer = \"core\",\n        line_content = \"\",\n        broadcast = true,\n        buffer = vim.api.nvim_get_current_buf(),\n        window = vim.api.nvim_get_current_win(),\n        mode = vim.fn.mode(),\n    })\n\n    -- Sometimes external plugins prefer hooking in to an autocommand\n    vim.api.nvim_exec_autocmds(\"User\", {\n        pattern = \"NeorgStarted\",\n    })\nend\n\n--- Returns whether or not Neorg is loaded\n--- @return boolean\nfunction neorg.is_loaded()\n    return config.started\nend\n\nreturn neorg\n"
  },
  {
    "path": "lua/neorg/modules/core/autocommands/module.lua",
    "content": "--[[\n    file: Autocommands\n    summary: Handles the creation and management of Neovim's autocommands.\n    description: Handles the creation and management of Neovim's autocommands.\n    internal: true\n    ---\nThis internal module exposes functionality for subscribing to autocommands and performing actions based on those autocommands.\n\n###### NOTE: This module will be soon deprecated, and it's favourable to use the `vim.api*` functions instead.\n\nIn your `module.setup()`, make sure to require `core.autocommands` (`requires = { \"core.autocommands\" }`)\nAfterwards in a function of your choice that gets called *after* core.autocommmands gets intialized (e.g. `load()`):\n\n```lua\nmodule.load = function()\n    module.required[\"core.autocommands\"].enable_autocommand(\"VimLeavePre\") -- Substitute VimLeavePre for any valid neovim autocommand\nend\n```\n\nAfterwards, be sure to subscribe to the event:\n\n```lua\nmodule.events.subscribed = {\n    [\"core.autocommands\"] = {\n        vimleavepre = true\n    }\n}\n```\n\nUpon receiving an event, it will come in this format:\n```lua\n{\n    type = \"core.autocommands.events.<name of autocommand, e.g. vimleavepre>\",\n    broadcast = true\n}\n```\n--]]\n\nlocal neorg = require(\"neorg.core\")\nlocal log, modules = neorg.log, neorg.modules\n\nlocal module = modules.create(\"core.autocommands\")\n\n--- This function gets invoked whenever a core.autocommands enabled autocommand is triggered. Note that this function should be only used internally\n---@param name string #The name of the autocommand that was just triggered\n---@param triggered_from_norg boolean #If true, that means we have received this event as part of a *.norg autocommand\n---@param ev? table the original event data\nfunction _neorg_module_autocommand_triggered(name, triggered_from_norg, ev)\n    local event = modules.create_event(module, name, { norg = triggered_from_norg }, ev)\n    assert(event)\n    modules.broadcast_event(event)\nend\n\n-- A convenience wrapper around modules.define_event_event\nmodule.private.autocmd_base = function(name)\n    return modules.define_event(module, name)\nend\n\n---@class core.autocommands\nmodule.public = {\n\n    --- By default, all autocommands are disabled for performance reasons. To enable them, use this command. If an invalid autocmd is given nothing happens.\n    ---@param autocmd string #The relative name of the autocommand to enable\n    ---@param dont_isolate boolean? #Defaults to false. Specifies whether the autocommand should run globally (* instead of in Neorg files (*.norg)\n    enable_autocommand = function(autocmd, dont_isolate)\n        dont_isolate = dont_isolate or false\n\n        autocmd = autocmd:lower()\n        local subscribed_autocommand = module.events.subscribed[\"core.autocommands\"][autocmd]\n\n        if subscribed_autocommand ~= nil then\n            vim.cmd(\"augroup Neorg\")\n\n            if dont_isolate and vim.fn.exists(\"#Neorg#\" .. autocmd .. \"#*\") == 0 then\n                vim.api.nvim_create_autocmd(autocmd, {\n                    callback = function(ev)\n                        _neorg_module_autocommand_triggered(\"core.autocommands.events.\" .. autocmd, false, ev)\n                    end,\n                })\n            elseif vim.fn.exists(\"#Neorg#\" .. autocmd .. \"#*.norg\") == 0 then\n                vim.api.nvim_create_autocmd(autocmd, {\n                    pattern = \"*.norg\",\n                    callback = function(ev)\n                        _neorg_module_autocommand_triggered(\"core.autocommands.events.\" .. autocmd, true, ev)\n                    end,\n                })\n            end\n            vim.cmd(\"augroup END\")\n            module.events.subscribed[\"core.autocommands\"][autocmd] = true\n        end\n    end,\n\n    version = \"0.0.8\",\n}\n\n-- All the subscribeable events for core.autocommands\nmodule.events.subscribed = {\n\n    [\"core.autocommands\"] = {\n\n        bufadd = false,\n        bufdelete = false,\n        bufenter = false,\n        buffilepost = false,\n        buffilepre = false,\n        bufhidden = false,\n        bufleave = false,\n        bufmodifiedset = false,\n        bufnew = false,\n        bufnewfile = false,\n        bufreadpost = false,\n        bufreadcmd = false,\n        bufreadpre = false,\n        bufunload = false,\n        bufwinenter = false,\n        bufwinleave = false,\n        bufwipeout = false,\n        bufwrite = false,\n        bufwritecmd = false,\n        bufwritepost = false,\n        chaninfo = false,\n        chanopen = false,\n        cmdundefined = false,\n        cmdlinechanged = false,\n        cmdlineenter = false,\n        cmdlineleave = false,\n        cmdwinenter = false,\n        cmdwinleave = false,\n        colorscheme = false,\n        colorschemepre = false,\n        completechanged = false,\n        completedonepre = false,\n        completedone = false,\n        cursorhold = false,\n        cursorholdi = false,\n        cursormoved = false,\n        cursormovedi = false,\n        diffupdated = false,\n        dirchanged = false,\n        fileappendcmd = false,\n        fileappendpost = false,\n        fileappendpre = false,\n        filechangedro = false,\n        exitpre = false,\n        filechangedshell = false,\n        filechangedshellpost = false,\n        filereadcmd = false,\n        filereadpost = false,\n        filereadpre = false,\n        filetype = false,\n        filewritecmd = false,\n        filewritepost = false,\n        filewritepre = false,\n        filterreadpost = false,\n        filterreadpre = false,\n        filterwritepost = false,\n        filterwritepre = false,\n        focusgained = false,\n        focuslost = false,\n        funcundefined = false,\n        uienter = false,\n        uileave = false,\n        insertchange = false,\n        insertcharpre = false,\n        textyankpost = false,\n        insertenter = false,\n        insertleavepre = false,\n        insertleave = false,\n        menupopup = false,\n        optionset = false,\n        quickfixcmdpre = false,\n        quickfixcmdpost = false,\n        quitpre = false,\n        remotereply = false,\n        sessionloadpost = false,\n        shellcmdpost = false,\n        signal = false,\n        shellfilterpost = false,\n        sourcepre = false,\n        sourcepost = false,\n        sourcecmd = false,\n        spellfilemissing = false,\n        stdinreadpost = false,\n        stdinreadpre = false,\n        swapexists = false,\n        syntax = false,\n        tabenter = false,\n        tableave = false,\n        tabnew = false,\n        tabnewentered = false,\n        tabclosed = false,\n        termopen = false,\n        termenter = false,\n        termleave = false,\n        termclose = false,\n        termresponse = false,\n        textchanged = false,\n        textchangedi = false,\n        textchangedp = false,\n        user = false,\n        usergettingbored = false,\n        vimenter = false,\n        vimleave = false,\n        vimleavepre = false,\n        vimresized = false,\n        vimresume = false,\n        vimsuspend = false,\n        winclosed = false,\n        winenter = false,\n        winleave = false,\n        winnew = false,\n        winscrolled = false,\n    },\n}\n\n-- All the autocommand definitions\nmodule.events.defined = {\n\n    bufadd = module.private.autocmd_base(\"bufadd\"),\n    bufdelete = module.private.autocmd_base(\"bufdelete\"),\n    bufenter = module.private.autocmd_base(\"bufenter\"),\n    buffilepost = module.private.autocmd_base(\"buffilepost\"),\n    buffilepre = module.private.autocmd_base(\"buffilepre\"),\n    bufhidden = module.private.autocmd_base(\"bufhidden\"),\n    bufleave = module.private.autocmd_base(\"bufleave\"),\n    bufmodifiedset = module.private.autocmd_base(\"bufmodifiedset\"),\n    bufnew = module.private.autocmd_base(\"bufnew\"),\n    bufnewfile = module.private.autocmd_base(\"bufnewfile\"),\n    bufreadpost = module.private.autocmd_base(\"bufreadpost\"),\n    bufreadcmd = module.private.autocmd_base(\"bufreadcmd\"),\n    bufreadpre = module.private.autocmd_base(\"bufreadpre\"),\n    bufunload = module.private.autocmd_base(\"bufunload\"),\n    bufwinenter = module.private.autocmd_base(\"bufwinenter\"),\n    bufwinleave = module.private.autocmd_base(\"bufwinleave\"),\n    bufwipeout = module.private.autocmd_base(\"bufwipeout\"),\n    bufwrite = module.private.autocmd_base(\"bufwrite\"),\n    bufwritecmd = module.private.autocmd_base(\"bufwritecmd\"),\n    bufwritepost = module.private.autocmd_base(\"bufwritepost\"),\n    chaninfo = module.private.autocmd_base(\"chaninfo\"),\n    chanopen = module.private.autocmd_base(\"chanopen\"),\n    cmdundefined = module.private.autocmd_base(\"cmdundefined\"),\n    cmdlinechanged = module.private.autocmd_base(\"cmdlinechanged\"),\n    cmdlineenter = module.private.autocmd_base(\"cmdlineenter\"),\n    cmdlineleave = module.private.autocmd_base(\"cmdlineleave\"),\n    cmdwinenter = module.private.autocmd_base(\"cmdwinenter\"),\n    cmdwinleave = module.private.autocmd_base(\"cmdwinleave\"),\n    colorscheme = module.private.autocmd_base(\"colorscheme\"),\n    colorschemepre = module.private.autocmd_base(\"colorschemepre\"),\n    completechanged = module.private.autocmd_base(\"completechanged\"),\n    completedonepre = module.private.autocmd_base(\"completedonepre\"),\n    completedone = module.private.autocmd_base(\"completedone\"),\n    cursorhold = module.private.autocmd_base(\"cursorhold\"),\n    cursorholdi = module.private.autocmd_base(\"cursorholdi\"),\n    cursormoved = module.private.autocmd_base(\"cursormoved\"),\n    cursormovedi = module.private.autocmd_base(\"cursormovedi\"),\n    diffupdated = module.private.autocmd_base(\"diffupdated\"),\n    dirchanged = module.private.autocmd_base(\"dirchanged\"),\n    fileappendcmd = module.private.autocmd_base(\"fileappendcmd\"),\n    fileappendpost = module.private.autocmd_base(\"fileappendpost\"),\n    fileappendpre = module.private.autocmd_base(\"fileappendpre\"),\n    filechangedro = module.private.autocmd_base(\"filechangedro\"),\n    exitpre = module.private.autocmd_base(\"exitpre\"),\n    filechangedshell = module.private.autocmd_base(\"filechangedshell\"),\n    filechangedshellpost = module.private.autocmd_base(\"filechangedshellpost\"),\n    filereadcmd = module.private.autocmd_base(\"filereadcmd\"),\n    filereadpost = module.private.autocmd_base(\"filereadpost\"),\n    filereadpre = module.private.autocmd_base(\"filereadpre\"),\n    filetype = module.private.autocmd_base(\"filetype\"),\n    filewritecmd = module.private.autocmd_base(\"filewritecmd\"),\n    filewritepost = module.private.autocmd_base(\"filewritepost\"),\n    filewritepre = module.private.autocmd_base(\"filewritepre\"),\n    filterreadpost = module.private.autocmd_base(\"filterreadpost\"),\n    filterreadpre = module.private.autocmd_base(\"filterreadpre\"),\n    filterwritepost = module.private.autocmd_base(\"filterwritepost\"),\n    filterwritepre = module.private.autocmd_base(\"filterwritepre\"),\n    focusgained = module.private.autocmd_base(\"focusgained\"),\n    focuslost = module.private.autocmd_base(\"focuslost\"),\n    funcundefined = module.private.autocmd_base(\"funcundefined\"),\n    uienter = module.private.autocmd_base(\"uienter\"),\n    uileave = module.private.autocmd_base(\"uileave\"),\n    insertchange = module.private.autocmd_base(\"insertchange\"),\n    insertcharpre = module.private.autocmd_base(\"insertcharpre\"),\n    textyankpost = module.private.autocmd_base(\"textyankpost\"),\n    insertenter = module.private.autocmd_base(\"insertenter\"),\n    insertleavepre = module.private.autocmd_base(\"insertleavepre\"),\n    insertleave = module.private.autocmd_base(\"insertleave\"),\n    menupopup = module.private.autocmd_base(\"menupopup\"),\n    optionset = module.private.autocmd_base(\"optionset\"),\n    quickfixcmdpre = module.private.autocmd_base(\"quickfixcmdpre\"),\n    quickfixcmdpost = module.private.autocmd_base(\"quickfixcmdpost\"),\n    quitpre = module.private.autocmd_base(\"quitpre\"),\n    remotereply = module.private.autocmd_base(\"remotereply\"),\n    sessionloadpost = module.private.autocmd_base(\"sessionloadpost\"),\n    shellcmdpost = module.private.autocmd_base(\"shellcmdpost\"),\n    signal = module.private.autocmd_base(\"signal\"),\n    shellfilterpost = module.private.autocmd_base(\"shellfilterpost\"),\n    sourcepre = module.private.autocmd_base(\"sourcepre\"),\n    sourcepost = module.private.autocmd_base(\"sourcepost\"),\n    sourcecmd = module.private.autocmd_base(\"sourcecmd\"),\n    spellfilemissing = module.private.autocmd_base(\"spellfilemissing\"),\n    stdinreadpost = module.private.autocmd_base(\"stdinreadpost\"),\n    stdinreadpre = module.private.autocmd_base(\"stdinreadpre\"),\n    swapexists = module.private.autocmd_base(\"swapexists\"),\n    syntax = module.private.autocmd_base(\"syntax\"),\n    tabenter = module.private.autocmd_base(\"tabenter\"),\n    tableave = module.private.autocmd_base(\"tableave\"),\n    tabnew = module.private.autocmd_base(\"tabnew\"),\n    tabnewentered = module.private.autocmd_base(\"tabnewentered\"),\n    tabclosed = module.private.autocmd_base(\"tabclosed\"),\n    termopen = module.private.autocmd_base(\"termopen\"),\n    termenter = module.private.autocmd_base(\"termenter\"),\n    termleave = module.private.autocmd_base(\"termleave\"),\n    termclose = module.private.autocmd_base(\"termclose\"),\n    termresponse = module.private.autocmd_base(\"termresponse\"),\n    textchanged = module.private.autocmd_base(\"textchanged\"),\n    textchangedi = module.private.autocmd_base(\"textchangedi\"),\n    textchangedp = module.private.autocmd_base(\"textchangedp\"),\n    user = module.private.autocmd_base(\"user\"),\n    usergettingbored = module.private.autocmd_base(\"usergettingbored\"),\n    vimenter = module.private.autocmd_base(\"vimenter\"),\n    vimleave = module.private.autocmd_base(\"vimleave\"),\n    vimleavepre = module.private.autocmd_base(\"vimleavepre\"),\n    vimresized = module.private.autocmd_base(\"vimresized\"),\n    vimresume = module.private.autocmd_base(\"vimresume\"),\n    vimsuspend = module.private.autocmd_base(\"vimsuspend\"),\n    winclosed = module.private.autocmd_base(\"winclosed\"),\n    winenter = module.private.autocmd_base(\"winenter\"),\n    winleave = module.private.autocmd_base(\"winleave\"),\n    winnew = module.private.autocmd_base(\"winnew\"),\n    winscrolled = module.private.autocmd_base(\"winscrolled\"),\n}\n\nmodule.examples = {\n    [\"Binding to an Autocommand\"] = function()\n        local mymodule = modules.create(\"my.module\")\n\n        mymodule.setup = function()\n            return {\n                success = true,\n                requires = {\n                    \"core.autocommands\", -- Be sure to require the module!\n                },\n            }\n        end\n\n        mymodule.load = function()\n            -- Enable an autocommand (in this case InsertLeave)\n            module.required[\"core.autocommands\"].enable_autocommand(\"InsertLeave\")\n        end\n\n        -- Listen for any incoming events\n        mymodule.on_event = function(event)\n            -- If it's the event we're looking for then do something!\n            if event.type == \"core.autocommands.events.insertleave\" then\n                log.warn(\"We left insert mode!\")\n            end\n        end\n\n        mymodule.events.subscribed = {\n            [\"core.autocommands\"] = {\n                insertleave = true, -- Be sure to listen in for this event!\n            },\n        }\n\n        return mymodule\n    end,\n}\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/clipboard/code-blocks/module.lua",
    "content": "--[[\n    file: Clipboard-Code-Blocks\n    title: Comfortable Code Copying in Neorg\n    summary: Removes beginning whitespace from text copied from code blocks.\n    embed: https://user-images.githubusercontent.com/76052559/216775085-7e808dbd-4985-49fa-b4c2-069b9782b300.gif\n    ---\nThe `code-blocks` module removes leading whitespace when copying from an `@code` tag, allowing\nfor easy pasting into external applications.\n\nTo use it, simply highlight some code within an `@code` block and paste it elsewhere!\nThis functionality will **only** work if the selection is inside the `@code` section,\nexcluding the `@code` and `@end` portion itself.\n\nIf the conditions are not met, the content is copied normally, preserving all indentation.\n--]]\n\nlocal neorg = require(\"neorg.core\")\nlocal modules = neorg.modules\n\nlocal module = modules.create(\"core.clipboard.code-blocks\")\n\nmodule.load = function()\n    modules.await(\"core.clipboard\", function(clipboard)\n        clipboard.add_callback(\"ranged_verbatim_tag_content\", function(node, content, position)\n            -- TODO: Handle visual/visual line/visual block modes\n\n            -- The end of \"ranged_tag_content\" spans one line too many\n            if position[\"end\"][1] > node:end_() - 1 then\n                return\n            end\n\n            -- Check if the start of the selection that was made is worth cutting off.\n            local _, indentation = node:start()\n\n            for i, line in ipairs(content) do\n                if i == 1 then\n                    local amount_to_cut_off = position[\"start\"][2] - indentation\n\n                    if amount_to_cut_off < 0 then\n                        content[i] = line:sub(-amount_to_cut_off + 1)\n                    end\n\n                    goto continue\n                end\n\n                content[i] = line:sub(indentation + 1)\n                ::continue::\n            end\n\n            return content\n        end, true)\n    end)\nend\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/clipboard/module.lua",
    "content": "--[[\n    file: Clipboard\n    title: Quality of Life Features for the Clipboard\n    summary: A module to manipulate and interact with the user's clipboard.\n    internal: true\n    ---\nThe clipboard module is a minimal and generic module allowing to overwrite or add special behaviour to the\n`y` (yank) keybind in Neovim.\n--]]\n\nlocal neorg = require(\"neorg.core\")\nlocal lib, modules = neorg.lib, neorg.modules\n\nlocal module = modules.create(\"core.clipboard\")\n\n---@type core.integrations.treesitter\nlocal ts\n\nmodule.setup = function()\n    return {\n        requires = {\n            \"core.integrations.treesitter\",\n        },\n    }\nend\n\nmodule.load = function()\n    ts = module.required[\"core.integrations.treesitter\"]\n    vim.api.nvim_create_autocmd(\"TextYankPost\", {\n        callback = function(data)\n            if\n                vim.api.nvim_get_option_value(\"filetype\", { buf = data.buf }) ~= \"norg\"\n                or vim.v.event.operator ~= \"y\"\n            then\n                return\n            end\n\n            local range = { vim.api.nvim_buf_get_mark(data.buf, \"[\"), vim.api.nvim_buf_get_mark(data.buf, \"]\") }\n            range[1][1] = range[1][1] - 1\n            range[2][1] = range[2][1] - 1\n\n            for i = range[1][1], range[2][1] do\n                local node = ts.get_first_node_on_line(data.buf, i)\n\n                while node and node:parent() do\n                    if module.private.callbacks[node:type()] then\n                        local register = vim.fn.getreg(assert(vim.v.register))\n\n                        vim.fn.setreg(\n                            vim.v.register,\n                            lib.filter(module.private.callbacks[node:type()], function(_, callback)\n                                if callback.strict and (range[1][1] < i or range[2][1] > node:end_()) then\n                                    return\n                                end\n\n                                return callback.cb(\n                                    node,\n                                    vim.split(assert(register --[[@as string]]), \"\\n\", {\n                                        plain = true,\n                                        -- TODO: This causes problems in places\n                                        -- where you actually want to copy\n                                        -- newlines.\n                                        trimempty = true,\n                                    }),\n                                    {\n                                        start = range[1],\n                                        [\"end\"] = range[2],\n                                        current = { i, range[1][2] },\n                                    }\n                                )\n                            end) or register,\n                            vim.v.event.regtype ---@diagnostic disable-line\n                        )\n\n                        return\n                    end\n\n                    node = node:parent()\n                end\n            end\n        end,\n    })\nend\n\nmodule.private = {\n    callbacks = {},\n}\n\n--- @class core.clipboard\nmodule.public = {\n    add_callback = function(node_type, func, strict)\n        module.private.callbacks[node_type] = module.private.callbacks[node_type] or {}\n        table.insert(module.private.callbacks[node_type], { cb = func, strict = strict })\n    end,\n}\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/completion/module.lua",
    "content": "--[[\n    file: Completion\n    title: Get completions in Neorg files\n    summary: A wrapper to interface with several different completion engines.\n    ---\n\nThis module is an intermediary between Neorg and the completion engine of your choice. After setting up this\nmodule (this usually just involves setting the `engine` field in the [configuration](#configuration) section),\nplease read the corresponding wiki page for the engine you selected ([`nvim-cmp`](@core.integrations.nvim-cmp)\n[`coq_nvim`](@core.integrations.coq_nvim) or [`nvim-compe`](@core.integrations.nvim-compe)) to complete setup.\n\nCompletions are provided in the following cases (examples in (), `|` represents the cursor location):\n- TODO items (`- (|`)\n- @ tags (`@|`)\n- \\# tags (`#|`)\n- file path links (`{:|`) provides workspace relative paths (`:$/workspace/relative/path:`)\n- header links (`{*|`)\n- fuzzy header links (`{#|`)\n- footnotes (`{^|`)\n- file path + header links (`{:path:*|`)\n- file path + fuzzy header links (`{:path:#|`)\n- file path + footnotes (`{:path:^|`)\n- anchor names (`[|`)\n- link names (`{<somelink>}[|`)\n\nHeader completions will show only valid headers at the current level in the current or specified file. All\nlink completions are smart about closing `:` and `}`.\n--]]\n\nlocal neorg = require(\"neorg.core\")\nlocal Path = require(\"pathlib\")\nlocal log, modules, utils = neorg.log, neorg.modules, neorg.utils\nlocal dirutils, dirman, link_utils, ts\n\nlocal module = modules.create(\"core.completion\")\n\nmodule.config.public = {\n    -- The engine to use for completion.\n    --\n    -- Possible values:\n    -- - [`\"nvim-cmp\"`](@core.integrations.nvim-cmp)\n    -- - [`\"coq_nvim\"`](@core.integrations.coq_nvim)\n    -- - [`\"nvim-compe\"`](@core.integrations.nvim-compe)\n    -- - `{ module_name = \"external.lsp-completion\" }` this must be used with\n    --   [neorg-interim-ls](https://github.com/benlubas/neorg-interim-ls) and can provide\n    --   completions through a shim Language Server. This allows users without an auto complete\n    --   plugin to still get Neorg completions\n    engine = nil,\n\n    -- The identifier for the Neorg source.\n    name = \"[Neorg]\",\n}\n\nmodule.setup = function()\n    return {\n        success = true,\n        requires = { \"core.dirman\", \"core.dirman.utils\", \"core.integrations.treesitter\", \"core.links\" },\n    }\nend\n\n---@class neorg.completion_engine\n---@field create_source function\n\nmodule.private = {\n    ---@type neorg.completion_engine\n    engine = nil,\n\n    --- Get a list of all norg files in current workspace. Returns { workspace_path, norg_files }\n    --- @return { [1]: PathlibPath, [2]: PathlibPath[]|nil }|nil\n    get_norg_files = function()\n        local current_workspace = dirman.get_current_workspace()\n        local norg_files = dirman.get_norg_files(current_workspace[1])\n        return { current_workspace[2], norg_files }\n    end,\n\n    --- Get the closing characters for a link completion\n    --- @param context table\n    --- @param colon boolean should there be a closing colon?\n    --- @return string \"\", \":\", or \":}\" depending on what's needed\n    get_closing_chars = function(context, colon)\n        local offset = 1\n        local closing_colon = \"\"\n        if colon then\n            closing_colon = \":\"\n            if string.sub(context.full_line, context.char + offset, context.char + offset) == \":\" then\n                closing_colon = \"\"\n                offset = 2\n            end\n        end\n\n        local closing_brace = \"}\"\n        if string.sub(context.full_line, context.char + offset, context.char + offset) == \"}\" then\n            closing_brace = \"\"\n        end\n\n        return closing_colon .. closing_brace\n    end,\n\n    --- query all the linkable items in a given buffer/file for a given link type\n    ---@param source number | string | PathlibPath bufnr or file path\n    ---@param link_type \"generic\" | \"definition\" | \"footnote\" | string\n    get_linkables = function(source, link_type)\n        local query_str = link_utils.get_link_target_query_string(link_type)\n        local norg_parser, iter_src = ts.get_ts_parser(source)\n        if not norg_parser then\n            return {}\n        end\n        local norg_tree = norg_parser:parse()[1]\n        local query = vim.treesitter.query.parse(\"norg\", query_str)\n        local links = {}\n        for id, node in query:iter_captures(norg_tree:root(), iter_src, 0, -1) do\n            local capture = query.captures[id]\n            if capture == \"title\" then\n                local original_title = ts.get_node_text(node, iter_src)\n                if original_title then\n                    local title = original_title:gsub(\"\\\\\", \"\")\n                    title = title:gsub(\"%s+\", \" \")\n                    title = title:gsub(\"^%s+\", \"\")\n                    table.insert(links, {\n                        original_title = original_title,\n                        title = title,\n                        node = node,\n                    })\n                end\n            end\n        end\n        return links\n    end,\n\n    generate_file_links = function(context, _prev, _saved, _match)\n        local res = {}\n        local files = module.private.get_norg_files()\n        if not files or not files[2] then\n            return {}\n        end\n\n        local closing_chars = module.private.get_closing_chars(context, true)\n        for _, file in pairs(files[2]) do\n            if not file:samefile(Path.new(vim.api.nvim_buf_get_name(0))) then\n                local rel = file:relative_to(files[1], false)\n                if rel and rel:len() > 0 then\n                    local link = \"$/\" .. rel:with_suffix(\"\"):tostring() .. closing_chars\n                    table.insert(res, link)\n                end\n            end\n        end\n\n        return res\n    end,\n\n    --- Generate list of autocompletion suggestions for links\n    --- @param context table\n    --- @param source number | string | PathlibPath\n    --- @param node_type string\n    --- @return string[]\n    suggestions = function(context, source, node_type)\n        local leading_whitespace = \" \"\n        if context.before_char == \" \" then\n            leading_whitespace = \"\"\n        end\n        local links = module.private.get_linkables(source, node_type)\n        local closing_chars = module.private.get_closing_chars(context, false)\n        return vim.iter(links)\n            :map(function(x)\n                return leading_whitespace .. x.title .. closing_chars\n            end)\n            :totable()\n    end,\n\n    --- All the things that you can link to (`{#|}` completions)\n    local_link_targets = function(context, _prev, _saved, _match)\n        return module.private.suggestions(context, 0, \"generic\")\n    end,\n\n    local_heading_links = function(context, _prev, _saved, match)\n        local heading_level = match[2] and #match[2]\n        return module.private.suggestions(context, 0, (\"heading%d\"):format(heading_level))\n    end,\n\n    foreign_heading_links = function(context, _prev, _saved, match)\n        local file = match[1]\n        local heading_level = match[2] and #match[2]\n        if file then\n            file = dirutils.expand_pathlib(file)\n            return module.private.suggestions(context, file, (\"heading%d\"):format(heading_level))\n        end\n        return {}\n    end,\n\n    foreign_generic_links = function(context, _prev, _saved, match)\n        local file = match[1]\n        if file then\n            file = dirutils.expand_pathlib(file)\n            return module.private.suggestions(context, file, \"generic\")\n        end\n        return {}\n    end,\n\n    local_footnote_links = function(context, _prev, _saved, _match)\n        return module.private.suggestions(context, 0, \"footnote\")\n    end,\n\n    foreign_footnote_links = function(context, _prev, _saved, match)\n        local file = match[2]\n        if match[2] then\n            file = dirutils.expand_pathlib(file)\n            return module.private.suggestions(context, file, \"footnote\")\n        end\n        return {}\n    end,\n\n    --- The node context for normal norg (ie. not in a code block)\n    normal_norg = function(current, previous, _, _)\n        -- If no previous node exists then try verifying the current node instead\n        if not previous then\n            return current and (current:type() ~= \"translation_unit\" or current:type() == \"document\") or false\n        end\n\n        -- If the previous node is not tag parameters or the tag name\n        -- (i.e. we are not inside of a tag) then show auto completions\n        return previous:type() ~= \"tag_parameters\" and previous:type() ~= \"tag_name\"\n    end,\n}\n\n---Suggest common link names for the given link. Suggests:\n--- - target name if the link point to a heading/footer/etc.\n--- - metadata `title` field\n--- - file description\n---@return string[]\nmodule.private.foreign_link_names = function(_context, _prev, _saved, match)\n    local file, target = match[2], match[3]\n    local path = dirutils.expand_pathlib(file)\n    local meta = ts.get_document_metadata(path)\n    local suggestions = {}\n    if meta then\n        table.insert(suggestions, meta.title)\n        table.insert(suggestions, meta.description)\n    end\n    if target ~= \"\" then\n        table.insert(suggestions, target)\n    end\n    return suggestions\nend\n\n---provide suggestions for anchors that are already defined in the document\n---@return string[]\nmodule.private.anchor_suggestions = function(_context, _prev, _saved, _match)\n    local suggestions = {}\n\n    local anchor_query_string = [[\n        (anchor_definition\n            (link_description\n              text: (paragraph) @anchor_name ))\n    ]]\n\n    ts.execute_query(anchor_query_string, function(query, id, node, _metadata)\n        local capture_name = query.captures[id]\n        if capture_name == \"anchor_name\" then\n            table.insert(suggestions, ts.get_node_text(node, 0))\n        end\n    end, 0)\n    return suggestions\nend\n\n--- suggest the link target name\n---@return string[]\nmodule.private.local_link_names = function(_context, _prev, _saved, match)\n    local target = match[2]\n    if target then\n        target = target:gsub(\"^%s+\", \"\")\n        target = target:gsub(\"%s+$\", \"\")\n    end\n    return { target }\nend\n\nmodule.load = function()\n    -- If we have not defined an engine then bail\n    if not module.config.public.engine then\n        log.error(\"No engine specified, aborting...\")\n        return\n    end\n\n    -- check if a custom completion module is provided\n    if type(module.config.public.engine) == \"table\" and module.config.public.engine[\"module_name\"] then\n        local completion_module = module.config.public.engine[\"module_name\"]\n        modules.load_module_as_dependency(completion_module, module.name, {})\n        module.private.engine = modules.get_module(completion_module)\n    elseif module.config.public.engine == \"nvim-compe\" and modules.load_module(\"core.integrations.nvim-compe\") then\n        -- If our engine is compe then attempt to load the integration module for nvim-compe\n        modules.load_module_as_dependency(\"core.integrations.nvim-compe\", module.name, {})\n        module.private.engine = modules.get_module(\"core.integrations.nvim-compe\")\n    elseif module.config.public.engine == \"nvim-cmp\" and modules.load_module(\"core.integrations.nvim-cmp\") then\n        modules.load_module_as_dependency(\"core.integrations.nvim-cmp\", module.name, {})\n        module.private.engine = modules.get_module(\"core.integrations.nvim-cmp\")\n    elseif module.config.public.engine == \"coq_nvim\" and modules.load_module(\"core.integrations.coq_nvim\") then\n        modules.load_module_as_dependency(\"core.integrations.coq_nvim\", module.name, {})\n        module.private.engine = modules.get_module(\"core.integrations.coq_nvim\")\n    else\n        log.error(\"Unable to load completion module -\", module.config.public.engine, \"is not a recognized engine.\")\n        return\n    end\n\n    dirutils = module.required[\"core.dirman.utils\"]\n    dirman = module.required[\"core.dirman\"]\n    link_utils = module.required[\"core.links\"]\n    ---@type core.integrations.treesitter\n    ts = module.required[\"core.integrations.treesitter\"]\n\n    -- Set a special function in the integration module to allow it to communicate with us\n    module.private.engine.invoke_completion_engine = function(context) ---@diagnostic disable-line\n        return module.public.complete(context) ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n    end\n\n    -- Create the integration engine's source\n    module.private.engine.create_source({\n        completions = module.config.public.completions,\n    })\nend\n\n---@class core.completion\nmodule.public = {\n\n    -- Define completions\n    completions = {\n        { -- Create a new completion (for `@|tags`)\n            -- Define the regex that should match in order to proceed\n            regex = \"^%s*@(%w*)\",\n\n            -- If regex can be matched, this item then gets verified via TreeSitter's AST\n            node = module.private.normal_norg,\n\n            -- The actual elements to show if the above tests were true\n            complete = {\n                \"table\",\n                \"code\",\n                \"image\",\n                \"embed\",\n                \"document\",\n            },\n\n            -- Additional options to pass to the completion engine\n            options = {\n                type = \"Tag\",\n                completion_start = \"@\",\n            },\n\n            -- We might have matched the top level item, but can we match it with any\n            -- more precision? Descend down the rabbit hole and try to more accurately match\n            -- the line.\n            descend = {\n                -- The cycle continues\n                {\n                    regex = \"document%.%w*\",\n\n                    complete = {\n                        \"meta\",\n                    },\n\n                    options = {\n                        type = \"Tag\",\n                    },\n\n                    descend = {},\n                },\n                {\n                    -- Define a regex (gets appended to parent's regex)\n                    regex = \"code%s+%w*\",\n                    -- No node variable, we don't need that sort of check here\n\n                    complete = utils.get_language_list(true),\n\n                    -- Extra options\n                    options = {\n                        type = \"Language\",\n                    },\n\n                    -- Don't descend any further, we've narrowed down our match\n                    descend = {},\n                },\n                {\n                    regex = \"export%s+%w*\",\n\n                    complete = utils.get_language_list(true),\n\n                    options = {\n                        type = \"Language\",\n                    },\n\n                    descend = {},\n                },\n                {\n                    regex = \"tangle%s+%w*\",\n\n                    complete = {\n                        \"<none>\",\n                    },\n\n                    options = {\n                        type = \"Property\",\n                    },\n                },\n                {\n                    regex = \"image%s+%w*\",\n\n                    complete = {\n                        \"jpeg\",\n                        \"png\",\n                        \"svg\",\n                        \"jfif\",\n                        \"exif\",\n                    },\n\n                    options = {\n                        type = \"Format\",\n                    },\n                },\n                {\n                    regex = \"embed%s+%w*\",\n\n                    complete = {\n                        \"video\",\n                        \"image\",\n                    },\n\n                    options = {\n                        type = \"Embed\",\n                    },\n                },\n            },\n        },\n        { -- `#|tags`\n            regex = \"^%s*%#(%w*)\",\n\n            complete = {\n                \"comment\",\n                \"ordered\",\n                \"time.due\",\n                \"time.start\",\n                \"contexts\",\n                \"waiting.for\",\n            },\n\n            options = {\n                type = \"Tag\",\n            },\n\n            descend = {},\n        },\n        { -- `@|end` tags\n            regex = \"^%s*@e?n?\",\n            node = function(_, previous)\n                if not previous then\n                    return false\n                end\n\n                return previous:type() == \"tag_parameters\" or previous:type() == \"tag_name\"\n            end,\n\n            complete = {\n                \"end\",\n            },\n\n            options = {\n                type = \"Directive\",\n                completion_start = \"@\",\n            },\n        },\n        { -- Detached Modifier Extensions `- (`, `* (`, etc.\n            regex = \"^%s*[%-*$~^]+%s+%(\",\n\n            complete = {\n                { \"( ) \", label = \"( ) (undone)\" },\n                { \"(-) \", label = \"(-) (pending)\" },\n                { \"(x) \", label = \"(x) (done)\" },\n                { \"(_) \", label = \"(_) (cancelled)\" },\n                { \"(!) \", label = \"(!) (important)\" },\n                { \"(+) \", label = \"(+) (recurring)\" },\n                { \"(=) \", label = \"(=) (on hold)\" },\n                { \"(?) \", label = \"(?) (uncertain)\" },\n            },\n\n            options = {\n                type = \"TODO\",\n                pre = function()\n                    local sub = vim.api.nvim_get_current_line():gsub(\"^(%s*%-+%s+%(%s*)%)\", \"%1\")\n\n                    if sub then\n                        vim.api.nvim_set_current_line(sub)\n                    end\n                end,\n\n                completion_start = \"-\",\n            },\n        },\n        { -- links for file paths `{:|`\n            regex = \"^.*{:([^:}]*)\",\n\n            node = module.private.normal_norg,\n\n            complete = module.private.generate_file_links,\n\n            options = {\n                type = \"File\",\n                completion_start = \"{\",\n            },\n        },\n        { -- links that have a file path, suggest any heading from the file `{:...:#|}`\n            regex = \"^.*{:(.*):#[^}]*\",\n\n            complete = module.private.foreign_generic_links,\n\n            node = module.private.normal_norg,\n\n            options = {\n                type = \"Reference\",\n                completion_start = \"#\",\n            },\n        },\n        { -- links that have a file path, suggest direct headings from the file `{:...:*|}`\n            regex = \"^.*{:(.*):(%*+)[^}]*\",\n\n            complete = module.private.foreign_heading_links,\n\n            node = module.private.normal_norg,\n\n            options = {\n                type = \"Reference\",\n                completion_start = \"*\",\n            },\n        },\n        { -- # links to headings in the current file `{#|}`\n            regex = \"^.*{#[^}]*\",\n\n            -- complete = module.private.generate_local_heading_links,\n            complete = module.private.local_link_targets,\n\n            node = module.private.normal_norg,\n\n            options = {\n                type = \"Reference\",\n                completion_start = \"#\",\n            },\n        },\n        { -- * links to headings in current file `{*|}`\n            regex = \"^(.*){(%*+)[^}]*\",\n            -- the first capture group is a nothing group so that match[2] is reliably the heading\n            -- level or nil if there's no heading level.\n\n            complete = module.private.local_heading_links,\n\n            node = module.private.normal_norg,\n\n            options = {\n                type = \"Reference\",\n                completion_start = \"*\",\n            },\n        },\n        { -- ^ footnote links in the current file `{^|}`\n            regex = \"^(.*){%^[^}]*\",\n\n            complete = module.private.local_footnote_links,\n\n            node = module.private.normal_norg,\n\n            options = {\n                type = \"Reference\",\n                completion_start = \"^\",\n            },\n        },\n        { -- ^ footnote links in another file `{:path:^|}`\n            regex = \"^(.*){:(.*):%^[^}]*\",\n\n            complete = module.private.foreign_footnote_links,\n\n            node = module.private.normal_norg,\n\n            options = {\n                type = \"Reference\",\n                completion_start = \"^\",\n            },\n        },\n        { -- foreign link name suggestions `{:path:target}[|]`\n            regex = \"^(.*){:([^:]*):[#$*%^]* ([^}]*)}%[\",\n\n            complete = module.private.foreign_link_names,\n\n            node = module.private.normal_norg,\n\n            options = {\n                type = \"Reference\",\n                completion_start = \"[\",\n            },\n        },\n        { -- local link name suggestions `{target}[|]` for `#`, `$`, `^`, `*` link targets\n            regex = \"^(.*){[#$*%^]+ ([^}]*)}%[\",\n\n            complete = module.private.local_link_names,\n\n            node = module.private.normal_norg,\n\n            options = {\n                type = \"Reference\",\n                completion_start = \"[\",\n            },\n        },\n        { -- complete anchor names that exist in the current buffer ` [|`\n            regex = {\n                \"^(.*)[^}]%[\",\n                \"^%[\",\n            },\n\n            complete = module.private.anchor_suggestions,\n\n            node = module.private.normal_norg,\n\n            options = {\n                type = \"Reference\",\n                completion_start = \"[\",\n            },\n        },\n    },\n\n    --- Parses the public completion table and attempts to find all valid matches\n    ---@param context table #The context provided by the integration engine\n    ---@param prev table? #The previous table of completions - used for descent\n    ---@param saved string? #The saved regex in the form of a string, used to concatenate children nodes with parent nodes' regexes\n    complete = function(context, prev, saved)\n        -- If the save variable wasn't passed then set it to an empty string\n        saved = saved or \"\"\n\n        -- If we haven't defined any explicit table to read then read the public completions table\n        local completions = prev or module.public.completions\n\n        -- Loop through every completion\n        for _, completion_data in ipairs(completions) do\n            -- If the completion data has a regex variable\n            if completion_data.regex then\n                local regexes\n\n                if type(completion_data.regex) == \"string\" then\n                    regexes = { completion_data.regex }\n                elseif type(completion_data.regex) == \"table\" then\n                    ---@diagnostic disable-next-line: cast-local-type\n                    regexes = completion_data.regex\n                else\n                    break\n                end\n\n                local match = {}\n                -- Attempt to match the current line before the cursor with any of the regex\n                -- expressions in the list, first one to succeed is used\n                ---@diagnostic disable-next-line: param-type-mismatch\n                for _, regex in ipairs(regexes) do\n                    match = { context.line:match(saved .. regex .. \"$\") }\n                    if not vim.tbl_isempty(match) then\n                        break\n                    end\n                end\n\n                -- If our match was successful\n                if not vim.tbl_isempty(match) then\n                    -- Construct a variable that will be returned on a successful match\n                    local items = type(completion_data.complete) == \"table\" and completion_data.complete\n                        or completion_data.complete(context, prev, saved, match)\n                    local ret_completions = { items = items, options = completion_data.options or {} }\n\n                    -- Set the match variable for the integration module\n                    ret_completions.match = match\n\n                    -- If the completion data has a node variable then attempt to match the current node too!\n                    if completion_data.node then\n                        -- If the type of completion data we're dealing with is a string then attempt to parse it\n                        if type(completion_data.node) == \"string\" then\n                            -- Split the completion node string down every pipe character\n                            local split = vim.split(completion_data.node --[[@as string]], \"|\")\n                            -- Check whether the first character of the string is an exclamation mark\n                            -- If this is present then it means we're looking for a node that *isn't* the one we specify\n                            local negate = split[1]:sub(0, 1) == \"!\"\n\n                            -- If we are negating then remove the leading exclamation mark so it doesn't interfere\n                            if negate then\n                                split[1] = split[1]:sub(2)\n                            end\n\n                            -- If we have a second split (i.e. in the string \"tag_name|prev\" this would be the \"prev\" string)\n                            if split[2] then\n                                -- Is our other value \"prev\"? If so, compare the current node in the syntax tree with the previous node\n                                if split[2] == \"prev\" then\n                                    -- Get the previous node\n                                    local current_node = vim.treesitter.get_node()\n\n                                    if not current_node then\n                                        return { items = {}, options = {} }\n                                    end\n\n                                    local previous_node = ts.get_previous_node(current_node, true, true)\n\n                                    -- If the previous node is nil\n                                    if not previous_node then\n                                        -- If we have specified a negation then that means our tag type doesn't match the previous tag's type,\n                                        -- which is good! That means we can return our completions\n                                        if negate then\n                                            return ret_completions\n                                        end\n\n                                        -- Otherwise continue on with the loop\n                                        goto continue\n                                    end\n\n                                    -- If we haven't negated and the previous node type is equal to the one we specified then return completions\n                                    if not negate and previous_node:type() == split[1] then\n                                        return ret_completions\n                                        -- Otherwise, if we want to negate and if the current node type is not equal to the one we specified\n                                        -- then also return completions - it means the match was successful\n                                    elseif negate and previous_node:type() ~= split[1] then\n                                        return ret_completions\n                                    else -- Otherwise just continue with the loop\n                                        goto continue\n                                    end\n                                    -- Else if our second split is equal to \"next\" then it's time to inspect the next node in the AST\n                                elseif split[2] == \"next\" then\n                                    -- Grab the next node\n                                    local current_node = vim.treesitter.get_node()\n\n                                    if not current_node then\n                                        return { items = {}, options = {} }\n                                    end\n\n                                    local next_node = ts.get_next_node(current_node, true, true)\n\n                                    -- If it's nil\n                                    if not next_node then\n                                        -- If we want to negate then return completions - the comparison was unsuccessful, which is what we wanted\n                                        if negate then\n                                            return ret_completions\n                                        end\n\n                                        -- Or just continue\n                                        goto continue\n                                    end\n\n                                    -- If we are not negating and the node values match then return completions\n                                    if not negate and next_node:type() == split[1] then\n                                        return ret_completions\n                                        -- If we are negating and then values don't match then also return completions\n                                    elseif negate and next_node:type() ~= split[1] then\n                                        return ret_completions\n                                    else\n                                        -- Else keep look through the completion table to see whether we can find another match\n                                        goto continue\n                                    end\n                                end\n                            else -- If we haven't defined a split (no pipe was found) then compare the current node\n                                if vim.treesitter.get_node():type() == split[1] then\n                                    -- If we're not negating then return completions\n                                    if not negate then\n                                        return ret_completions\n                                    else -- Else continue\n                                        goto continue\n                                    end\n                                end\n                            end\n                            -- If our completion data type is not a string but rather it is a function then\n                        elseif type(completion_data.node) == \"function\" then\n                            -- Grab all the necessary variables (current node, previous node, next node)\n                            local current_node = vim.treesitter.get_node()\n\n                            -- The file is blank, return completions\n                            if not current_node then\n                                return ret_completions\n                            end\n\n                            local next_node = ts.get_next_node(current_node, true, true)\n                            local previous_node = ts.get_previous_node(current_node, true, true)\n\n                            -- Execute the callback function with all of our parameters.\n                            -- If it returns true then that means the match was successful, and so return completions\n                            if completion_data.node(current_node, previous_node, next_node, ts) then\n                                return ret_completions\n                            end\n\n                            -- If no completions were found, try looking whether we can descend any further down the syntax tree.\n                            -- Maybe we can find something extra there?\n                            if completion_data.descend then\n                                -- Recursively call complete() with the nested table\n                                local descent = module.public.complete(\n                                    context,\n                                    completion_data.descend,\n                                    saved .. completion_data.regex\n                                )\n\n                                -- If the returned completion items actually hold some data (i.e. a match was found) then return those matches\n                                if not vim.tbl_isempty(descent.items) then\n                                    return descent\n                                end\n                            end\n\n                            -- Else just don't bother and continue\n                            goto continue\n                        end\n                    end\n\n                    -- If none of the checks matched, then we can conclude that only the regex variable was defined,\n                    -- and since that was matched properly, we can return all completions.\n                    return ret_completions\n                    -- If the regex for the current line wasn't matched then attempt to descend further down,\n                    -- similarly to what we did earlier\n                elseif completion_data.descend then\n                    -- Recursively call function with new parameters\n                    local descent =\n                        module.public.complete(context, completion_data.descend, saved .. completion_data.regex)\n\n                    -- If we had some completions from that function then return those completions\n                    if not vim.tbl_isempty(descent.items) then\n                        return descent\n                    end\n                end\n            end\n            ::continue::\n        end\n\n        -- If absolutely no matches were found return empty data (no completions)\n        return { items = {}, options = {} }\n    end,\n}\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/concealer/module.lua",
    "content": "--[[\n    file: Concealer\n    title: Display Markup as Icons, not Text\n    description: The concealer module converts verbose markup elements into beautified icons for your viewing pleasure.\n    summary: Enhances the basic Neorg experience by using icons instead of text.\n    embed: https://user-images.githubusercontent.com/76052559/216767027-726b451d-6da1-4d09-8fa4-d08ec4f93f54.png\n    ---\n\"Concealing\" is the process of hiding away from plain sight. When writing raw Norg, long strings like\n`***** Hello` or `$$ Definition` can be distracting and sometimes unpleasant when sifting through large notes.\n\nTo reduce the amount of cognitive load required to \"parse\" Norg documents with your own eyes, this module\nmasks, or sometimes completely hides many categories of markup.\n\nThe concealer depends on [Nerd Fonts >=v3.0.1](https://github.com/ryanoasis/nerd-fonts/releases/latest) to be\ninstalled on your system.\n\nThis module respects `:h conceallevel` and `:h concealcursor`. Setting the wrong values for these options can\nmake it look like this module isn't working.\n--]]\n\n-- utils  to be refactored\n\nlocal neorg = require(\"neorg.core\")\nlocal log, modules, utils = neorg.log, neorg.modules, neorg.utils\n\nlocal function in_range(k, l, r_ex)\n    return l <= k and k < r_ex\nend\n\nlocal function is_concealing_on_row_range(mode, conceallevel, concealcursor, current_row_0b, row_start_0b, row_end_0bex)\n    if conceallevel < 1 then\n        return false\n    elseif not in_range(current_row_0b, row_start_0b, row_end_0bex) then\n        return true\n    else\n        return (concealcursor:find(mode) ~= nil)\n    end\nend\n\nlocal function table_extend_in_place(tbl, tbl_ext)\n    for k, v in pairs(tbl_ext) do\n        tbl[k] = v\n    end\nend\n\nlocal function get_node_position_and_text_length(bufid, node)\n    local row_start_0b, col_start_0b = node:range()\n\n    -- FIXME parser: multi_definition_suffix, weak_paragraph_delimiter should not span across lines\n    -- assert(row_start_0b == row_end_0bin, row_start_0b .. \",\" .. row_end_0bin)\n    local text = vim.treesitter.get_node_text(node, bufid)\n    local past_end_offset_1b = text:find(\"%s\") or text:len() + 1\n    return row_start_0b, col_start_0b, (past_end_offset_1b - 1)\nend\n\nlocal function get_header_prefix_node(header_node)\n    local first_child = header_node:child(0)\n    assert(first_child:type() == header_node:type() .. \"_prefix\")\n    return first_child\nend\n\nlocal function get_line_length(bufid, row_0b)\n    return vim.api.nvim_strwidth(vim.api.nvim_buf_get_lines(bufid, row_0b, row_0b + 1, true)[1])\nend\n\n--- end utils\n\nlocal module = modules.create(\"core.concealer\", {\n    \"preset_basic\",\n    \"preset_varied\",\n    \"preset_diamond\",\n})\n\nmodule.setup = function()\n    return {\n        success = true,\n        requires = {\n            \"core.autocommands\",\n            \"core.integrations.treesitter\",\n        },\n    }\nend\n\nmodule.private = {\n    ns_icon = vim.api.nvim_create_namespace(\"neorg-conceals\"),\n    ns_prettify_flag = vim.api.nvim_create_namespace(\"neorg-conceals.prettify-flag\"),\n    rerendering_scheduled_bufids = {},\n    enabled = true,\n    cursor_record = {},\n}\n\nlocal function set_mark(bufid, row_0b, col_0b, text, highlight, ext_opts)\n    local ns_icon = module.private.ns_icon\n    local opt = {\n        virt_text = { { text, highlight } },\n        virt_text_pos = \"overlay\",\n        virt_text_win_col = nil,\n        hl_group = nil,\n        conceal = nil,\n        id = nil,\n        end_row = row_0b,\n        end_col = col_0b,\n        hl_eol = nil,\n        virt_text_hide = true,\n        hl_mode = \"combine\",\n        virt_lines = nil,\n        virt_lines_above = nil,\n        virt_lines_leftcol = nil,\n        ephemeral = nil,\n        right_gravity = nil,\n        end_right_gravity = nil,\n        priority = nil,\n        strict = nil, -- default true\n        sign_text = nil,\n        sign_hl_group = nil,\n        number_hl_group = nil,\n        line_hl_group = nil,\n        cursorline_hl_group = nil,\n        spell = nil,\n        ui_watched = nil,\n        invalidate = true,\n    }\n\n    if ext_opts then\n        table_extend_in_place(opt, ext_opts)\n    end\n\n    vim.api.nvim_buf_set_extmark(bufid, ns_icon, row_0b, col_0b, opt)\nend\n\nlocal function table_get_default_last(tbl, index)\n    return tbl[index] or tbl[#tbl]\nend\n\nlocal function get_ordered_index(bufid, prefix_node)\n    -- TODO: calculate levels in one pass, since treesitter API implementation seems to have ridiculously high complexity\n    local _, _, level = get_node_position_and_text_length(bufid, prefix_node)\n    local header_node = prefix_node:parent()\n    -- TODO: fix parser: `(ERROR)` on standalone prefix not followed by text, like `- `\n    -- assert(header_node:type() .. \"_prefix\" == prefix_node:type())\n    local sibling = header_node:prev_named_sibling()\n    local count = 1\n\n    while sibling and (sibling:type() == header_node:type()) do\n        local _, _, sibling_level = get_node_position_and_text_length(bufid, get_header_prefix_node(sibling))\n        if sibling_level < level then\n            break\n        elseif sibling_level == level then\n            count = count + 1\n        end\n        sibling = sibling:prev_named_sibling()\n    end\n\n    return count, (sibling or header_node:parent())\nend\n\nlocal function tbl_reverse(tbl)\n    local result = {}\n    for i = 1, #tbl do\n        result[i] = tbl[#tbl - i + 1]\n    end\n    return result\nend\n\nlocal function tostring_lowercase(n)\n    local t = {}\n    while n > 0 do\n        t[#t + 1] = string.char(0x61 + (n - 1) % 26)\n        n = math.floor((n - 1) / 26)\n    end\n    return table.concat(t):reverse()\nend\n\nlocal roman_numerals = {\n    { \"i\", \"ii\", \"iii\", \"iv\", \"v\", \"vi\", \"vii\", \"viii\", \"ix\" },\n    { \"x\", \"xx\", \"xxx\", \"xl\", \"l\", \"lx\", \"lxx\", \"lxxx\", \"xc\" },\n    { \"c\", \"cc\", \"ccc\", \"cd\", \"d\", \"dc\", \"dcc\", \"dccc\", \"cm\" },\n    { \"m\", \"mm\", \"mmm\" },\n}\n\nlocal function tostring_roman_lowercase(n)\n    if n >= 4000 then\n        -- too large to render\n        return\n    end\n\n    local result = {}\n    local i = 1\n    while n > 0 do\n        result[#result + 1] = roman_numerals[i][n % 10]\n        n = math.floor(n / 10)\n        i = i + 1\n    end\n    return table.concat(tbl_reverse(result))\nend\n\nlocal ordered_icon_table = {\n    [\"0\"] = function(i)\n        return tostring(i - 1)\n    end,\n    [\"1\"] = function(i)\n        return tostring(i)\n    end,\n    [\"a\"] = function(i)\n        return tostring_lowercase(i)\n    end,\n    [\"A\"] = function(i)\n        return tostring_lowercase(i):upper()\n    end,\n    [\"i\"] = function(i)\n        return tostring_roman_lowercase(i)\n    end,\n    [\"I\"] = function(i)\n        return tostring_roman_lowercase(i):upper()\n    end,\n    [\"Ⅰ\"] = {\n        \"Ⅰ\",\n        \"Ⅱ\",\n        \"Ⅲ\",\n        \"Ⅳ\",\n        \"Ⅴ\",\n        \"Ⅵ\",\n        \"Ⅶ\",\n        \"Ⅷ\",\n        \"Ⅸ\",\n        \"Ⅹ\",\n        \"Ⅺ\",\n        \"Ⅻ\",\n    },\n    [\"ⅰ\"] = {\n        \"ⅰ\",\n        \"ⅱ\",\n        \"ⅲ\",\n        \"ⅳ\",\n        \"ⅴ\",\n        \"ⅵ\",\n        \"ⅶ\",\n        \"ⅷ\",\n        \"ⅸ\",\n        \"ⅹ\",\n        \"ⅺ\",\n        \"ⅻ\",\n    },\n    [\"⒈\"] = {\n        \"⒈\",\n        \"⒉\",\n        \"⒊\",\n        \"⒋\",\n        \"⒌\",\n        \"⒍\",\n        \"⒎\",\n        \"⒏\",\n        \"⒐\",\n        \"⒑\",\n        \"⒒\",\n        \"⒓\",\n        \"⒔\",\n        \"⒕\",\n        \"⒖\",\n        \"⒗\",\n        \"⒘\",\n        \"⒙\",\n        \"⒚\",\n        \"⒛\",\n    },\n    [\"⑴\"] = {\n        \"⑴\",\n        \"⑵\",\n        \"⑶\",\n        \"⑷\",\n        \"⑸\",\n        \"⑹\",\n        \"⑺\",\n        \"⑻\",\n        \"⑼\",\n        \"⑽\",\n        \"⑾\",\n        \"⑿\",\n        \"⒀\",\n        \"⒁\",\n        \"⒂\",\n        \"⒃\",\n        \"⒄\",\n        \"⒅\",\n        \"⒆\",\n        \"⒇\",\n    },\n    [\"①\"] = {\n        \"①\",\n        \"②\",\n        \"③\",\n        \"④\",\n        \"⑤\",\n        \"⑥\",\n        \"⑦\",\n        \"⑧\",\n        \"⑨\",\n        \"⑩\",\n        \"⑪\",\n        \"⑫\",\n        \"⑬\",\n        \"⑭\",\n        \"⑮\",\n        \"⑯\",\n        \"⑰\",\n        \"⑱\",\n        \"⑲\",\n        \"⑳\",\n    },\n    [\"⒜\"] = {\n        \"⒜\",\n        \"⒝\",\n        \"⒞\",\n        \"⒟\",\n        \"⒠\",\n        \"⒡\",\n        \"⒢\",\n        \"⒣\",\n        \"⒤\",\n        \"⒥\",\n        \"⒦\",\n        \"⒧\",\n        \"⒨\",\n        \"⒩\",\n        \"⒪\",\n        \"⒫\",\n        \"⒬\",\n        \"⒭\",\n        \"⒮\",\n        \"⒯\",\n        \"⒰\",\n        \"⒱\",\n        \"⒲\",\n        \"⒳\",\n        \"⒴\",\n        \"⒵\",\n    },\n    [\"Ⓐ\"] = {\n        \"Ⓐ\",\n        \"Ⓑ\",\n        \"Ⓒ\",\n        \"Ⓓ\",\n        \"Ⓔ\",\n        \"Ⓕ\",\n        \"Ⓖ\",\n        \"Ⓗ\",\n        \"Ⓘ\",\n        \"Ⓙ\",\n        \"Ⓚ\",\n        \"Ⓛ\",\n        \"Ⓜ\",\n        \"Ⓝ\",\n        \"Ⓞ\",\n        \"Ⓟ\",\n        \"Ⓠ\",\n        \"Ⓡ\",\n        \"Ⓢ\",\n        \"Ⓣ\",\n        \"Ⓤ\",\n        \"Ⓥ\",\n        \"Ⓦ\",\n        \"Ⓧ\",\n        \"Ⓨ\",\n        \"Ⓩ\",\n    },\n    [\"ⓐ\"] = {\n        \"ⓐ\",\n        \"ⓑ\",\n        \"ⓒ\",\n        \"ⓓ\",\n        \"ⓔ\",\n        \"ⓕ\",\n        \"ⓖ\",\n        \"ⓗ\",\n        \"ⓘ\",\n        \"ⓙ\",\n        \"ⓚ\",\n        \"ⓛ\",\n        \"ⓜ\",\n        \"ⓝ\",\n        \"ⓞ\",\n        \"ⓟ\",\n        \"ⓠ\",\n        \"ⓡ\",\n        \"ⓢ\",\n        \"ⓣ\",\n        \"ⓤ\",\n        \"ⓥ\",\n        \"ⓦ\",\n        \"ⓧ\",\n        \"ⓨ\",\n        \"ⓩ\",\n    },\n}\n\nlocal memoized_ordered_icon_generator = {}\n\nlocal function format_ordered_icon(pattern, index)\n    if type(pattern) == \"function\" then\n        return pattern(index)\n    end\n\n    local gen = memoized_ordered_icon_generator[pattern]\n    if gen then\n        return gen(index)\n    end\n\n    for char_one, number_table in pairs(module.config.public.ordered_icons) do\n        local l, r = pattern:find(char_one:find(\"%w\") and \"%f[%w]\" .. char_one .. \"%f[%W]\" or char_one)\n        if l then\n            gen = function(index_)\n                local icon = type(number_table) == \"function\" and number_table(index_) or number_table[index_]\n                return icon and pattern:sub(1, l - 1) .. icon .. pattern:sub(r + 1)\n            end\n            break\n        end\n    end\n\n    gen = gen or function(_) end\n\n    memoized_ordered_icon_generator[pattern] = gen\n    return gen(index)\nend\n\nlocal superscript_digits = {\n    [\"0\"] = \"⁰\",\n    [\"1\"] = \"¹\",\n    [\"2\"] = \"²\",\n    [\"3\"] = \"³\",\n    [\"4\"] = \"⁴\",\n    [\"5\"] = \"⁵\",\n    [\"6\"] = \"⁶\",\n    [\"7\"] = \"⁷\",\n    [\"8\"] = \"⁸\",\n    [\"9\"] = \"⁹\",\n    [\"-\"] = \"⁻\",\n}\n\n---@class core.concealer\nmodule.public = {\n    icon_renderers = {\n        on_left = function(config, bufid, node)\n            if not config.icon then\n                return\n            end\n            local row_0b, col_0b, len = get_node_position_and_text_length(bufid, node)\n            local text = (\" \"):rep(len - 1) .. config.icon\n            set_mark(bufid, row_0b, col_0b, text, config.highlight)\n        end,\n\n        multilevel_on_right = function(is_ordered)\n            return function(config, bufid, node)\n                if not config.icons then\n                    return\n                end\n\n                local row_0b, col_0b, len = get_node_position_and_text_length(bufid, node)\n                local icon_pattern = table_get_default_last(config.icons, len)\n                if not icon_pattern then\n                    return\n                end\n\n                local icon = not is_ordered and icon_pattern\n                    or format_ordered_icon(icon_pattern, get_ordered_index(bufid, node))\n                if not icon then\n                    return\n                end\n\n                local text = (\" \"):rep(len - 1) .. icon\n\n                local _, first_unicode_end = text:find(\"[%z\\1-\\127\\194-\\244][\\128-\\191]*\", len)\n                local highlight = config.highlights and table_get_default_last(config.highlights, len)\n                set_mark(bufid, row_0b, col_0b, text:sub(1, first_unicode_end), highlight)\n                if vim.fn.strcharlen(text) > len then\n                    set_mark(bufid, row_0b, col_0b + len, text:sub(first_unicode_end + 1), highlight, {\n                        virt_text_pos = \"inline\",\n                    })\n                end\n            end\n        end,\n\n        footnote_concealed = function(config, bufid, node)\n            local link_title_node = node:next_named_sibling()\n            local link_title = vim.treesitter.get_node_text(link_title_node, bufid)\n            if config.numeric_superscript and link_title:match(\"^[-0-9]+$\") then\n                local t = {}\n                for i = 1, #link_title do\n                    local d = link_title:sub(i, i)\n                    table.insert(t, superscript_digits[d])\n                end\n                local superscripted_title = table.concat(t)\n                local row_start_0b, col_start_0b, _, _ = link_title_node:range()\n                local highlight = config.title_highlight\n                set_mark(bufid, row_start_0b, col_start_0b, superscripted_title, highlight)\n            end\n        end,\n\n        ---@param node TSNode\n        quote_concealed = function(config, bufid, node)\n            if not config.icons then\n                return\n            end\n\n            local prefix = node:named_child(0)\n\n            local row_0b, col_0b, len = get_node_position_and_text_length(bufid, prefix)\n\n            local last_icon, last_highlight\n\n            for _, child in ipairs(node:field(\"content\")) do\n                local row_last_0b, col_last_0b = child:end_()\n\n                -- Sometimes the parser overshoots to the next newline, breaking\n                -- the range.\n                -- To counteract this we correct the overshoot.\n                if col_last_0b == 0 then\n                    row_last_0b = row_last_0b - 1\n                end\n\n                for line = row_0b, row_last_0b do\n                    if get_line_length(bufid, line) > len then\n                        for col = 1, len do\n                            if config.icons[col] ~= nil then\n                                last_icon = config.icons[col]\n                            end\n                            if not last_icon then\n                                goto continue\n                            end\n                            last_highlight = config.highlights[col] or last_highlight\n                            set_mark(bufid, line, col_0b + (col - 1), last_icon, last_highlight)\n                            ::continue::\n                        end\n                    end\n                end\n            end\n        end,\n\n        fill_text = function(config, bufid, node)\n            if not config.icon then\n                return\n            end\n            local row_0b, col_0b, len = get_node_position_and_text_length(bufid, node)\n            local text = config.icon:rep(len)\n            set_mark(bufid, row_0b, col_0b, text, config.highlight)\n        end,\n\n        fill_multiline_chop2 = function(config, bufid, node)\n            if not config.icon then\n                return\n            end\n            local row_start_0b, col_start_0b, row_end_0bin, col_end_0bex = node:range()\n            for i = row_start_0b, row_end_0bin do\n                local l = i == row_start_0b and col_start_0b + 1 or 0\n                local r_ex = i == row_end_0bin and col_end_0bex - 1 or get_line_length(bufid, i)\n                set_mark(bufid, i, l, config.icon:rep(r_ex - l), config.highlight)\n            end\n        end,\n\n        render_horizontal_line = function(config, bufid, node)\n            if not config.icon then\n                return\n            end\n\n            local row_start_0b, col_start_0b, _, col_end_0bex = node:range()\n            local render_col_start_0b = config.left == \"here\" and col_start_0b or 0\n            local opt_textwidth = vim.bo[bufid].textwidth\n            local render_col_end_0bex = config.right == \"textwidth\" and (opt_textwidth > 0 and opt_textwidth or 79)\n                or vim.api.nvim_win_get_width(assert(vim.fn.bufwinid(bufid)))\n            local len = math.max(col_end_0bex - col_start_0b, render_col_end_0bex - render_col_start_0b)\n            set_mark(bufid, row_start_0b, render_col_start_0b, config.icon:rep(len), config.highlight)\n        end,\n\n        render_code_block = function(config, bufid, node)\n            local tag_name = vim.treesitter.get_node_text(node:named_child(0), bufid)\n            if not (tag_name == \"code\" or tag_name == \"embed\") then\n                return\n            end\n\n            local row_start_0b, col_start_0b, row_end_0bin = node:range()\n            assert(row_start_0b < row_end_0bin)\n            local conceal_on = (vim.wo.conceallevel >= 2) and config.conceal\n\n            if conceal_on then\n                for _, row_0b in ipairs({ row_start_0b, row_end_0bin }) do\n                    vim.api.nvim_buf_set_extmark(\n                        bufid,\n                        module.private.ns_icon,\n                        row_0b,\n                        0,\n                        { end_col = get_line_length(bufid, row_0b), conceal = \"\" }\n                    )\n                end\n            end\n\n            if conceal_on or config.content_only then\n                row_start_0b = row_start_0b + 1\n                row_end_0bin = row_end_0bin - 1\n            end\n\n            local line_lengths = {}\n            local max_len = config.min_width or 0\n            for row_0b = row_start_0b, row_end_0bin do\n                local len = get_line_length(bufid, row_0b)\n                if len > max_len then\n                    max_len = len\n                end\n                table.insert(line_lengths, len)\n            end\n\n            local to_eol = (config.width ~= \"content\")\n\n            for row_0b = row_start_0b, row_end_0bin do\n                local len = line_lengths[row_0b - row_start_0b + 1]\n                local mark_col_start_0b = math.max(0, col_start_0b - config.padding.left)\n                local mark_col_end_0bex = max_len + config.padding.right\n                local priority = 101\n                if len >= mark_col_start_0b then\n                    vim.api.nvim_buf_set_extmark(bufid, module.private.ns_icon, row_0b, mark_col_start_0b, {\n                        end_row = row_0b + 1,\n                        hl_eol = to_eol,\n                        hl_group = config.highlight,\n                        hl_mode = \"blend\",\n                        virt_text = not to_eol and { { (\" \"):rep(mark_col_end_0bex - len), config.highlight } } or nil,\n                        virt_text_pos = \"overlay\",\n                        virt_text_win_col = len,\n                        spell = config.spell_check,\n                        priority = priority,\n                    })\n                else\n                    vim.api.nvim_buf_set_extmark(bufid, module.private.ns_icon, row_0b, len, {\n                        end_row = row_0b + 1,\n                        hl_eol = to_eol,\n                        hl_group = config.highlight,\n                        hl_mode = \"blend\",\n                        virt_text = {\n                            { (\" \"):rep(mark_col_start_0b - len) },\n                            { not to_eol and (\" \"):rep(mark_col_end_0bex - mark_col_start_0b) or \"\", config.highlight },\n                        },\n                        virt_text_pos = \"overlay\",\n                        virt_text_win_col = len,\n                        spell = config.spell_check,\n                        priority = priority,\n                    })\n                end\n            end\n        end,\n    },\n\n    icon_removers = {\n        quote = function(_, bufid, node)\n            for _, content in ipairs(node:field(\"content\")) do\n                local end_row, end_col = content:end_()\n\n                -- This counteracts the issue where a quote can span onto the next\n                -- line, even though it shouldn't.\n                if end_col == 0 then\n                    end_row = end_row - 1\n                end\n\n                vim.api.nvim_buf_clear_namespace(bufid, module.private.ns_icon, (content:start()), end_row + 1)\n            end\n        end,\n    },\n}\n\nmodule.config.public = {\n    -- Which icon preset to use.\n    --\n    -- The currently available icon presets are:\n    -- - \"basic\" - use a mixture of icons (includes cute flower icons!)\n    -- - \"diamond\" - use diamond shapes for headings\n    -- - \"varied\" - use a mix of round and diamond shapes for headings; no cute flower icons though :(\n    icon_preset = \"basic\",\n\n    -- If true, Neorg will enable folding by default for `.norg` documents.\n    -- You may use the inbuilt Neovim folding options like `foldnestmax`,\n    -- `foldlevelstart` and others to then tune the behaviour to your liking.\n    --\n    -- Set to `false` if you do not want Neorg setting anything.\n    folds = true,\n\n    -- When set to `auto`, Neorg will open all folds when opening new documents if `foldlevel` is 0.\n    -- When set to `always`, Neorg will always open all folds when opening new documents.\n    -- When set to `never`, Neorg will not do anything.\n    init_open_folds = \"auto\",\n\n    -- Provide custom ordered icons for ordered lists.\n    -- - keys are a string matched against the first character of the values in `icons.ordered.icons`\n    -- - value are either a list of string icons to use (one for each index), or a function that\n    --   takes the index and returns the string value\n    ordered_icons = ordered_icon_table,\n\n    -- Configuration for icons.\n    --\n    -- This table contains the full configuration set for each icon, including\n    -- its query (where to be placed), render functions (how to be placed) and\n    -- characters to use.\n    --\n    -- For most use cases, the only values that you should be changing is the `icon`/`icons` field.\n    -- `icon` is a string, while `icons` is a table of strings for multilevel elements like\n    -- headings, lists, and quotes.\n    --\n    -- To disable part of the config, replace the table with `false`, or prepend `false and` to it.\n    -- For example: `done = false` or `done = false and { ... }`.\n    icons = {\n        todo = {\n            done = {\n                icon = \"󰄬\",\n                nodes = { \"todo_item_done\" },\n                render = module.public.icon_renderers.on_left,\n            },\n            pending = {\n                icon = \"󰥔\",\n                nodes = { \"todo_item_pending\" },\n                render = module.public.icon_renderers.on_left,\n            },\n            undone = {\n                icon = \" \",\n                nodes = { \"todo_item_undone\" },\n                render = module.public.icon_renderers.on_left,\n            },\n            uncertain = {\n                icon = \"\",\n                nodes = { \"todo_item_uncertain\" },\n                render = module.public.icon_renderers.on_left,\n            },\n            on_hold = {\n                icon = \"\",\n                nodes = { \"todo_item_on_hold\" },\n                render = module.public.icon_renderers.on_left,\n            },\n            cancelled = {\n                icon = \"\",\n                nodes = { \"todo_item_cancelled\" },\n                render = module.public.icon_renderers.on_left,\n            },\n            recurring = {\n                icon = \"↺\",\n                nodes = { \"todo_item_recurring\" },\n                render = module.public.icon_renderers.on_left,\n            },\n            urgent = {\n                icon = \"⚠\",\n                nodes = { \"todo_item_urgent\" },\n                render = module.public.icon_renderers.on_left,\n            },\n        },\n\n        list = {\n            icons = { \"•\" },\n            nodes = {\n                \"unordered_list1_prefix\",\n                \"unordered_list2_prefix\",\n                \"unordered_list3_prefix\",\n                \"unordered_list4_prefix\",\n                \"unordered_list5_prefix\",\n                \"unordered_list6_prefix\",\n            },\n            render = module.public.icon_renderers.multilevel_on_right(false),\n        },\n        ordered = {\n            icons = { \"1.\", \"A.\", \"a.\", \"(1)\", \"I.\", \"i.\" },\n            nodes = {\n                \"ordered_list1_prefix\",\n                \"ordered_list2_prefix\",\n                \"ordered_list3_prefix\",\n                \"ordered_list4_prefix\",\n                \"ordered_list5_prefix\",\n                \"ordered_list6_prefix\",\n            },\n            render = module.public.icon_renderers.multilevel_on_right(true),\n        },\n        quote = {\n            icons = { \"│\" },\n            nodes = {\n                \"quote1\",\n                \"quote2\",\n                \"quote3\",\n                \"quote4\",\n                \"quote5\",\n                \"quote6\",\n            },\n            highlights = {\n                \"@neorg.quotes.1.prefix\",\n                \"@neorg.quotes.2.prefix\",\n                \"@neorg.quotes.3.prefix\",\n                \"@neorg.quotes.4.prefix\",\n                \"@neorg.quotes.5.prefix\",\n                \"@neorg.quotes.6.prefix\",\n            },\n            render = module.public.icon_renderers.quote_concealed,\n            clear = module.public.icon_removers.quote,\n        },\n        heading = {\n            icons = { \"◉\", \"◎\", \"○\", \"✺\", \"▶\", \"⤷\" },\n            highlights = {\n                \"@neorg.headings.1.prefix\",\n                \"@neorg.headings.2.prefix\",\n                \"@neorg.headings.3.prefix\",\n                \"@neorg.headings.4.prefix\",\n                \"@neorg.headings.5.prefix\",\n                \"@neorg.headings.6.prefix\",\n            },\n            nodes = {\n                \"heading1_prefix\",\n                \"heading2_prefix\",\n                \"heading3_prefix\",\n                \"heading4_prefix\",\n                \"heading5_prefix\",\n                \"heading6_prefix\",\n                concealed = {\n                    \"link_target_heading1\",\n                    \"link_target_heading2\",\n                    \"link_target_heading3\",\n                    \"link_target_heading4\",\n                    \"link_target_heading5\",\n                    \"link_target_heading6\",\n                },\n            },\n            render = module.public.icon_renderers.multilevel_on_right(false),\n        },\n        definition = {\n            single = {\n                icon = \"≡\",\n                nodes = { \"single_definition_prefix\", concealed = { \"link_target_definition\" } },\n                render = module.public.icon_renderers.on_left,\n            },\n            multi_prefix = {\n                icon = \"⋙ \",\n                nodes = { \"multi_definition_prefix\" },\n                render = module.public.icon_renderers.on_left,\n            },\n            multi_suffix = {\n                icon = \"⋘ \",\n                nodes = { \"multi_definition_suffix\" },\n                render = module.public.icon_renderers.on_left,\n            },\n        },\n\n        footnote = {\n            single = {\n                icon = \"⁎\",\n                -- When set to true, footnote link with numeric title will be\n                -- concealed to superscripts.\n                numeric_superscript = true,\n                title_highlight = \"@neorg.footnotes.title\",\n                nodes = { \"single_footnote_prefix\", concealed = { \"link_target_footnote\" } },\n                render = module.public.icon_renderers.on_left,\n                render_concealed = module.public.icon_renderers.footnote_concealed,\n            },\n            multi_prefix = {\n                icon = \"⁑ \",\n                nodes = { \"multi_footnote_prefix\" },\n                render = module.public.icon_renderers.on_left,\n            },\n            multi_suffix = {\n                icon = \"⁑ \",\n                nodes = { \"multi_footnote_suffix\" },\n                render = module.public.icon_renderers.on_left,\n            },\n        },\n\n        delimiter = {\n            weak = {\n                icon = \"⟨\",\n                highlight = \"@neorg.delimiters.weak\",\n                nodes = { \"weak_paragraph_delimiter\" },\n                render = module.public.icon_renderers.fill_text,\n            },\n            strong = {\n                icon = \"⟪\",\n                highlight = \"@neorg.delimiters.strong\",\n                nodes = { \"strong_paragraph_delimiter\" },\n                render = module.public.icon_renderers.fill_text,\n            },\n            horizontal_line = {\n                icon = \"─\",\n                highlight = \"@neorg.delimiters.horizontal_line\",\n                nodes = { \"horizontal_line\" },\n                -- The starting position of horizontal lines:\n                -- - \"window\": the horizontal line starts from the first column, reaching the left of the window\n                -- - \"here\": the horizontal line starts from the node column\n                left = \"here\",\n                -- The ending position of horizontal lines:\n                -- - \"window\": the horizontal line ends at the last column, reaching the right of the window\n                -- - \"textwidth\": the horizontal line ends at column `textwidth` or 79 when it's set to zero\n                right = \"window\",\n                render = module.public.icon_renderers.render_horizontal_line,\n            },\n        },\n\n        markup = {\n            spoiler = {\n                icon = \"•\",\n                highlight = \"@neorg.markup.spoiler\",\n                nodes = { \"spoiler\" },\n                render = module.public.icon_renderers.fill_multiline_chop2,\n            },\n        },\n\n        -- Options that control the behaviour of code block dimming\n        -- (placing a darker background behind `@code` tags).\n        code_block = {\n            -- If true will only dim the content of the code block (without the\n            -- `@code` and `@end` lines), not the entirety of the code block itself.\n            content_only = true,\n\n            -- The width to use for code block backgrounds.\n            --\n            -- When set to `fullwidth` (the default), will create a background\n            -- that spans the width of the buffer.\n            --\n            -- When set to `content`, will only span as far as the longest line\n            -- within the code block.\n            width = \"fullwidth\",\n\n            -- When set to a number, the code block background will be at least\n            -- this many chars wide. Useful in conjunction with `width = \"content\"`\n            min_width = nil,\n\n            -- Additional padding to apply to either the left or the right. Making\n            -- these values negative is considered undefined behaviour (it is\n            -- likely to work, but it's not officially supported).\n            padding = {\n                left = 0,\n                right = 0,\n            },\n\n            -- If `true` will conceal (hide) the `@code` and `@end` portion of the code\n            -- block.\n            conceal = false,\n\n            -- If `false` will disable spell check on code blocks when 'spell' option is switched on.\n            spell_check = true,\n\n            nodes = { \"ranged_verbatim_tag\" },\n            highlight = \"@neorg.tags.ranged_verbatim.code_block\",\n            render = module.public.icon_renderers.render_code_block,\n            insert_enabled = true,\n        },\n    },\n}\n\nlocal function pos_eq(pos1, pos2)\n    return (pos1.x == pos2.x) and (pos1.y == pos2.y)\nend\n\nlocal function pos_le(pos1, pos2)\n    return pos1.x < pos2.x or (pos1.x == pos2.x and pos1.y <= pos2.y)\nend\n\n-- local function pos_lt(pos1, pos2)\n--     return pos1.x < pos2.x or (pos1.x == pos2.x and pos1.y < pos2.y)\n-- end\n\nlocal function remove_extmarks(bufid, pos_start_0b_0b, pos_end_0bin_0bex)\n    assert(pos_le(pos_start_0b_0b, pos_end_0bin_0bex))\n    if pos_eq(pos_start_0b_0b, pos_end_0bin_0bex) then\n        return\n    end\n\n    local ns_icon = module.private.ns_icon\n    for _, result in\n        ipairs(\n            vim.api.nvim_buf_get_extmarks(\n                bufid,\n                ns_icon,\n                { pos_start_0b_0b.x, pos_start_0b_0b.y },\n                { pos_end_0bin_0bex.x - ((pos_end_0bin_0bex.y == 0) and 1 or 0), pos_end_0bin_0bex.y - 1 },\n                {}\n            )\n        )\n    do\n        local extmark_id = result[1]\n        -- TODO: Optimize\n        -- local node_pos_0b_0b = { x = result[2], y = result[3] }\n        -- assert(\n        --     pos_le(pos_start_0b_0b, node_pos_0b_0b) and pos_le(node_pos_0b_0b, pos_end_0bin_0bex),\n        --     (\"start=%s, end=%s, node=%s\"):format(\n        --         vim.inspect(pos_start_0b_0b),\n        --         vim.inspect(pos_end_0bin_0bex),\n        --         vim.inspect(node_pos_0b_0b)\n        --     )\n        -- )\n        vim.api.nvim_buf_del_extmark(bufid, ns_icon, extmark_id)\n    end\nend\n\nlocal function is_inside_example(_)\n    -- TODO: waiting for parser fix\n    return false\nend\n\nlocal function should_skip_prettify(mode, current_row_0b, node, config, row_start_0b, row_end_0bex)\n    local result\n    if config.insert_enabled then\n        result = false\n    elseif (mode == \"i\") and in_range(current_row_0b, row_start_0b, row_end_0bex) then\n        result = true\n    elseif is_inside_example(node) then\n        result = true\n    else\n        result = false\n    end\n    return result\nend\n\nlocal function query_get_nodes(query, document_root, bufid, row_start_0b, row_end_0bex)\n    local result = {}\n    local concealed_node_ids = {}\n    for id, node in query:iter_captures(document_root, bufid, row_start_0b, row_end_0bex) do\n        if node:missing() then\n            goto continue\n        end\n        if query.captures[id] == \"icon-concealed\" then\n            concealed_node_ids[node:id()] = true\n        end\n        table.insert(result, node)\n        ::continue::\n    end\n    return result, concealed_node_ids\nend\n\nlocal function check_min(xy, x_new, y_new)\n    if (x_new < xy.x) or (x_new == xy.x and y_new < xy.y) then\n        xy.x = x_new\n        xy.y = y_new\n    end\nend\n\nlocal function check_max(xy, x_new, y_new)\n    if (x_new > xy.x) or (x_new == xy.x and y_new > xy.y) then\n        xy.x = x_new\n        xy.y = y_new\n    end\nend\n\nlocal function add_prettify_flag_line(bufid, row)\n    local ns_prettify_flag = module.private.ns_prettify_flag\n    vim.api.nvim_buf_set_extmark(bufid, ns_prettify_flag, row, 0, {})\nend\n\nlocal function add_prettify_flag_range(bufid, row_start_0b, row_end_0bex)\n    for row = row_start_0b, row_end_0bex - 1 do\n        add_prettify_flag_line(bufid, row)\n    end\nend\n\nlocal function remove_prettify_flag_on_line(bufid, row_0b)\n    -- TODO: optimize\n    local ns_prettify_flag = module.private.ns_prettify_flag\n    vim.api.nvim_buf_clear_namespace(bufid, ns_prettify_flag, row_0b, row_0b + 1)\nend\n\nlocal function remove_prettify_flag_range(bufid, row_start_0b, row_end_0bex)\n    -- TODO: optimize\n    local ns_prettify_flag = module.private.ns_prettify_flag\n    vim.api.nvim_buf_clear_namespace(bufid, ns_prettify_flag, row_start_0b, row_end_0bex)\nend\n\nlocal function remove_prettify_flag_all(bufid)\n    remove_prettify_flag_range(bufid, 0, -1)\nend\n\nlocal function get_visible_line_range(winid)\n    local row_start_1b = vim.fn.line(\"w0\", winid)\n    local row_end_1b = vim.fn.line(\"w$\", winid)\n    return (row_start_1b - 1), row_end_1b\nend\n\nlocal function get_parsed_query_lazy()\n    if module.private.prettify_query then\n        return module.private.prettify_query\n    end\n\n    local keys = { \"config\", \"icons\" }\n    local function traverse_config(config, f)\n        if config == false then\n            return\n        end\n        if config.nodes then\n            f(config)\n            return\n        end\n        if type(config) ~= \"table\" then\n            log.warn((\"unsupported icon config: %s = %s\"):format(table.concat(keys, \".\"), config))\n            return\n        end\n        local key_pos = #keys + 1\n        for key, sub_config in pairs(config) do\n            keys[key_pos] = key\n            traverse_config(sub_config, f)\n            keys[key_pos] = nil\n        end\n    end\n\n    local config_by_node_name = {}\n    local queries = { \"[\" }\n\n    traverse_config(module.config.public.icons, function(config)\n        for _, node_type in ipairs(config.nodes) do\n            table.insert(queries, (\"(%s)@icon\"):format(node_type))\n            config_by_node_name[node_type] = config\n        end\n        for _, node_type in ipairs(config.nodes.concealed or {}) do\n            table.insert(queries, (\"(%s)@icon-concealed\"):format(node_type))\n            config_by_node_name[node_type] = config\n        end\n    end)\n\n    table.insert(queries, \"]\")\n    local query_combined = table.concat(queries, \" \")\n    module.private.prettify_query = utils.ts_parse_query(\"norg\", query_combined)\n    assert(module.private.prettify_query)\n    module.private.config_by_node_name = config_by_node_name\n    return module.private.prettify_query\nend\n\nlocal function prettify_range(bufid, row_start_0b, row_end_0bex)\n    -- in case there's undo/removal garbage\n    -- TODO: optimize\n    row_end_0bex = math.min(row_end_0bex + 1, vim.api.nvim_buf_line_count(bufid))\n\n    local treesitter_module = module.required[\"core.integrations.treesitter\"]\n    local document_root = treesitter_module.get_document_root(bufid)\n    assert(document_root)\n\n    local nodes, concealed_node_ids =\n        query_get_nodes(get_parsed_query_lazy(), document_root, bufid, row_start_0b, row_end_0bex)\n\n    local winid = vim.fn.bufwinid(bufid)\n    assert(winid > 0)\n    local current_row_0b = vim.api.nvim_win_get_cursor(winid)[1] - 1\n    local current_mode = vim.api.nvim_get_mode().mode\n    local conceallevel = vim.wo[winid].conceallevel\n    local concealcursor = vim.wo[winid].concealcursor\n\n    assert(document_root)\n\n    for _, node in ipairs(nodes) do\n        local node_row_start_0b, node_col_start_0b, node_row_end_0bin, node_col_end_0bex = node:range()\n        local node_row_end_0bex = node_row_end_0bin + 1\n        local config = module.private.config_by_node_name[node:type()]\n\n        if config.clear then\n            config:clear(bufid, node)\n        else\n            local pos_start_0b_0b, pos_end_0bin_0bex =\n                { x = node_row_start_0b, y = node_col_start_0b }, { x = node_row_end_0bin, y = node_col_end_0bex }\n\n            check_min(pos_start_0b_0b, node:start())\n            check_max(pos_end_0bin_0bex, node:end_())\n\n            remove_extmarks(bufid, pos_start_0b_0b, pos_end_0bin_0bex)\n        end\n\n        remove_prettify_flag_range(bufid, node_row_start_0b, node_row_end_0bex)\n        add_prettify_flag_range(bufid, node_row_start_0b, node_row_end_0bex)\n\n        if should_skip_prettify(current_mode, current_row_0b, node, config, node_row_start_0b, node_row_end_0bex) then\n            goto continue\n        end\n\n        local has_conceal = (\n            concealed_node_ids[node:id()]\n            and (not config.check_conceal or config.check_conceal(node))\n            and is_concealing_on_row_range(\n                current_mode,\n                conceallevel,\n                concealcursor,\n                current_row_0b,\n                node_row_start_0b,\n                node_row_end_0bex\n            )\n        )\n\n        if has_conceal then\n            if config.render_concealed then\n                config:render_concealed(bufid, node)\n            end\n        else\n            config:render(bufid, node)\n        end\n\n        ::continue::\n    end\nend\n\nlocal function render_window_buffer(bufid)\n    local ns_prettify_flag = module.private.ns_prettify_flag\n    local winid = vim.fn.bufwinid(bufid)\n    local row_start_0b, row_end_0bex = get_visible_line_range(winid)\n    local prettify_flags_0b = vim.api.nvim_buf_get_extmarks(\n        bufid,\n        ns_prettify_flag,\n        { row_start_0b, 0 },\n        { row_end_0bex - 1, -1 },\n        {}\n    )\n    local row_nomark_start_0b, row_nomark_end_0bin\n    local i_flag = 1\n    for i = row_start_0b, row_end_0bex - 1 do\n        while i_flag <= #prettify_flags_0b and i > prettify_flags_0b[i_flag][2] do\n            i_flag = i_flag + 1\n        end\n\n        if i_flag <= #prettify_flags_0b and i == prettify_flags_0b[i_flag][2] then\n            i_flag = i_flag + 1\n        else\n            assert(i < (prettify_flags_0b[i_flag] and prettify_flags_0b[i_flag][2] or row_end_0bex))\n            row_nomark_start_0b = row_nomark_start_0b or i\n            row_nomark_end_0bin = i\n        end\n    end\n\n    assert((row_nomark_start_0b == nil) == (row_nomark_end_0bin == nil))\n    if row_nomark_start_0b then\n        prettify_range(bufid, row_nomark_start_0b, row_nomark_end_0bin + 1)\n    end\nend\n\nlocal function render_all_scheduled_and_done()\n    for bufid, _ in pairs(module.private.rerendering_scheduled_bufids) do\n        if vim.fn.bufwinid(bufid) >= 0 then\n            render_window_buffer(bufid)\n        end\n    end\n    module.private.rerendering_scheduled_bufids = {}\nend\n\nlocal function schedule_rendering(bufid)\n    local not_scheduled = vim.tbl_isempty(module.private.rerendering_scheduled_bufids)\n    module.private.rerendering_scheduled_bufids[bufid] = true\n    if not_scheduled then\n        vim.schedule(render_all_scheduled_and_done)\n    end\nend\n\nlocal function mark_line_changed(bufid, row_0b)\n    remove_prettify_flag_on_line(bufid, row_0b)\n    schedule_rendering(bufid)\nend\n\nlocal function mark_line_range_changed(bufid, row_start_0b, row_end_0bex)\n    remove_prettify_flag_range(bufid, row_start_0b, row_end_0bex)\n    schedule_rendering(bufid)\nend\n\nlocal function mark_all_lines_changed(bufid)\n    if not module.private.enabled then\n        return\n    end\n\n    remove_prettify_flag_all(bufid)\n    schedule_rendering(bufid)\nend\n\nlocal function clear_all_extmarks(bufid)\n    local ns_icon = module.private.ns_icon\n    local ns_prettify_flag = module.private.ns_prettify_flag\n    vim.api.nvim_buf_clear_namespace(bufid, ns_icon, 0, -1)\n    vim.api.nvim_buf_clear_namespace(bufid, ns_prettify_flag, 0, -1)\nend\n\nlocal function get_table_default_empty(tbl, key)\n    if not tbl[key] then\n        tbl[key] = {}\n    end\n    return tbl[key]\nend\n\nlocal function update_cursor(event)\n    local cursor_record = get_table_default_empty(module.private.cursor_record, event.buffer)\n    cursor_record.row_0b = event.cursor_position[1] - 1\n    cursor_record.col_0b = event.cursor_position[2]\n    cursor_record.line_content = event.line_content\nend\n\nlocal function handle_init_event(event)\n    assert(vim.api.nvim_win_is_valid(event.window))\n    update_cursor(event)\n\n    local function on_line_callback(\n        tag,\n        bufid,\n        _changedtick, ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n        row_start_0b,\n        _row_end_0bex, ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n        row_updated_0bex,\n        _n_byte_prev ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n    )\n        assert(tag == \"lines\")\n\n        if not module.private.enabled then\n            return\n        end\n\n        mark_line_range_changed(bufid, row_start_0b, row_updated_0bex)\n    end\n\n    local attach_succeeded = vim.api.nvim_buf_attach(event.buffer, true, { on_lines = on_line_callback })\n    assert(attach_succeeded)\n    local language_tree = vim.treesitter.get_parser(event.buffer, \"norg\")\n    if not language_tree then\n        log.error(\"Failed to get parser for language norg in buffer \" .. event.buffer)\n        return\n    end\n\n    local bufid = event.buffer\n    -- used for detecting non-local (multiline) changes, like spoiler / code block\n    -- TODO: exemption in certain cases, for example when changing only heading followed by pure texts,\n    -- in which case all its descendants would be unnecessarily re-concealed.\n    local function on_changedtree_callback(ranges)\n        -- TODO: abandon if too large\n        for i = 1, #ranges do\n            local range = ranges[i]\n            local row_start_0b = range[1]\n            local row_end_0bex = range[3] + 1\n            remove_prettify_flag_range(bufid, row_start_0b, row_end_0bex)\n        end\n    end\n\n    language_tree:register_cbs({ on_changedtree = on_changedtree_callback })\n    mark_all_lines_changed(event.buffer)\n\n    if\n        module.config.public.folds\n        and vim.api.nvim_win_is_valid(event.window)\n        and vim.api.nvim_buf_is_valid(event.buffer)\n    then\n        vim.api.nvim_buf_call(event.buffer, function()\n            -- NOTE(vhyrro): `vim.wo` only supports `wo[winid][0]`,\n            -- hence the `buf_call` here.\n            local wo = vim.wo[event.window][0]\n            wo.foldmethod = \"expr\"\n            wo.foldexpr = vim.treesitter.foldexpr and \"v:lua.vim.treesitter.foldexpr()\" or \"nvim_treesitter#foldexpr()\"\n            wo.foldtext = \"\"\n\n            local init_open_folds = module.config.public.init_open_folds\n            local function open_folds()\n                vim.cmd(\"normal! zR\")\n            end\n\n            if init_open_folds == \"always\" then\n                open_folds()\n            elseif init_open_folds == \"never\" then -- luacheck:ignore 542\n                -- do nothing\n            else\n                if init_open_folds ~= \"auto\" then\n                    log.warn('\"init_open_folds\" must be \"auto\", \"always\", or \"never\"')\n                end\n\n                if wo.foldlevel == 0 then\n                    open_folds()\n                end\n            end\n        end)\n    end\nend\n\nlocal function handle_insert_toggle(event)\n    mark_line_changed(event.buffer, event.cursor_position[1] - 1)\nend\n\nlocal function handle_insertenter(event)\n    handle_insert_toggle(event)\nend\n\nlocal function handle_insertleave(event)\n    handle_insert_toggle(event)\nend\n\nlocal function handle_toggle_prettifier(event)\n    -- FIXME: module.private.enabled should be a map from bufid to boolean\n    module.private.enabled = not module.private.enabled\n    if module.private.enabled then\n        mark_all_lines_changed(event.buffer)\n    else\n        module.private.rerendering_scheduled_bufids[event.buffer] = nil\n        clear_all_extmarks(event.buffer)\n    end\nend\n\nlocal function is_same_line_movement(event)\n    -- some operations like dd / u cannot yet be listened reliably\n    -- below is our best approximation\n    local cursor_record = module.private.cursor_record\n    return (\n        cursor_record\n        and cursor_record.row_0b == event.cursor_position[1] - 1\n        and cursor_record.col_0b ~= event.cursor_position[2]\n        and cursor_record.line_content == event.line_content\n    )\nend\n\nlocal function handle_cursor_moved(event)\n    -- reveal/conceal when conceallevel>0\n    -- also triggered when dd / u\n    if not is_same_line_movement(event) then\n        local cursor_record = module.private.cursor_record[event.buffer]\n        if cursor_record then\n            -- leaving previous line, conceal it if necessary\n            mark_line_changed(event.buffer, cursor_record.row_0b)\n        end\n        -- entering current line, conceal it if necessary\n        local current_row_0b = event.cursor_position[1] - 1\n        mark_line_changed(event.buffer, current_row_0b)\n    end\n    update_cursor(event)\nend\n\nlocal function handle_cursor_moved_i(event)\n    return handle_cursor_moved(event)\nend\n\nlocal function handle_winscrolled(event)\n    schedule_rendering(event.buffer)\nend\n\nlocal function handle_filetype(event)\n    handle_init_event(event)\nend\n\nlocal event_handlers = {\n    [\"core.neorgcmd.events.core.concealer.toggle\"] = handle_toggle_prettifier,\n    -- [\"core.autocommands.events.bufnewfile\"] = handle_init_event,\n    [\"core.autocommands.events.filetype\"] = handle_filetype,\n    [\"core.autocommands.events.bufreadpost\"] = handle_init_event,\n    [\"core.autocommands.events.insertenter\"] = handle_insertenter,\n    [\"core.autocommands.events.insertleave\"] = handle_insertleave,\n    [\"core.autocommands.events.cursormoved\"] = handle_cursor_moved,\n    [\"core.autocommands.events.cursormovedi\"] = handle_cursor_moved_i,\n    [\"core.autocommands.events.winscrolled\"] = handle_winscrolled,\n}\n\nmodule.on_event = function(event)\n    if event.referrer == \"core.autocommands\" and vim.bo[event.buffer].ft ~= \"norg\" then\n        return\n    end\n\n    if (not module.private.enabled) and (event.type ~= \"core.neorgcmd.events.core.concealer.toggle\") then\n        return\n    end\n    return event_handlers[event.type](event)\nend\n\nmodule.load = function()\n    local icon_preset =\n        module.imported[module.name .. \".preset_\" .. module.config.public.icon_preset].config.private[\"icon_preset_\" .. module.config.public.icon_preset]\n    if not icon_preset then\n        log.error(\n            (\"Unable to load icon preset '%s' - such a preset does not exist\"):format(module.config.public.icon_preset)\n        )\n        return\n    end\n\n    module.config.public =\n        vim.tbl_deep_extend(\"force\", module.config.public, { icons = icon_preset }, module.config.custom or {})\n\n    -- module.required[\"core.autocommands\"].enable_autocommand(\"BufNewFile\")\n    module.required[\"core.autocommands\"].enable_autocommand(\"FileType\", true)\n    module.required[\"core.autocommands\"].enable_autocommand(\"BufReadPost\")\n    module.required[\"core.autocommands\"].enable_autocommand(\"InsertEnter\")\n    module.required[\"core.autocommands\"].enable_autocommand(\"InsertLeave\")\n    module.required[\"core.autocommands\"].enable_autocommand(\"CursorMoved\")\n    module.required[\"core.autocommands\"].enable_autocommand(\"CursorMovedI\")\n    module.required[\"core.autocommands\"].enable_autocommand(\"WinScrolled\", true)\n\n    modules.await(\"core.neorgcmd\", function(neorgcmd)\n        neorgcmd.add_commands_from_table({\n            [\"toggle-concealer\"] = {\n                name = \"core.concealer.toggle\",\n                args = 0,\n                condition = \"norg\",\n            },\n        })\n    end)\n\n    vim.api.nvim_create_autocmd(\"OptionSet\", {\n        pattern = \"conceallevel\",\n        callback = function()\n            local bufid = vim.api.nvim_get_current_buf()\n            if vim.bo[bufid].ft ~= \"norg\" then\n                return\n            end\n            mark_all_lines_changed(bufid)\n        end,\n    })\nend\n\nmodule.events.subscribed = {\n    [\"core.autocommands\"] = {\n        -- bufnewfile = true,\n        filetype = true,\n        bufreadpost = true,\n        insertenter = true,\n        insertleave = true,\n        cursormoved = true,\n        cursormovedi = true,\n        winscrolled = true,\n    },\n\n    [\"core.neorgcmd\"] = {\n        [\"core.concealer.toggle\"] = true,\n    },\n}\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/concealer/preset_basic/module.lua",
    "content": "local neorg = require(\"neorg.core\")\nlocal modules = neorg.modules\n\nlocal module = modules.create(\"core.concealer.preset_basic\")\n\nmodule.config.private.icon_preset_basic = {}\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/concealer/preset_diamond/module.lua",
    "content": "local neorg = require(\"neorg.core\")\nlocal modules = neorg.modules\n\nlocal module = modules.create(\"core.concealer.preset_diamond\")\n\nmodule.config.private.icon_preset_diamond = {\n    heading = {\n        icons = { \"◈\", \"◇\", \"◆\", \"⋄\", \"❖\", \"⟡\" },\n    },\n\n    footnote = {\n        single = {\n            icon = \"†\",\n        },\n        multi_prefix = {\n            icon = \"‡ \",\n        },\n        multi_suffix = {\n            icon = \"‡ \",\n        },\n    },\n}\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/concealer/preset_varied/module.lua",
    "content": "local neorg = require(\"neorg.core\")\nlocal modules = neorg.modules\n\nlocal module = modules.create(\"core.concealer.preset_varied\")\n\nmodule.config.private.icon_preset_varied = {\n    heading = {\n        icons = { \"◉\", \"◆\", \"✿\", \"○\", \"▶\", \"⤷\" },\n    },\n\n    footnote = {\n        single = {\n            icon = \"\",\n        },\n        multi_prefix = {\n            icon = \" \",\n        },\n        multi_suffix = {\n            icon = \" \",\n        },\n    },\n}\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/defaults/module.lua",
    "content": "--[[\n    file: Defaults\n    summary: Metamodule for storing the most necessary modules.\n    internal: true\n    ---\nThis file contains all of the most important modules that any user would want\nto have a \"just works\" experience.\n\nIndividual entries can be disabled via the \"disable\" flag:\n```lua\nload = {\n    [\"core.defaults\"] = {\n        config = {\n            disable = {\n                -- module list goes here\n                \"core.autocommands\",\n                \"core.itero\",\n            },\n        },\n    },\n}\n```\n--]]\n\nlocal neorg = require(\"neorg.core\")\nlocal modules = neorg.modules\n\nreturn modules.create_meta(\n    \"core.defaults\",\n    \"core.autocommands\",\n    \"core.clipboard\",\n    \"core.clipboard.code-blocks\",\n    \"core.esupports.hop\",\n    \"core.esupports.indent\",\n    \"core.esupports.metagen\",\n    \"core.integrations.treesitter\",\n    \"core.itero\",\n    \"core.journal\",\n    \"core.keybinds\",\n    \"core.looking-glass\",\n    \"core.neorgcmd\",\n    \"core.pivot\",\n    \"core.promo\",\n    \"core.qol.toc\",\n    \"core.qol.todo_items\",\n    \"core.storage\",\n    \"core.tangle\",\n    \"core.tempus\",\n    \"core.todo-introspector\",\n    \"core.ui.calendar\"\n)\n"
  },
  {
    "path": "lua/neorg/modules/core/dirman/module.lua",
    "content": "--[[\n    file: Dirman\n    title: The Most Critical Component of any Organized Workflow\n    description: The `dirman` module handles different collections of notes in separate directories.\n    summary: This module is be responsible for managing directories full of .norg files.\n    ---\n`core.dirman` provides other modules the ability to see which directories the user is in, where\neach note collection is stored and how to interact with it.\n\nWhen writing notes, it is often crucial to have notes on a certain topic be isolated from notes on another topic.\nDirman achieves this with a concept of \"workspaces\", which are named directories full of `.norg` notes.\n\nTo use `core.dirman`, simply load up the module in your configuration and specify the directories you would like to be managed for you:\n\n```lua\nrequire('neorg').setup {\n    load = {\n        [\"core.defaults\"] = {},\n        [\"core.dirman\"] = {\n            config = {\n                workspaces = {\n                    my_ws = \"~/neorg\", -- Format: <name_of_workspace> = <path_to_workspace_root>\n                    my_other_notes = \"~/work/notes\",\n                },\n                index = \"index.norg\", -- The name of the main (root) .norg file\n            }\n        }\n    }\n}\n```\n\nTo query the current workspace, run `:Neorg workspace`. To set the workspace, run `:Neorg workspace <workspace_name>`.\n\n### Changing the Current Working Directory\nAfter a recent update `core.dirman` will no longer change the current working directory after switching\nworkspace. To get the best experience it's recommended to set the `autochdir` Neovim option.\n\n\n### Create a new note (in lua)\nYou can use dirman to create new notes in your workspaces.\n\n```lua\nlocal dirman = require('neorg').modules.get_module(\"core.dirman\")\ndirman.create_file(\"my_file\", \"my_ws\", {\n    no_open  = false,  -- open file after creation?\n    force    = false,  -- overwrite file if exists\n    metadata = {}      -- key-value table for metadata fields\n})\n```\n\n## Keybinds\n\nThis module exposes the following keybinds (see [`core.keybinds`](@core.keybinds) for instructions on\nmapping them):\n\n- `neorg.dirman.new-note` - Create a new note in the current workspace, prompt for name\n\n--]]\n\nlocal Path = require(\"pathlib\")\n\nlocal neorg = require(\"neorg.core\")\nlocal log, modules, utils = neorg.log, neorg.modules, neorg.utils\n---@type core.dirman.utils, core.ui\nlocal dirman_utils, ui\n\nlocal module = modules.create(\"core.dirman\")\n\nmodule.setup = function()\n    return {\n        success = true,\n        requires = { \"core.autocommands\", \"core.ui\", \"core.storage\", \"core.dirman.utils\" },\n    }\nend\n\nmodule.load = function()\n    -- Go through every workspace and expand special symbols like ~\n    for name, workspace_location in pairs(module.config.public.workspaces) do\n        -- module.config.public.workspaces[name] = vim.fn.expand(vim.fn.fnameescape(workspace_location)) ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n        module.config.public.workspaces[name] = Path(workspace_location):resolve():to_absolute()\n    end\n\n    dirman_utils = module.required[\"core.dirman.utils\"]\n    ui = module.required[\"core.ui\"]\n\n    vim.keymap.set(\"\", \"<Plug>(neorg.dirman.new-note)\", module.public.new_note)\n\n    -- Used to detect when we've entered a buffer with a potentially different cwd\n    module.required[\"core.autocommands\"].enable_autocommand(\"BufEnter\", true)\n\n    modules.await(\"core.neorgcmd\", function(neorgcmd)\n        neorgcmd.add_commands_from_table({\n            index = {\n                args = 0,\n                name = \"dirman.index\",\n            },\n        })\n    end)\n\n    -- Synchronize core.neorgcmd autocompletions\n    module.public.sync()\n\n    local default_workspace = module.public.get_default_workspace()\n    if module.config.public.open_last_workspace and vim.fn.argc(-1) == 0 then\n        if module.config.public.open_last_workspace == \"default\" then\n            if not default_workspace then\n                log.warn(\n                    'Configuration error in `core.dirman`: the `open_last_workspace` option is set to \"default\", but no default workspace is provided in the `default_workspace` configuration variable. Defaulting to opening the last known workspace.'\n                )\n                module.public.set_last_workspace()\n                return\n            end\n\n            module.public.open_workspace(default_workspace)\n        else\n            module.public.set_last_workspace()\n        end\n    elseif default_workspace then\n        module.public.set_workspace(default_workspace)\n    end\nend\n\nmodule.config.public = {\n    -- The list of active Neorg workspaces.\n    --\n    -- There is always an inbuilt workspace called `default`, whose location is\n    -- set to the Neovim current working directory on boot.\n    ---@type table<string, PathlibPath>\n    workspaces = {\n        default = require(\"pathlib\").cwd(),\n    },\n    -- The name for the index file.\n    --\n    -- The index file is the \"entry point\" for all of your notes.\n    index = \"index.norg\",\n    -- The default workspace to set whenever Neovim starts.\n    -- If a function, will be called with the current workspace and should resolve to a valid workspace name\n    default_workspace = nil,\n    -- Whether to open the last workspace's index file when `nvim` is executed\n    -- without arguments.\n    --\n    -- May also be set to the string `\"default\"`, due to which Neorg will always\n    -- open up the index file for the workspace defined in `default_workspace`.\n    open_last_workspace = false,\n    -- Whether to use core.ui.text_popup for `dirman.new.note` event.\n    -- if `false`, will use vim's default `vim.ui.input` instead.\n    use_popup = true,\n}\n\nmodule.private = {\n    ---@type { [1]: string, [2]: PathlibPath }\n    current_workspace = { \"default\", Path.cwd() },\n}\n\n---@class core.dirman\nmodule.public = {\n    ---@return table<string, PathlibPath>\n    get_workspaces = function()\n        return module.config.public.workspaces\n    end,\n    ---@return string[]\n    get_workspace_names = function()\n        return vim.tbl_keys(module.config.public.workspaces)\n    end,\n    --- If present retrieve a workspace's path by its name, else returns nil\n    ---@param name string #The name of the workspace\n    ---@return PathlibPath\n    get_workspace = function(name)\n        return module.config.public.workspaces[name]\n    end,\n    --- @return { [1]: string, [2]: PathlibPath }\n    get_current_workspace = function()\n        return module.private.current_workspace\n    end,\n    --- The default workspace, may be set dynamically based on cwd\n    ---@return string? # Should evaluate to a valid workspace name\n    get_default_workspace = function()\n        if type(module.config.public.default_workspace) == \"function\" then\n            return module.config.public.default_workspace()\n        end\n\n        return module.config.public.default_workspace\n    end,\n    --- Sets the workspace to the one specified (if it exists) and broadcasts the workspace_changed event\n    ---@param ws_name string #The name of a valid namespace we want to switch to\n    ---@return boolean #True if the workspace is set correctly, false otherwise\n    set_workspace = function(ws_name)\n        -- Grab the workspace location\n        local workspace = module.config.public.workspaces[ws_name]\n        -- Create a new object describing our new workspace\n        local new_workspace = { ws_name, workspace }\n\n        -- If the workspace does not exist then error out\n        if not workspace then\n            log.warn(\"Unable to set workspace to\", workspace, \"- that workspace does not exist\")\n            return false\n        end\n\n        -- Create the workspace directory if not already present\n        workspace:mkdir(Path.const.o755, true)\n\n        -- Cache the current workspace\n        local current_ws = vim.deepcopy(module.private.current_workspace)\n\n        -- Set the current workspace to the new workspace object we constructed\n        module.private.current_workspace = new_workspace\n\n        if ws_name ~= \"default\" then\n            module.required[\"core.storage\"].store(\"last_workspace\", ws_name)\n        end\n\n        -- Broadcast the workspace_changed event with all the necessary information\n        modules.broadcast_event(\n            assert(\n                modules.create_event(\n                    module,\n                    \"core.dirman.events.workspace_changed\",\n                    { old = current_ws, new = new_workspace }\n                )\n            )\n        )\n\n        return true\n    end,\n    --- Dynamically defines a new workspace if the name isn't already occupied and broadcasts the workspace_added event\n    ---@return boolean True if the workspace is added successfully, false otherwise\n    ---@param workspace_name string #The unique name of the new workspace\n    ---@param workspace_path string|PathlibPath #A full path to the workspace root\n    add_workspace = function(workspace_name, workspace_path)\n        -- If the module already exists then bail\n        if module.config.public.workspaces[workspace_name] then\n            return false\n        end\n\n        workspace_path = Path(workspace_path):resolve():to_absolute()\n        -- Set the new workspace and its path accordingly\n        module.config.public.workspaces[workspace_name] = workspace_path\n        -- Broadcast the workspace_added event with the newly added workspace as the content\n        modules.broadcast_event(\n            assert(\n                modules.create_event(module, \"core.dirman.events.workspace_added\", { workspace_name, workspace_path })\n            )\n        )\n\n        -- Sync autocompletions so the user can see the new workspace\n        module.public.sync()\n\n        return true\n    end,\n    --- If the file we opened is within a workspace directory, returns the name of the workspace, else returns nil\n    get_workspace_match = function()\n        -- Cache the current working directory\n        module.config.public.workspaces.default = Path.cwd()\n\n        local file = Path(vim.fn.expand(\"%:p\"))\n\n        -- Name of matching workspace. Falls back to \"default\"\n        local ws_name = \"default\"\n\n        -- Store the depth of the longest match\n        local longest_match = 0\n\n        -- Find a matching workspace\n        for workspace, location in pairs(module.config.public.workspaces) do\n            if workspace ~= \"default\" then\n                if file:is_relative_to(location) and location:depth() > longest_match then\n                    ws_name = workspace\n                    longest_match = location:depth()\n                end\n            end\n        end\n\n        return ws_name\n    end,\n    --- Uses the `get_workspace_match()` function to determine the root of the workspace based on the\n    --- current working directory, then changes into that workspace\n    set_closest_workspace_match = function()\n        -- Get the closest workspace match\n        local ws_match = module.public.get_workspace_match()\n\n        -- If that match exists then set the workspace to it!\n        if ws_match then\n            module.public.set_workspace(ws_match)\n        else\n            -- Otherwise try to reset the workspace to the default\n            module.public.set_workspace(\"default\")\n        end\n    end,\n    --- Updates completions for the :Neorg command\n    sync = function()\n        -- Get all the workspace names\n        local workspace_names = module.public.get_workspace_names()\n\n        -- Add the command to core.neorgcmd so it can be used by the user!\n        modules.await(\"core.neorgcmd\", function(neorgcmd)\n            neorgcmd.add_commands_from_table({\n                workspace = {\n                    max_args = 1,\n                    name = \"dirman.workspace\",\n                    complete = { workspace_names },\n                },\n            })\n        end)\n    end,\n\n    ---@class core.dirman.create_file_opts\n    ---@field no_open? boolean do not open the file after creation?\n    ---@field force? boolean overwrite file if it already exists?\n    ---@field metadata? core.esupports.metagen.metadata metadata fields, if provided inserts metadata - an empty table uses default values\n\n    --- Takes in a path (can include directories) and creates a .norg file from that path\n    ---@param path string|PathlibPath a path to place the .norg file in\n    ---@param workspace? string workspace name\n    ---@param opts? core.dirman.create_file_opts additional options\n    create_file = function(path, workspace, opts)\n        opts = opts or {}\n\n        -- Grab the current workspace's full path\n        local fullpath\n\n        if workspace ~= nil then\n            fullpath = module.public.get_workspace(workspace)\n        else\n            fullpath = module.public.get_current_workspace()[2]\n        end\n\n        if fullpath == nil then\n            log.error(\"Error in fetching workspace path\")\n            return\n        end\n\n        local destination = (fullpath / path):add_suffix(\".norg\")\n\n        -- Generate parents just in case\n        destination:parent_assert():mkdir(Path.const.o755 + 4 * math.pow(8, 4), true) -- 40755(oct)\n\n        -- Create or overwrite the file\n        local fd = destination:fs_open(opts.force and \"w\" or \"a\", Path.const.o644, false)\n        if fd then\n            vim.loop.fs_close(fd)\n        end\n\n        -- Broadcast file creation event\n        local bufnr = module.public.get_file_bufnr(destination:tostring())\n        modules.broadcast_event(\n            assert(modules.create_event(module, \"core.dirman.events.file_created\", { buffer = bufnr, opts = opts }))\n        )\n\n        if not opts.no_open then\n            -- Begin editing that newly created file\n            vim.cmd(\"e \" .. destination:cmd_string() .. \"| w\")\n        end\n    end,\n\n    --- Takes in a workspace name and a path for a file and opens it\n    ---@param workspace_name string #The name of the workspace to use\n    ---@param path string|PathlibPath #A path to open the file (e.g directory/filename.norg)\n    open_file = function(workspace_name, path)\n        local workspace = module.public.get_workspace(workspace_name)\n\n        if workspace == nil then\n            return\n        end\n\n        vim.cmd(\"e \" .. (workspace / path):cmd_string() .. \" | w\")\n    end,\n    --- Reads the neorg_last_workspace.txt file and loads the cached workspace from there\n    set_last_workspace = function()\n        -- Attempt to open the last workspace cache file in read-only mode\n        local storage = modules.get_module(\"core.storage\")\n\n        if not storage then\n            log.trace(\"Module `core.storage` not loaded, refusing to load last user's workspace.\")\n            return\n        end\n\n        local last_workspace = storage.retrieve(\"last_workspace\")\n        last_workspace = type(last_workspace) == \"string\" and last_workspace\n            or module.public.get_default_workspace()\n            or \"\"\n\n        local workspace_path = module.public.get_workspace(last_workspace)\n\n        if not workspace_path then\n            log.trace(\"Unable to switch to workspace '\" .. last_workspace .. \"'. The workspace does not exist.\")\n            return\n        end\n\n        -- If we were successful in switching to that workspace then begin editing that workspace's index file\n        if module.public.set_workspace(last_workspace) then\n            vim.cmd(\"e \" .. (workspace_path / module.public.get_index()):cmd_string())\n\n            utils.notify(\"Last Workspace -> \" .. workspace_path)\n        end\n    end,\n    --- Checks for file existence by supplying a full path in `filepath`\n    ---@param filepath string|PathlibPath\n    file_exists = function(filepath)\n        return Path(filepath):exists()\n    end,\n    --- Get the bufnr for a `filepath` (full path)\n    ---@param filepath string|PathlibPath\n    get_file_bufnr = function(filepath)\n        if module.public.file_exists(filepath) then\n            local uri = vim.uri_from_fname(tostring(filepath))\n            return vim.uri_to_bufnr(uri)\n        end\n    end,\n    --- Returns a list of all files relative path from a `workspace_name`\n    ---@param workspace_name string\n    ---@return PathlibPath[]|nil\n    get_norg_files = function(workspace_name)\n        local res = {}\n        local workspace = module.public.get_workspace(workspace_name)\n\n        if not workspace then\n            return\n        end\n\n        for path in workspace:fs_iterdir(true, 20) do\n            if path:is_file(true) and path:suffix() == \".norg\" then\n                table.insert(res, path)\n            end\n        end\n\n        return res\n    end,\n    --- Sets the current workspace and opens that workspace's index file\n    ---@param workspace string #The name of the workspace to open\n    open_workspace = function(workspace)\n        -- If we have, then query that workspace\n        local ws_match = module.public.get_workspace(workspace)\n\n        -- If the workspace does not exist then give the user a nice error and bail\n        if not ws_match then\n            log.error('Unable to switch to workspace - \"' .. workspace .. '\" does not exist')\n            return\n        end\n\n        -- Set the workspace to the one requested\n        module.public.set_workspace(workspace)\n\n        -- If we're switching to a workspace that isn't the default workspace then enter the index file\n        if workspace ~= \"default\" then\n            vim.cmd(\"e \" .. (ws_match / module.public.get_index()):cmd_string())\n        end\n    end,\n    --- Touches a file in workspace\n    ---@param path string|PathlibPath\n    ---@param workspace string\n    touch_file = function(path, workspace)\n        vim.validate({\n            path = { path, \"string\", \"table\" },\n            workspace = { workspace, \"string\" },\n        })\n\n        local ws_match = module.public.get_workspace(workspace)\n\n        if not workspace then\n            return false\n        end\n\n        return (ws_match / path):touch(Path.const.o644, true)\n    end,\n    get_index = function()\n        return module.config.public.index\n    end,\n    new_note = function()\n        if module.config.public.use_popup then\n            ui.create_prompt(\"NeorgNewNote\", \"New Note: \", function(text)\n                -- Create the file that the user has entered\n                module.public.create_file(text)\n            end, {\n                center_x = true,\n                center_y = true,\n            }, {\n                width = 25,\n                height = 1,\n                row = 10,\n                col = 0,\n            })\n        else\n            vim.ui.input({ prompt = \"New Note: \" }, function(text)\n                if text ~= nil and #text > 0 then\n                    module.public.create_file(text)\n                end\n            end)\n        end\n    end,\n\n    ---Is the file a part of the given workspace?\n    ---@param file PathlibPath\n    ---@param workspace_name string? workspace or current ws when nil\n    ---@return boolean\n    in_workspace = function(file, workspace_name)\n        local ws_path\n        if not workspace_name then\n            ws_path = module.private.current_workspace[2]\n        else\n            ws_path = module.public.get_workspace(workspace_name)\n        end\n        return not not file:match(\"^\" .. ws_path)\n    end,\n}\n\nmodule.on_event = function(event)\n    -- If somebody has executed the :Neorg workspace command then\n    if event.type == \"core.neorgcmd.events.dirman.workspace\" then\n        -- Have we supplied an argument?\n        if event.content[1] then\n            module.public.open_workspace(event.content[1])\n\n            vim.schedule(function()\n                local new_workspace = module.public.get_workspace(event.content[1])\n\n                if not new_workspace then\n                    return\n                end\n\n                utils.notify(\"New Workspace: \" .. event.content[1] .. \" -> \" .. new_workspace)\n            end)\n        else -- No argument supplied, simply print the current workspace\n            -- Query the current workspace\n            local current_ws = module.public.get_current_workspace()\n            -- Nicely print it. We schedule_wrap here because people with a configured logger will have this message\n            -- silenced by other trace logs\n            vim.schedule(function()\n                utils.notify(\"Current Workspace: \" .. current_ws[1] .. \" -> \" .. current_ws[2])\n            end)\n        end\n    end\n\n    -- If somebody has executed the :Neorg index command then\n    if event.type == \"core.neorgcmd.events.dirman.index\" then\n        local current_ws = module.public.get_current_workspace()\n\n        local index_path = current_ws[2] / module.public.get_index()\n\n        if vim.fn.filereadable(index_path:tostring(\"/\")) == 0 then\n            if current_ws[1] == \"default\" then\n                utils.notify(table.concat({\n                    \"Index file cannot be created in 'default' workspace to avoid confusion.\",\n                    \"If this is intentional, manually create an index file beforehand to use this command.\",\n                }, \" \"))\n                return\n            end\n            if not index_path:touch(Path.const.o644, true) then\n                utils.notify(\n                    table.concat({\n                        \"Unable to create '\",\n                        module.public.get_index(),\n                        \"' in the current workspace - are your filesystem permissions set correctly?\",\n                    }),\n                    vim.log.levels.WARN\n                )\n                return\n            end\n        end\n\n        dirman_utils.edit_file(index_path:cmd_string())\n        return\n    end\nend\n\nmodule.events.defined = {\n    workspace_changed = modules.define_event(module, \"workspace_changed\"),\n    workspace_added = modules.define_event(module, \"workspace_added\"),\n    workspace_cache_empty = modules.define_event(module, \"workspace_cache_empty\"),\n    file_created = modules.define_event(module, \"file_created\"),\n}\n\nmodule.events.subscribed = {\n    [\"core.autocommands\"] = {\n        bufenter = true,\n    },\n    [\"core.dirman\"] = {\n        workspace_changed = true,\n    },\n    [\"core.neorgcmd\"] = {\n        [\"dirman.workspace\"] = true,\n        [\"dirman.index\"] = true,\n    },\n}\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/dirman/tests.lua",
    "content": "local tests = require(\"neorg.tests\")\nlocal Path = require(\"pathlib\")\n\ndescribe(\"core.dirman tests\", function()\n    local dirman = tests\n        .neorg_with(\"core.dirman\", {\n            workspaces = {\n                test = \"./test-workspace\",\n            },\n        }).modules\n        .get_module(\"core.dirman\")\n\n    describe(\"workspace-related functions\", function()\n        it(\"properly expands workspace paths\", function()\n            assert.same(dirman.get_workspaces(), {\n                default = Path.cwd(),\n                test = Path.cwd() / \"test-workspace\",\n            })\n        end)\n\n        it(\"properly sets and retrieves workspaces\", function()\n            assert.is_true(dirman.set_workspace(\"test\"))\n\n            assert.equal(dirman.get_current_workspace()[1], \"test\")\n        end)\n\n        it(\"properly creates and writes files\", function()\n            local ws_path = (Path.cwd() / \"test-workspace\")\n\n            dirman.create_file(\"example-file\", \"test\", {\n                no_open = true,\n            })\n\n            finally(function()\n                vim.fn.delete(ws_path:tostring(), \"rf\")\n            end)\n\n            assert.equal(vim.fn.filereadable((ws_path / \"example-file.norg\"):tostring()), 1)\n        end)\n    end)\nend)\n"
  },
  {
    "path": "lua/neorg/modules/core/dirman/utils/module.lua",
    "content": "--[[\n    file: Dirman-Utils\n    summary: A set of utilities for the `core.dirman` module.\n    internal: true\n    ---\nThis internal submodule implements some basic utility functions for [`core.dirman`](@core.dirman).\nCurrently the only exposed API function is `expand_path`, which takes a path like `$name/my/location` and\nconverts `$name` into the full path of the workspace called `name`.\n--]]\n\nlocal Path = require(\"pathlib\")\n\nlocal neorg = require(\"neorg.core\")\nlocal log, modules = neorg.log, neorg.modules\n\nlocal module = neorg.modules.create(\"core.dirman.utils\")\n\n---@class core.dirman.utils\nmodule.public = {\n    ---Resolve `$<workspace>/path/to/file` and return the real path\n    ---@param path string | PathlibPath # path\n    ---@param raw_path boolean? # If true, returns resolved path, otherwise, returns resolved path and append \".norg\"\n    ---@param host_file string | PathlibPath | nil file the link resides in, if the link is relative, this file is used instead of the current file\n    ---@return PathlibPath?, boolean? # Resolved path. If path does not start with `$` or not absolute, adds relative from current file.\n    expand_pathlib = function(path, raw_path, host_file)\n        local relative = false\n        if not host_file then\n            host_file = vim.fn.expand(\"%:p\")\n        end\n        local filepath = Path(path)\n        -- Expand special chars like `$`\n        local custom_workspace_path = filepath:match(\"^%$([^/\\\\]*)[/\\\\]\")\n        if custom_workspace_path then\n            ---@type core.dirman?\n            local dirman = modules.get_module(\"core.dirman\")\n            if not dirman then\n                log.error(table.concat({\n                    \"Unable to jump to link with custom workspace: `core.dirman` is not loaded.\",\n                    \"Please load the module in order to get workspace support.\",\n                }, \" \"))\n                return\n            end\n            -- If the user has given an empty workspace name (i.e. `$/myfile`)\n            if custom_workspace_path:len() == 0 then\n                filepath = dirman.get_current_workspace()[2] / filepath:relative_to(Path(\"$\"))\n            else -- If the user provided a workspace name (i.e. `$my-workspace/myfile`)\n                local workspace = dirman.get_workspace(custom_workspace_path)\n                if not workspace then\n                    local msg = \"Unable to expand path: workspace '%s' does not exist\"\n                    log.warn(string.format(msg, custom_workspace_path))\n                    return\n                end\n                filepath = workspace / filepath:relative_to(Path(\"$\" .. custom_workspace_path))\n            end\n        elseif filepath:is_relative() then\n            relative = true\n            local this_file = Path(host_file):absolute()\n            filepath = this_file:parent_assert() / filepath\n        else\n            filepath = filepath:absolute()\n        end\n        -- requested to expand norg file\n        if not raw_path then\n            if type(path) == \"string\" and (path:sub(#path) == \"/\" or path:sub(#path) == \"\\\\\") then\n                -- if path ends with `/`, it is an invalid request!\n                log.error(table.concat({\n                    \"Norg file location cannot point to a directory.\",\n                    string.format(\"Current link points to '%s'\", path),\n                    \"which ends with a `/`.\",\n                }, \" \"))\n                return\n            end\n            filepath = filepath:add_suffix(\".norg\")\n        end\n        return filepath, relative\n    end,\n\n    ---Call attempt to edit a file, catches and suppresses the error caused by a swap file being\n    ---present. Re-raises other errors via log.error\n    ---@param path string | PathlibPath\n    edit_file = function(path)\n        local ok, err = pcall(vim.cmd.edit, tostring(path))\n        if not ok then\n            -- Vim:E325 is the swap file error, in which case, a lengthy message already shows to\n            -- the user, and we don't have to crash out of this function (which creates a long and\n            -- misleading error message).\n            if err and not err:match(\"Vim:E325\") then\n                log.error(\"Failed to edit file %s. Error:\\n%s\"):format(path, err)\n            end\n        end\n    end,\n\n    ---Resolve `$<workspace>/path/to/file` and return the real path\n    -- NOTE: Use `expand_pathlib` which returns a PathlibPath object instead.\n    ---\n    ---\\@deprecate Use `expand_pathlib` which returns a PathlibPath object instead. TODO: deprecate this <2024-03-27>\n    ---@param path string|PathlibPath # path\n    ---@param raw_path boolean? # If true, returns resolved path, otherwise, returns resolved path and append \".norg\"\n    ---@return string? # Resolved path. If path does not start with `$` or not absolute, adds relative from current file.\n    expand_path = function(path, raw_path)\n        local res = module.public.expand_pathlib(path, raw_path)\n        return res and res:tostring() or nil\n    end,\n}\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/esupports/hop/module.lua",
    "content": "--[[\n    file: Esupports-Hop\n    title: Follow Various Link Locations\n    description: `esupport.hop` handles the process of dealing with links so you don't have to\n    summary: \"Hop\" between Neorg links, following them with a single keypress.\n    ---\nThe hop module serves to provide an easy way to follow and fix broken links with a single keypress.\n\nBy default, pressing `<CR>` in normal mode under a link will attempt to follow said link.\nIf the link location is found, you will be taken to the destination - if it is not, you will be\nprompted with a set of actions that you can perform on the broken link.\n\n## Keybinds\n\nThis module exposes the following keybinds (see [`core.keybinds`](@core.keybinds) for instructions on\nmapping them):\n\n- `neorg.esupports.hop.hop-link` - Follow the link under the cursor, seeks forward\n- `neorg.esupports.hop.hop-link.vsplit` - Same, but open the link in a vertical split\n- `neorg.esupports.hop.hop-link.tab-drop` - Same as hop-link, but open the link in a new tab; if the destination is already\n                                            open in an existing tab then just navigate to that tab (check :help :drop)\n- `neorg.esupports.hop.hop-link.drop` - Same as hop-link, but navigate to the buffer if the destination is already open\n                                        in an existing buffer (check :help :drop)\n--]]\n\nlocal neorg = require(\"neorg.core\")\nlocal config, lib, log, modules, utils = neorg.config, neorg.lib, neorg.log, neorg.modules, neorg.utils\n\nlocal module = modules.create(\"core.esupports.hop\")\n\n---@type core.ui, core.integrations.treesitter, core.links, core.dirman.utils\nlocal ui, ts, links, dirman_utils\n\nmodule.setup = function()\n    return {\n        success = true,\n        requires = {\n            \"core.integrations.treesitter\",\n            \"core.ui\",\n            \"core.dirman.utils\",\n            \"core.links\",\n        },\n    }\nend\n\nmodule.load = function()\n    ui = module.required[\"core.ui\"]\n    links = module.required[\"core.links\"]\n    dirman_utils = module.required[\"core.dirman.utils\"]\n    ts = module.required[\"core.integrations.treesitter\"]\n    vim.keymap.set(\"\", \"<Plug>(neorg.esupports.hop.hop-link)\", module.public.hop_link)\n    vim.keymap.set(\"\", \"<Plug>(neorg.esupports.hop.hop-link.vsplit)\", lib.wrap(module.public.hop_link, \"vsplit\"))\n    vim.keymap.set(\"\", \"<Plug>(neorg.esupports.hop.hop-link.drop)\", lib.wrap(module.public.hop_link, \"drop\"))\n    vim.keymap.set(\"\", \"<Plug>(neorg.esupports.hop.hop-link.tab-drop)\", lib.wrap(module.public.hop_link, \"tab-drop\"))\nend\n\nmodule.config.public = {\n    -- If true, will attempt to find a link further than your cursor on the current line,\n    -- even if your cursor is not over the link itself.\n    lookahead = true,\n\n    -- This value determines the strictness of fuzzy matching when trying to fix a link.\n    -- Zero means only exact matches will be found, and higher values mean more lenience.\n    --\n    -- `0.5` is the optimal default value, and it is recommended to keep this option as-is.\n    fuzzing_threshold = 0.5,\n\n    -- List of strings specifying which filetypes to open in an external application,\n    -- should the user want to open a link to such a file.\n    external_filetypes = {},\n}\n\nlocal function xy_le(x0, y0, x1, y1)\n    return x0 < x1 or (x0 == x1 and y0 <= y1)\nend\n\nlocal function range_contains(r_out, r_in)\n    return xy_le(r_out.row_start, r_out.column_start, r_in.row_start, r_in.column_start)\n        and xy_le(r_in.row_end, r_in.column_end, r_out.row_end, r_out.column_end)\nend\n\n---@alias LinkType\n---|\"url\"\n---|\"generic\"\n---|\"external_file\"\n---|\"definition\"\n---|\"timestamp\"\n---|\"footnote\"\n---|\"heading1\"\n---|\"heading2\"\n---|\"heading3\"\n---|\"heading4\"\n---|\"heading5\"\n---|\"heading6\"\n---|\"line_number\"\n---|\"wiki\"\n\n---@class (exact) Link\n---@field link_node TSNode The treesitter node of the link.\n---@field link_file_text string? A provided path, if any.\n---@field link_type LinkType? The type of link that was provided.\n---@field link_location_text string? The target title/URL of the link.\n---@field link_description string? The description of the link, if provided.\n\n---@alias LinkTargetType\n--- |\"buffer\"\n--- |\"external_app\"\n--- |\"external_file\"\n--- |\"wiki\"\n--- |\"calendar\"\n\n---@class LinkTarget\n---@field original_title string The title of the link that points to this target.\n---@field node TSNode The node of the target.\n---@field type LinkTargetType The type of target that was located.\n---@field buffer number The buffer ID in which the target was found.\n---@field uri? string For external links\n---@field line? number Link number\n---@field date? osdate For calendar links\n---@field path? string | PathlibPath\n\n---@class (exact) PotentialLinkFixes\n---@field similarity number The similarity of this candidate to the current title of the link.\n---@field text string The title of the candidate link title (will replace the existing link target).\n---@field node TSNode The node the fixed link points to.\n\n---@class core.esupports.hop\nmodule.public = {\n    --- Follow link from a specific node\n    ---@param node TSNode\n    ---@param open_mode string? if not nil, will open a new split with the split mode defined (vsplitr...) or new tab (mode=\"tab\") or with external app (mode=\"external\")\n    ---@param parsed_link Link A table of link information gathered from parse_link()\n    follow_link = function(node, open_mode, parsed_link)\n        if node:type() == \"anchor_declaration\" then\n            local located_anchor_declaration = module.public.locate_anchor_declaration_target(node)\n\n            if not located_anchor_declaration then\n                return\n            end\n\n            local range = ts.get_node_range(located_anchor_declaration.node)\n\n            vim.cmd([[normal! m`]])\n            vim.api.nvim_win_set_cursor(0, { range.row_start + 1, range.column_start })\n            return\n        end\n\n        if not parsed_link then\n            log.warn(\"Please parse your link before calling this function.\")\n            return\n        end\n\n        local located_link_information = module.public.locate_link_target(parsed_link)\n\n        local function os_open_link(link_location)\n            local o = {}\n            if config.os_info == \"windows\" then\n                o.command = \"rundll32.exe\"\n                o.args = { \"url.dll,FileProtocolHandler\", link_location }\n            else\n                o.args = { link_location }\n                if config.os_info == \"linux\" then\n                    o.command = \"xdg-open\"\n                elseif config.os_info == \"mac\" then\n                    o.command = \"open\"\n                elseif config.os_info == \"wsl2\" then\n                    o.command = \"wslview\"\n                    -- The file uri should be decoded when being transformed to a unix path.\n                    -- The decoding step is temporarily missing from wslview (https://github.com/wslutilities/wslu/issues/295),\n                    -- so we work around the problem by doing the transformation before invoking wslview.\n                    o.args[1] = vim.uri_to_fname(link_location)\n                elseif config.os_info == \"wsl\" then\n                    o.command = \"explorer.exe\"\n                end\n            end\n\n            require(\"plenary.job\"):new(o):start()\n        end\n\n        local function open_split()\n            if open_mode then\n                if open_mode == \"vsplit\" then\n                    vim.cmd(\"vsplit\")\n                elseif open_mode == \"split\" then\n                    vim.cmd(\"split\")\n                elseif open_mode == \"tab\" then\n                    vim.cmd(\"tabnew\")\n                end\n            end\n        end\n\n        local function jump_to_line(line)\n            local status, _ = pcall(vim.api.nvim_win_set_cursor, 0, { line, 0 })\n\n            if not status then\n                log.error(\"Failed to jump to line:\", line, \"- make sure the line number exists!\")\n            end\n        end\n\n        if located_link_information then\n            if open_mode == \"external\" then\n                os_open_link(located_link_information.uri or located_link_information.path)\n                return\n            end\n\n            lib.match(located_link_information.type)({\n                -- Filter the currently unsupported link types and let the user know that they do not work\n                wiki = function()\n                    vim.notify(\n                        \"Neorg doesn't support wiki links yet, please use a more specific link type instead.\",\n                        vim.log.levels.WARN\n                    )\n                end,\n\n                -- If we're dealing with a URI, simply open the URI in the user's preferred method\n                external_app = function()\n                    os_open_link(located_link_information.uri)\n                end,\n\n                -- If we're dealing with an external file, open it up in another Neovim buffer (unless otherwise applicable)\n                external_file = function()\n                    open_split()\n\n                    dirman_utils.edit_file(located_link_information.path)\n\n                    if located_link_information.line then\n                        jump_to_line(located_link_information.line)\n                    end\n                end,\n\n                buffer = function()\n                    if open_mode ~= \"tab-drop\" and open_mode ~= \"drop\" then\n                        open_split()\n                    end\n\n                    if located_link_information.buffer ~= vim.api.nvim_get_current_buf() then\n                        if open_mode == \"tab-drop\" then\n                            vim.cmd(\"tab drop \" .. vim.api.nvim_buf_get_name(located_link_information.buffer))\n                        elseif open_mode == \"drop\" then\n                            vim.cmd(\"drop \" .. vim.api.nvim_buf_get_name(located_link_information.buffer))\n                        else\n                            vim.api.nvim_set_option_value(\"buflisted\", true, { buf = located_link_information.buffer })\n                            vim.api.nvim_set_current_buf(located_link_information.buffer)\n                        end\n                    end\n\n                    if located_link_information.line then\n                        jump_to_line(located_link_information.line)\n                        return\n                    end\n\n                    if located_link_information.node then\n                        local range = ts.get_node_range(located_link_information.node)\n\n                        vim.cmd([[normal! m`]])\n                        vim.api.nvim_win_set_cursor(0, { range.row_start + 1, range.column_start })\n                        return\n                    end\n                end,\n\n                calendar = function()\n                    local calendar = modules.get_module(\"core.ui.calendar\")\n                    if not calendar then\n                        log.error(\"`core.ui.calendar` is not loaded! Unable to open timestamp.\")\n                        return\n                    end\n\n                    local tempus = modules.get_module(\"core.tempus\")\n                    if not tempus then\n                        log.error(\"`core.tempus` is not loaded! Unable to parse timestamp.\")\n                        return\n                    end\n\n                    local buffer = vim.api.nvim_get_current_buf()\n                    calendar.select_date({\n                        date = located_link_information.date,\n                        callback = function(input)\n                            local start_row, start_col, end_row, end_col = located_link_information.node:range()\n                            vim.api.nvim_buf_set_text(\n                                buffer,\n                                start_row,\n                                start_col,\n                                end_row,\n                                end_col,\n                                { \"{@ \" .. tostring(tempus.to_date(input, false)) .. \"}\" }\n                            )\n                        end,\n                    })\n                end,\n            })\n            return\n        end\n\n        local link_not_found_buf = ui.create_split(\"link-not-found\")\n        if link_not_found_buf == nil then\n            return\n        end\n\n        local selection = ui.begin_selection(link_not_found_buf)\n            :listener({\n                \"<Esc>\",\n            }, function(self)\n                self:destroy()\n            end)\n            :apply({\n                warning = function(self, text)\n                    return self:text(\"WARNING: \" .. text, \"@text.warning\")\n                end,\n                desc = function(self, text)\n                    return self:text(text, \"@comment\")\n                end,\n            })\n\n        selection\n            :title(\"Link not found - what do we do now?\")\n            :blank()\n            :text(\"There are a few actions that you can perform whenever a link cannot be located.\", \"Normal\")\n            :text(\"Press one of the available keys to perform your desired action.\")\n            :blank()\n            :desc(\"The most common action will be to try and fix the link.\")\n            :desc(\"Fixing the link will perform a fuzzy search on every item of the same type in the file\")\n            :desc(\"and make the link point to the closest match:\")\n            :flag(\"f\", \"Attempt to fix the link\", function()\n                local similarities = module.private.fix_link_strict(parsed_link)\n\n                if not similarities or vim.tbl_isempty(similarities) then\n                    return\n                end\n\n                module.private.write_fixed_link(node, parsed_link, similarities) ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n            end)\n            :blank()\n            :desc(\"Does the same as the above keybind, however doesn't limit matches to those\")\n            :desc(\"defined by the link type. This means that even if the link points to a level 1\")\n            :desc(\"heading this fixing algorithm will be able to match any other item type:\")\n            :flag(\"F\", \"Attempt to fix the link (loose fuzzing)\", function()\n                local similarities = module.private.fix_link_loose(parsed_link)\n\n                if not similarities or vim.tbl_isempty(similarities) then\n                    return\n                end\n\n                module.private.write_fixed_link(node, parsed_link, similarities, true)\n            end)\n            :blank()\n            :warning(\"The below flags currently do not work, this is a beta build.\")\n            :desc(\"Instead of fixing the link you may actually want to create the target:\")\n            :flag(\"a\", \"Place target above current link parent\")\n            :flag(\"b\", \"Place target below current link parent\")\n    end,\n\n    --- Locate a `link` or `anchor` node under the cursor\n    ---@return TSNode? #A `link` or `anchor` node if present under the cursor, else `nil`\n    extract_link_node = function()\n        local current_node = vim.treesitter.get_node()\n        if not current_node then\n            return\n        end\n        local found_node = ts.find_parent(current_node, { \"link\", \"anchor_declaration\", \"anchor_definition\" })\n\n        if not found_node then\n            found_node = (module.config.public.lookahead and module.public.lookahead_link_node())\n        end\n\n        return found_node\n    end,\n\n    --- Attempts to locate a `link` or `anchor` node after the cursor on the same line\n    ---@return TSNode? #A `link` or `anchor` node if present on the current line, else `nil`\n    lookahead_link_node = function()\n        local line = vim.api.nvim_get_current_line()\n        local current_cursor_pos = vim.api.nvim_win_get_cursor(0)\n        local current_line = current_cursor_pos[1]\n        local index = current_cursor_pos[2]\n        local resulting_node\n\n        while not resulting_node do\n            local next_square_bracket = line:find(\"%[\", index)\n            local next_curly_bracket = line:find(\"{\", index)\n            local smaller_value\n\n            if not next_square_bracket and not next_curly_bracket then\n                return\n            elseif not next_square_bracket and next_curly_bracket then\n                smaller_value = next_curly_bracket\n            elseif next_square_bracket and not next_curly_bracket then\n                smaller_value = next_square_bracket\n            else\n                smaller_value = (next_square_bracket < next_curly_bracket and next_square_bracket or next_curly_bracket)\n            end\n\n            vim.api.nvim_win_set_cursor(0, {\n                current_line,\n                smaller_value - 1,\n            })\n\n            local node_under_cursor = vim.treesitter.get_node()\n            if not node_under_cursor then\n                return\n            end\n\n            if vim.tbl_contains({ \"link_location\", \"link_description\" }, node_under_cursor:type()) then\n                resulting_node = node_under_cursor:parent()\n            end\n\n            index = index + 1\n        end\n\n        return resulting_node\n    end,\n\n    --- Locates the node that an anchor is pointing to\n    ---@param anchor_decl_node TSNode #A valid anchor declaration node\n    ---@return LinkTarget? #The target of the link if it was found.\n    locate_anchor_declaration_target = function(anchor_decl_node)\n        if not anchor_decl_node:named_child(0) then\n            return\n        end\n\n        local target = ts.get_node_text(anchor_decl_node:named_child(0):named_child(0)):gsub(\"[%s\\\\]\", \"\")\n\n        local query_str = [[\n            (anchor_definition\n                (link_description\n                    text: (paragraph) @text\n                )\n            )\n        ]]\n\n        local document_root = ts.get_document_root()\n\n        if not document_root then\n            return\n        end\n\n        local query = utils.ts_parse_query(\"norg\", query_str)\n\n        for id, node in query:iter_captures(document_root, 0) do\n            local capture = query.captures[id]\n\n            if capture == \"text\" then\n                local original_title = ts.get_node_text(node)\n                local title = original_title:gsub(\"[%s\\\\]\", \"\")\n\n                if title:lower() == target:lower() then\n                    return {\n                        original_title = original_title,\n                        node = node,\n                    }\n                end\n            end\n        end\n    end,\n\n    --- Converts a link node into a table of data related to the link\n    ---@param link_node TSNode #The link node that was found by e.g. `extract_link_node()`\n    ---@param buf number? #The buffer to parse the link in\n    ---@return Link? #A table of data about the link\n    parse_link = function(link_node, buf)\n        buf = buf or 0\n\n        if not link_node or not vim.tbl_contains({ \"link\", \"anchor_definition\" }, link_node:type()) then\n            return\n        end\n\n        local query_text = [[\n            [\n                (link\n                    (link_location\n                        file: (\n                            (link_file_text) @link_file_text\n                        )?\n                        type: [\n                            (link_target_url)\n                            (link_target_generic)\n                            (link_target_external_file)\n                            (link_target_definition)\n                            (link_target_timestamp)\n                            (link_target_footnote)\n                            (link_target_heading1)\n                            (link_target_heading2)\n                            (link_target_heading3)\n                            (link_target_heading4)\n                            (link_target_heading5)\n                            (link_target_heading6)\n                            (link_target_line_number)\n                            (link_target_wiki)\n                        ]? @link_type\n                        text: (paragraph)? @link_location_text\n                    )\n                    (link_description\n                        text: (paragraph) @link_description\n                    )?\n                )\n                (anchor_definition\n                    (link_description\n                        text: (paragraph) @link_description\n                    )\n                    (link_location\n                        file: (\n                            (link_file_text) @link_file_text\n                        )?\n                        type: [\n                            (link_target_url)\n                            (link_target_generic)\n                            (link_target_external_file)\n                            (link_target_definition)\n                            (link_target_timestamp)\n                            (link_target_footnote)\n                            (link_target_heading1)\n                            (link_target_heading2)\n                            (link_target_heading3)\n                            (link_target_heading4)\n                            (link_target_heading5)\n                            (link_target_heading6)\n                            (link_target_wiki)\n                        ]? @link_type\n                        text: (paragraph)? @link_location_text\n                    )\n                )\n            ]\n        ]]\n\n        ---@type TSNode?\n        local document_root = ts.get_document_root(buf)\n\n        if not document_root then\n            return\n        end\n\n        ---@type vim.treesitter.Query\n        local query = utils.ts_parse_query(\"norg\", query_text)\n        local range = ts.get_node_range(link_node)\n\n        ---@type Link\n        local parsed_link_information = {\n            link_node = link_node,\n        }\n\n        for id, node in query:iter_captures(document_root, buf, range.row_start, range.row_end + 1) do\n            local capture = query.captures[id]\n\n            local capture_node_range = ts.get_node_range(node)\n\n            -- Check whether the node captured node is in bounds.\n            -- There are certain rare cases where incorrect nodes would be parsed.\n            if range_contains(range, capture_node_range) then\n                local extract_node_text = lib.wrap(ts.get_node_text, node)\n\n                parsed_link_information[capture] = parsed_link_information[capture]\n                    or lib.match(capture)({\n                        link_file_text = extract_node_text,\n                        link_type = lib.wrap(string.sub, node:type(), string.len(\"link_target_\") + 1),\n                        link_location_text = extract_node_text,\n                        link_description = extract_node_text,\n\n                        _ = function()\n                            log.error(\"Unknown capture type encountered when parsing link:\", capture)\n                        end,\n                    })\n            end\n        end\n\n        return parsed_link_information\n    end,\n\n    --- Locate the target that a link points to\n    ---@param parsed_link_information Link #A table returned by `parse_link()`\n    ---@return LinkTarget #A table containing data about the link target\n    locate_link_target = function(parsed_link_information)\n        --- A pointer to the target buffer we will be parsing.\n        -- This may change depending on the target file the user gave.\n        local buf_pointer = vim.api.nvim_get_current_buf()\n\n        -- Check whether our target is from a different file\n        if parsed_link_information.link_file_text then\n            local expanded_link_text = dirman_utils.expand_path(parsed_link_information.link_file_text)\n\n            if expanded_link_text ~= vim.fn.expand(\"%:p\") then\n                -- We are dealing with a foreign file\n                buf_pointer = vim.uri_to_bufnr(\"file://\" .. expanded_link_text)\n            end\n\n            if not parsed_link_information.link_type then\n                return {\n                    type = \"buffer\",\n                    original_title = nil,\n                    node = nil,\n                    buffer = buf_pointer,\n                }\n            end\n        end\n\n        return lib.match(parsed_link_information.link_type)({\n            -- Wiki links are currently unsupported, so we simply forward the link type\n            wiki = function()\n                return { type = \"wiki\" }\n            end,\n\n            url = function()\n                return { type = \"external_app\", uri = parsed_link_information.link_location_text }\n            end,\n\n            external_file = function()\n                -- There has to be a link location present for a link to be recognized as an external file,\n                -- therefore we can safely assert here.\n                local destination = assert(parsed_link_information.link_location_text)\n                local path, line = string.match(destination, \"^(.*):(%d+)$\")\n                if line then\n                    destination = path\n                    line = tonumber(line)\n                end\n                destination = (\n                    vim.tbl_contains({ \"/\", \"~\" }, destination:sub(1, 1)) and \"\" or (vim.fn.expand(\"%:p:h\") .. \"/\")\n                ) .. destination\n\n                return lib.match(vim.fn.fnamemodify(destination, \":e\"))({\n                    [{ \"jpg\", \"jpeg\", \"png\", \"pdf\" }] = {\n                        type = \"external_app\",\n                        uri = vim.uri_from_fname(vim.fn.expand(destination)),\n                    },\n                    [module.config.public.external_filetypes] = {\n                        type = \"external_app\",\n                        uri = vim.uri_from_fname(vim.fn.expand(destination)),\n                    },\n                    _ = function()\n                        return {\n                            type = \"external_file\",\n                            path = vim.fn.fnamemodify(destination, \":p\"),\n                            line = line,\n                        }\n                    end,\n                })\n            end,\n\n            line_number = function()\n                return {\n                    type = \"buffer\",\n                    buffer = buf_pointer,\n                    line = tonumber(parsed_link_information.link_location_text),\n                }\n            end,\n\n            timestamp = function()\n                local tempus = modules.get_module(\"core.tempus\")\n\n                if not tempus then\n                    log.error(\"`core.tempus` is not loaded! Unable to parse timestamp.\")\n                    return {}\n                end\n\n                local parsed_date = tempus.parse_date(parsed_link_information.link_location_text)\n\n                if type(parsed_date) == \"string\" then\n                    log.error(\"[ERROR]:\", parsed_date)\n                    return {}\n                end\n\n                return {\n                    type = \"calendar\",\n                    date = tempus.to_lua_date(parsed_date),\n                    node = parsed_link_information.link_node,\n                }\n            end,\n\n            _ = function()\n                local query_str = links.get_link_target_query_string(parsed_link_information.link_type)\n                local document_root = ts.get_document_root(buf_pointer)\n\n                if not document_root then\n                    return\n                end\n\n                local query = utils.ts_parse_query(\"norg\", query_str)\n\n                for id, node in query:iter_captures(document_root, buf_pointer) do\n                    local capture = query.captures[id]\n\n                    if capture == \"title\" then\n                        local original_title = ts.get_node_text(node, buf_pointer)\n\n                        if original_title then\n                            local title = original_title:gsub(\"[%s\\\\]\", \"\")\n                            local target = parsed_link_information.link_location_text:gsub(\"[%s\\\\]\", \"\")\n\n                            if title:lower() == target:lower() then\n                                return {\n                                    type = \"buffer\",\n                                    original_title = original_title,\n                                    node = node,\n                                    buffer = buf_pointer,\n                                }\n                            end\n                        end\n                    end\n                end\n            end,\n        } --[[@as table<string, fun(): LinkTarget?>]])\n    end,\n\n    hop_link = function(split_mode)\n        local link_node_at_cursor = module.public.extract_link_node()\n\n        if not link_node_at_cursor then\n            log.trace(\"No link under cursor.\")\n            return\n        end\n\n        local parsed_link = module.public.parse_link(link_node_at_cursor)\n        if not parsed_link then\n            log.trace(\"Failed to parse link\", vim.inspect(link_node_at_cursor))\n            return\n        end\n\n        module.public.follow_link(link_node_at_cursor, split_mode, parsed_link)\n    end,\n}\n\nmodule.private = {\n    --- Damerau-levenstein implementation\n    calculate_similarity = function(lhs, rhs)\n        -- https://en.wikipedia.org/wiki/Damerau%E2%80%93Levenshtein_distance\n        local str1 = lhs\n        local str2 = rhs\n        local matrix = {}\n        local cost\n\n        -- build matrix\n        for i = 0, #str1 do\n            matrix[i] = {}\n            matrix[i][0] = i\n        end\n\n        for j = 0, #str2 do\n            matrix[0][j] = j\n        end\n\n        for j = 1, #str2 do\n            for i = 1, #str1 do\n                if str1:sub(i, i) == str2:sub(j, j) then\n                    cost = 0\n                else\n                    cost = 1\n                end\n                matrix[i][j] = math.min(matrix[i - 1][j] + 1, matrix[i][j - 1] + 1, matrix[i - 1][j - 1] + cost)\n                if\n                    i > 1\n                    and j > 1\n                    and str1:sub(i, i) == str2:sub(j - 1, j - 1)\n                    and str1:sub(i - 1, i - 1) == str2:sub(j, j)\n                then\n                    matrix[i][j] = math.min(matrix[i][j], matrix[i - 2][j - 2] + cost)\n                end\n            end\n        end\n\n        return matrix[#str1][#str2]\n            / (\n                (#str1 + #str2)\n                + (function()\n                    local index = 1\n                    local ret = 0\n\n                    while index < #str1 do\n                        if str1:sub(index, index):lower() == str2:sub(index, index):lower() then\n                            ret = ret + 0.2\n                        end\n\n                        index = index + 1\n                    end\n\n                    return ret\n                end)()\n            )\n    end,\n\n    --- Fuzzy fixes a link with a loose type checking query\n    ---@param parsed_link_information Link #A table as returned by `parse_link()`\n    ---@return PotentialLinkFixes[]? #A table of similarities (fuzzed items)\n    fix_link_loose = function(parsed_link_information)\n        local generic_query = [[\n            [(_\n              [(strong_carryover_set\n                 (strong_carryover\n                   name: (tag_name) @tag_name\n                   (tag_parameters) @title\n                   (#eq? @tag_name \"name\")))\n               (weak_carryover_set\n                 (weak_carryover\n                   name: (tag_name) @tag_name\n                   (tag_parameters) @title\n                   (#eq? @tag_name \"name\")))]?\n               title: (paragraph_segment) @title)\n             (inline_link_target\n               (paragraph) @title)]\n        ]]\n\n        return module.private.fix_link(parsed_link_information, generic_query)\n    end,\n\n    --- Fuzzy fixes a link with a strict type checking query\n    ---@param parsed_link_information Link #A table as returned by `parse_link()`\n    ---@return PotentialLinkFixes[]? #A table of similarities (fuzzed items)\n    fix_link_strict = function(parsed_link_information)\n        local query = lib.match(parsed_link_information.link_type)({\n            generic = [[\n                [(_\n                  [(strong_carryover_set\n                     (strong_carryover\n                       name: (tag_name) @tag_name\n                       (tag_parameters) @title\n                       (#eq? @tag_name \"name\")))\n                   (weak_carryover_set\n                     (weak_carryover\n                       name: (tag_name) @tag_name\n                       (tag_parameters) @title\n                       (#eq? @tag_name \"name\")))]?\n                   title: (paragraph_segment) @title)\n                 (inline_link_target\n                   (paragraph) @title)]\n            ]],\n            [{ \"definition\", \"footnote\" }] = string.format(\n                [[\n                (%s_list\n                    (strong_carryover_set\n                          (strong_carryover\n                            name: (tag_name) @tag_name\n                            (tag_parameters) @title\n                            (#eq? @tag_name \"name\")))?\n                    .\n                    [(single_%s\n                       (weak_carryover_set\n                          (weak_carryover\n                            name: (tag_name) @tag_name\n                            (tag_parameters) @title\n                            (#eq? @tag_name \"name\")))?\n                       (single_%s_prefix)\n                       title: (paragraph_segment) @title)\n                     (multi_%s\n                       (weak_carryover_set\n                          (weak_carryover\n                            name: (tag_name) @tag_name\n                            (tag_parameters) @title\n                            (#eq? @tag_name \"name\")))?\n                        (multi_%s_prefix)\n                          title: (paragraph_segment) @title)])\n            ]],\n                lib.reparg(parsed_link_information.link_type, 5)\n            ),\n            _ = string.format(\n                [[\n                    (%s\n                       [(strong_carryover_set\n                          (strong_carryover\n                            name: (tag_name) @tag_name\n                            (tag_parameters) @title\n                            (#eq? @tag_name \"name\")))\n                        (weak_carryover_set\n                          (weak_carryover\n                            name: (tag_name) @tag_name\n                            (tag_parameters) @title\n                            (#eq? @tag_name \"name\")))]?\n                        (%s_prefix)\n                        title: (paragraph_segment) @title)\n                ]],\n                lib.reparg(parsed_link_information.link_type, 2)\n            ),\n        })\n\n        return module.private.fix_link(parsed_link_information, query)\n    end,\n\n    --- Query all similar targets that a link could be pointing to\n    ---@param parsed_link_information table #A table as returned by `parse_link()`\n    ---@param query_str string #The query to be used during the search\n    ---@return PotentialLinkFixes[]? #A table of similarities (fuzzed items)\n    fix_link = function(parsed_link_information, query_str)\n        local buffer = vim.api.nvim_get_current_buf()\n\n        if parsed_link_information.link_file_text then\n            local expanded_link_text = dirman_utils.expand_path(parsed_link_information.link_file_text)\n\n            if expanded_link_text ~= vim.fn.expand(\"%:p\") then\n                -- We are dealing with a foreign file\n                buffer = vim.uri_to_bufnr(\"file://\" .. expanded_link_text)\n            end\n        end\n\n        local query = utils.ts_parse_query(\"norg\", query_str)\n\n        local document_root = ts.get_document_root(buffer)\n\n        if not document_root then\n            return\n        end\n\n        ---@type PotentialLinkFixes[]\n        local similarities = {}\n\n        for id, node in query:iter_captures(document_root, buffer) do\n            local capture_name = query.captures[id]\n\n            if capture_name == \"title\" then\n                local text = ts.get_node_text(node, buffer)\n                local similarity = module.private.calculate_similarity(parsed_link_information.link_location_text, text)\n\n                -- If our match is similar enough then add it to the list\n                if similarity < module.config.public.fuzzing_threshold then\n                    table.insert(similarities, { similarity = similarity, text = text, node = node:parent() })\n                end\n            end\n        end\n\n        if vim.tbl_isempty(similarities) then\n            utils.notify(\"Sorry, Neorg couldn't fix that link.\", vim.log.levels.WARN)\n        end\n\n        table.sort(similarities, function(lhs, rhs)\n            return lhs.similarity < rhs.similarity\n        end)\n\n        return similarities\n    end,\n\n    --- Writes a link that was fixed through fuzzing into the buffer\n    ---@param link_node TSNode #The treesitter node of the link, extracted by e.g. `extract_link_node()`\n    ---@param parsed_link_information Link #A table as returned by `parse_link()`\n    ---@param similarities PotentialLinkFixes[] #The table of similarities as returned by `fix_link_*()`\n    ---@param force_type boolean #If true will forcefully overwrite the link type to the target type as well (e.g. would convert `#` -> `*`)\n    write_fixed_link = function(link_node, parsed_link_information, similarities, force_type)\n        local most_similar = similarities[1]\n\n        if not link_node or not most_similar then\n            return\n        end\n\n        local range = ts.get_node_range(link_node)\n\n        local prefix = lib.when(\n            parsed_link_information.link_type == \"generic\" and not force_type,\n            \"#\",\n            lib.match(most_similar.node:type())({\n                heading1 = \"*\",\n                heading2 = \"**\",\n                heading3 = \"***\",\n                heading4 = \"****\",\n                heading5 = \"*****\",\n                heading6 = \"******\",\n                single_definition = \"$\",\n                multi_definition = \"$\",\n                single_footnote = \"^\",\n                multi_footnote = \"^\",\n                _ = \"#\",\n            })\n        ) .. \" \"\n\n        local function callback(replace)\n            vim.api.nvim_buf_set_text(\n                0,\n                range.row_start,\n                range.column_start,\n                range.row_end,\n                range.column_end,\n                { replace }\n            )\n        end\n        callback(\n            \"{\"\n                .. lib.when(\n                    parsed_link_information.link_file_text --[[@as boolean]],\n                    lib.lazy_string_concat(\":\", parsed_link_information.link_file_text, \":\"),\n                    \"\"\n                )\n                .. prefix\n                .. most_similar.text\n                .. \"}\"\n                .. lib.when(\n                    parsed_link_information.link_description --[[@as boolean]],\n                    lib.lazy_string_concat(\"[\", parsed_link_information.link_description, \"]\"),\n                    \"\"\n                )\n        )\n    end,\n}\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/esupports/indent/module.lua",
    "content": "--[[\n    file: Indent\n    title: Formatting on the Fly\n    summary: A set of instructions for Neovim to indent Neorg documents.\n    ---\n`core.esupports.indent` uses Norg's format to unambiguously determine\nthe indentation level for the current line.\n\nThe indent calculation is aided by [treesitter](@core.integrations.treesitter), which\nmeans that the quality of indents is \"limited\" by the quality of the produced syntax tree,\nwhich will get better and better with time.\n\nTo reindent a file, you may use the inbuilt Neovim `=` operator.\nIndent levels are also calculated as you type, but may not be entirely correct\ndue to incomplete syntax trees (if you find any such examples, then file an issue!).\n\nIt is also noteworthy that indents add the indentation level to the beginning of the line\nand doesn't carry on the indentation level from the previous heading, meaning that if both heading1\nand heading2 have an indentation level of 4, heading2 will not be indented an additional 4 spaces from heading1.\n--]]\n\nlocal neorg = require(\"neorg.core\")\nlocal lib, modules = neorg.lib, neorg.modules\n\nlocal module = modules.create(\"core.esupports.indent\")\n\n---@type core.integrations.treesitter\nlocal ts\n\nmodule.setup = function()\n    return {\n        requires = {\n            \"core.integrations.treesitter\",\n            \"core.autocommands\",\n        },\n    }\nend\n\n---@class core.esupports.indent\nmodule.public = {\n    indentexpr = function(buf, line, node)\n        line = line or (vim.v.lnum - 1)\n        node = node or ts.get_first_node_on_line(buf, line)\n\n        if not node then\n            return 0\n        end\n\n        local indent_data = module.config.public.indents[node:type()] or module.config.public.indents._\n\n        if not indent_data then\n            return 0\n        end\n\n        local _, initial_indent = node:start()\n\n        local indent = 0\n\n        for _, modifier in ipairs(indent_data.modifiers or {}) do\n            if module.config.public.modifiers[modifier] then\n                local ret = module.config.public.modifiers[modifier](buf, node, line, initial_indent) ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n\n                if ret ~= 0 then\n                    indent = ret\n                end\n            end\n        end\n\n        local line_len = (vim.api.nvim_buf_get_lines(buf, line, line + 1, true)[1] or \"\"):len()\n\n        -- Ensure that the cursor is within the `norg` language\n        local current_lang = vim.treesitter.get_parser(buf, \"norg\"):language_for_range({\n            line,\n            line_len,\n            line,\n            line_len,\n        })\n\n        -- If it isn't then fall back to `nvim-treesitter`'s indent instead.\n        if current_lang:lang() ~= \"norg\" then\n            -- If we're in a ranged tag then apart from providing nvim-treesitter indents also make sure\n            -- to account for the indentation level of the tag itself.\n            if node:type() == \"ranged_verbatim_tag_content\" then\n                local lnum = line\n                local start = node:range()\n\n                while lnum > start do\n                    if vim.api.nvim_buf_get_lines(0, lnum - 1, lnum, true)[1]:match(\"^%s*$\") then\n                        lnum = lnum - 1\n                    else\n                        return vim.fn[\"nvim_treesitter#indent\"]()\n                    end\n                end\n\n                return ts.get_node_range(node:parent()).column_start + vim.fn[\"nvim_treesitter#indent\"]()\n            else\n                return vim.fn[\"nvim_treesitter#indent\"]()\n            end\n        end\n\n        -- Check if the code is within a verbatim block\n        local ranged_tag = ts.find_parent(node, \"ranged_verbatim_tag_content\")\n        indent_data = ranged_tag and module.config.public.indents[ranged_tag:type()] or indent_data\n\n        -- Indents can be a static value, so account for that here\n        if type(indent_data.indent) == \"number\" then\n            -- If the indent is -1 then let Neovim indent instead of us\n            if indent_data.indent == -1 then\n                return -1\n            end\n\n            local new_indent = indent + indent_data.indent + (module.config.public.tweaks[node:type()] or 0)\n\n            if (not module.config.public.dedent_excess) and new_indent <= initial_indent then\n                return initial_indent\n            end\n\n            return new_indent\n        end\n\n        local calculated_indent = indent_data.indent(buf, node, line, indent, initial_indent) or 0\n\n        if calculated_indent == -1 then\n            return -1\n        end\n\n        local new_indent = indent + calculated_indent + (module.config.public.tweaks[node:type()] or 0)\n\n        if (not module.config.public.dedent_excess) and new_indent <= initial_indent then\n            return initial_indent\n        end\n\n        return new_indent\n    end,\n\n    ---re-evaluate the indent expression for each line in the range, and apply the new indentation\n    ---@param buffer number\n    ---@param row_start number 0 based\n    ---@param row_end number 0 based exclusive\n    reindent_range = function(buffer, row_start, row_end)\n        for i = row_start, row_end - 1 do\n            local indent_level = module.public.indentexpr(buffer, i)\n            module.public.buffer_set_line_indent(buffer, i, indent_level)\n        end\n    end,\n\n    ---Set the indent of the given line to the new value\n    ---@param buffer number\n    ---@param start_row number 0 based\n    ---@param new_indent number\n    buffer_set_line_indent = function(buffer, start_row, new_indent)\n        local line = vim.api.nvim_buf_get_lines(buffer, start_row, start_row + 1, true)[1]\n        if line:match(\"^%s*$\") or new_indent == -1 then\n            return\n        end\n\n        local leading_whitespace = line:match(\"^%s*\"):len()\n        vim.api.nvim_buf_set_text(buffer, start_row, 0, start_row, leading_whitespace, { (\" \"):rep(new_indent) })\n    end,\n}\n\nmodule.config.public = {\n    -- The table of indentations.\n    --\n    -- This table describes a set of node types and how they should be indented\n    -- when encountered in the syntax tree.\n    --\n    -- It also allows for certain nodes to be given properties (modifiers), which\n    -- can additively stack indentation given more complex circumstances.\n    indents = {\n        -- Default behaviour for every other node not explicitly defined.\n        _ = {\n            modifiers = { \"under-headings\" },\n            indent = 0,\n        },\n\n        -- Indent behaviour for paragraph segments (lines of text).\n        [\"paragraph_segment\"] = {\n            modifiers = { \"under-headings\", \"under-nestable-detached-modifiers\" },\n            indent = 0,\n        },\n\n        -- Indent behaviour for strong paragraph delimiters.\n        --\n        -- The indentation of these should be determined based on the heading level\n        -- that it is a part of. Since the `strong_paragraph_delimiter` node isn't actually\n        -- a child of the previous heading in the syntax tree some extra work is required to\n        -- make it indent as expected.\n        [\"strong_paragraph_delimiter\"] = {\n            indent = function(buf, _, line, _, _)\n                local node = ts.get_first_node_on_line(buf, vim.fn.prevnonblank(line) - 1)\n\n                if not node then\n                    return 0\n                end\n\n                return ts.get_node_range(node:type():match(\"heading%d\") and node:named_child(1) or node).column_start\n            end,\n        },\n\n        -- Indent behaviour for headings.\n        --\n        -- In \"idiomatic norg\", headings should not be indented.\n        [\"heading1\"] = {\n            indent = 0,\n        },\n\n        -- Indent behaviour for headings.\n        --\n        -- In \"idiomatic norg\", headings should not be indented.\n        [\"heading2\"] = {\n            indent = 0,\n        },\n\n        -- Indent behaviour for headings.\n        --\n        -- In \"idiomatic norg\", headings should not be indented.\n        [\"heading3\"] = {\n            indent = 0,\n        },\n\n        -- Indent behaviour for headings.\n        --\n        -- In \"idiomatic norg\", headings should not be indented.\n        [\"heading4\"] = {\n            indent = 0,\n        },\n\n        -- Indent behaviour for headings.\n        --\n        -- In \"idiomatic norg\", headings should not be indented.\n        [\"heading5\"] = {\n            indent = 0,\n        },\n\n        -- Indent behaviour for headings.\n        --\n        -- In \"idiomatic norg\", headings should not be indented.\n        [\"heading6\"] = {\n            indent = 0,\n        },\n\n        -- Ranged tag contents' indentation should be calculated by Neovim itself.\n        [\"ranged_verbatim_tag_content\"] = {\n            indent = -1,\n        },\n\n        -- `@end` tags should always be indented as far as the beginning `@` ranged verbatim tag.\n        [\"ranged_verbatim_tag_end\"] = {\n            modifiers = { \"ranged-tag-end\" },\n            indent = 0,\n        },\n\n        -- `|end` tags should always be indented as far as the beginning `|` ranged tag.\n        [\"ranged_tag_end\"] = {\n            modifiers = { \"ranged-tag-end\" },\n            indent = 0,\n        },\n\n        -- `=end` tags should always be indented as far as the beginning `=` ranged tag.\n        [\"macro_tag_end\"] = {\n            modifiers = { \"ranged-tag-end\" },\n            indent = 0,\n        },\n    },\n\n    -- Apart from indents, modifiers may also be defined.\n    --\n    -- These are repeatable instructions for nodes that share common traits.\n    modifiers = {\n        -- For any object that can exist under headings\n        [\"under-headings\"] = function(_, node)\n            local heading = ts.find_parent(node:parent(), \"heading%d\")\n\n            if not heading then\n                return 0\n            end\n            local child = heading:named_child(1)\n            if not child then\n                return 0\n            end\n\n            return ts.get_node_range(child).column_start\n        end,\n\n        -- For any object that should be indented under a list\n        [\"under-nestable-detached-modifiers\"] = function(_, node)\n            local list = ts.find_parent(node, {\n                \"unordered_list1\",\n                \"unordered_list2\",\n                \"unordered_list3\",\n                \"unordered_list4\",\n                \"unordered_list5\",\n                \"unordered_list6\",\n                \"ordered_list1\",\n                \"ordered_list2\",\n                \"ordered_list3\",\n                \"ordered_list4\",\n                \"ordered_list5\",\n                \"ordered_list6\",\n                \"quote1\",\n                \"quote2\",\n                \"quote3\",\n                \"quote4\",\n                \"quote5\",\n                \"quote6\",\n            })\n\n            if not list or not list:named_child(1) then\n                return 0\n            end\n\n            if list:named_child(1):type() == \"detached_modifier_extension\" then\n                local child = list:named_child(2)\n                if not child then\n                    return 0\n                end\n                return ts.get_node_range(child).column_start + ts.get_node_text(list:named_child(2)):match(\"^%s*\"):len()\n            end\n\n            local child = list:named_child(1)\n            if not child then\n                return 0\n            end\n            return ts.get_node_range(child).column_start\n        end,\n\n        -- For any ranged tag end that should always be indented as far as the beginning of the ranged tag\n        [\"ranged-tag-end\"] = function(_, node)\n            return ts.get_node_range(node:parent()).column_start\n        end,\n    },\n\n    -- Tweaks are user defined `node_name` => `indent_level` mappings,\n    -- allowing the user to overwrite the indentation level for certain nodes.\n    --\n    -- Nodes can be found via treesitter's `:InspectTree`. For example,\n    -- indenting an unordered list can be done with `unordered_list2 = 4`\n    tweaks = {},\n\n    -- When true, will reformat the current line every time you press `<CR>` (Enter).\n    format_on_enter = true,\n\n    -- When true, will reformat the current line every time you press `<Esc>` (i.e. every\n    -- time you leave insert mode).\n    format_on_escape = true,\n\n    -- When false will not dedent nodes, only indent them. This means that if a node\n    -- is indented too much to the right, it will not be touched. It will only be indented\n    -- if the node is to the left of the expected indentation level.\n    --\n    -- Useful when writing documentation in the style of vimdoc, where content is indented\n    -- heavily to the right in comparison to the default Neorg style.\n    dedent_excess = true,\n}\n\nmodule.load = function()\n    module.required[\"core.autocommands\"].enable_autocommand(\"BufEnter\")\n\n    ts = module.required[\"core.integrations.treesitter\"]\nend\n\nmodule.on_event = function(event)\n    if event.type == \"core.autocommands.events.bufenter\" and event.content.norg then\n        vim.api.nvim_set_option_value(\n            \"indentexpr\",\n            (\"v:lua.require'neorg'.modules.get_module('core.esupports.indent').indentexpr(%d)\"):format(event.buffer),\n            { buf = event.buffer }\n        )\n\n        local indentkeys = \"o,O,*<M-o>,*<M-O>\"\n            .. lib.when(module.config.public.format_on_enter, \",*<CR>\", \"\")\n            .. lib.when(module.config.public.format_on_escape, \",*<Esc>\", \"\")\n        vim.api.nvim_set_option_value(\"indentkeys\", indentkeys, { buf = event.buffer })\n    end\nend\n\nmodule.events.subscribed = {\n    [\"core.autocommands\"] = {\n        bufenter = true,\n    },\n}\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/esupports/metagen/module.lua",
    "content": "--[[\n    file: Metagen\n    title: Manually Writing Metadata? No Thanks\n    description: The metagen module automatically places relevant metadata at the top of your `.norg` files.\n    summary: A Neorg module for generating document metadata automatically.\n    ---\nThe metagen module exposes two commands - `:Neorg inject-metadata` and `:Neorg update-metadata`.\n\n- The `inject-metadata` command will remove any existing metadata and overwrite it with fresh information.\n- The `update-metadata` preserves existing info, updating things like the `updated` fields (when the file\n  was last edited) as well as a few other non-destructive fields.\n--]]\n\nlocal neorg = require(\"neorg.core\")\n---@type neorg.configuration, neorg.modules, neorg.core.utils\nlocal config, modules, utils, lib = neorg.config, neorg.modules, neorg.utils, neorg.lib\n\nlocal module = modules.create(\"core.esupports.metagen\")\n\nlocal function get_timezone_offset()\n    -- http://lua-users.org/wiki/TimeZon\n    -- return the timezone offset in seconds, as it was on the time given by ts\n    -- Eric Feliksik\n    local utcdate = os.date(\"!*t\", 0)\n    local localdate = os.date(\"*t\", 0)\n    localdate.isdst = false -- this is the trick\n    return os.difftime(os.time(localdate), os.time(utcdate)) ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\nend\n\nlocal function get_timestamp()\n    -- generate a ISO-8601 timestamp\n    -- example: 2023-09-05T09:09:11-0500\n    --\n    local timezone_config = module.config.public.timezone\n    if timezone_config == \"utc\" then\n        return os.date(\"!%Y-%m-%dT%H:%M:%S+0000\")\n    elseif timezone_config == \"implicit-local\" then\n        return os.date(\"%Y-%m-%dT%H:%M:%S\")\n    else\n        -- assert(timezone_config == \"local\")\n        local tz_offset = get_timezone_offset()\n        local h, m = math.modf(tz_offset / 3600)\n        return os.date(\"%Y-%m-%dT%H:%M:%S\") .. string.format(\"%+.4d\", h * 100 + m * 60)\n    end\nend\n\nlocal function get_author()\n    local author_config = module.config.public.author\n\n    if author_config == nil or author_config == \"\" then\n        return utils.get_username()\n    else\n        return author_config\n    end\nend\n\n-- The default template found in the config for this module.\nlocal default_template = {\n    -- The title field generates a title for the file based on the filename.\n    {\n        \"title\",\n        function()\n            return vim.fn.expand(\"%:p:t:r\")\n        end,\n    },\n\n    -- The description field is always kept empty for the user to fill in.\n    { \"description\", \"\" },\n\n    -- The authors field is taken from config or autopopulated by querying the current user's system username.\n    {\n        \"authors\",\n        get_author,\n    },\n\n    -- The categories field is always kept empty for the user to fill in.\n    { \"categories\", \"\" },\n\n    -- The created field is populated with the current date as returned by `os.date`.\n    {\n        \"created\",\n        get_timestamp,\n    },\n\n    -- When creating fresh, new metadata, the updated field is populated the same way\n    -- as the `created` date.\n    {\n        \"updated\",\n        get_timestamp,\n    },\n\n    -- The version field determines which Norg version was used when\n    -- the file was created.\n    {\n        \"version\",\n        function()\n            return config.norg_version\n        end,\n    },\n}\n\n-- For all of the currently configured template entries, fall back to default handling\n-- if the configuration omits the handler function. This allows an end user to specify\n-- they want an entry in the generated metadata but they do not want to override the\n-- default value for that entry by adding a singleton (like { \"description\" }) to the\n-- template.\nlocal function fill_template_defaults()\n    local function match_first(comparand)\n        return function(_key, value) ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n            if value[1] == comparand then\n                return value\n            else\n                return nil\n            end\n        end\n    end\n\n    module.config.public.template = vim.iter(module.config.public.template)\n        :map(function(elem)\n            if not elem[2] then\n                return lib.filter(default_template, match_first(elem[1]))\n            end\n            return elem\n        end)\n        :totable()\nend\n\nmodule.setup = function()\n    return { requires = { \"core.autocommands\", \"core.integrations.treesitter\" } }\nend\n\nmodule.config.public = {\n    -- One of \"none\", \"auto\" or \"empty\"\n    -- - \"none\" generates no metadata\n    -- - \"auto\" generates metadata if it is not present\n    -- - \"empty\" generates metadata only for new files/buffers.\n    type = \"none\",\n\n    -- Whether updated date field should be automatically updated on save if required\n    update_date = true,\n\n    -- How to generate a tabulation inside the `@document.meta` tag\n    tab = \"\",\n\n    -- Custom delimiter between tag and value\n    delimiter = \": \",\n\n    -- Custom template to use for generating content inside `@document.meta` tag\n    -- The template is a list of lists, each defining a key-value pair of metadata\n    --\n    -- Example:\n    -- ```\n    -- template = {\n    --   -- Default field name without a value will fall back to the default behavior\n    --   { \"title\" },\n    --   -- Set a custom value for \"authors\" field\n    --   { \"authors\", \"Vhyrro\" },\n    --   -- Fields can be set by lua functions\n    --   {\n    --     \"categories\",\n    --     function()\n    --       return {\"Category-1\", \"Category-2\"}\n    --     end\n    --   }\n    -- }\n    -- ```\n    template = default_template,\n\n    -- Custom author name that overrides default value if not nil or empty\n    -- Default value is autopopulated by querying the current user's system username.\n    author = \"\",\n\n    -- Timezone information in the timestamps\n    -- - \"utc\" the timestamp is in UTC+0\n    -- - \"local\" the timestamp is in the local timezone\n    -- - \"implicit-local\" like \"local\", but the timezone information is omitted from the timestamp\n    timezone = \"local\",\n\n    -- Whether or not to call `:h :undojoin` just before changing the timestamp in\n    -- `update_metadata`. This will make your undo key undo the last change before writing the file\n    -- in addition to the timestamp change. This will move your cursor to the top of the file. For\n    -- users with an autosave plugin, this option must be paired with keybinds for undo/redo to\n    -- avoid problems with undo tree branching:\n    -- ```lua\n    -- vim.keymap.set(\"n\", \"u\", function()\n    --     require(\"neorg.modules\").get_module(\"core.esupports.metagen\").skip_next_update()\n    --     local k = vim.api.nvim_replace_termcodes(\"u<c-o>\", true, false, true)\n    --     vim.api.nvim_feedkeys(k, 'n', false)\n    -- end)\n    -- vim.keymap.set(\"n\", \"<C-r>\", function()\n    --     require(\"neorg.modules\").get_module(\"core.esupports.metagen\").skip_next_update()\n    --     local k = vim.api.nvim_replace_termcodes(\"<c-r><c-o>\", true, false, true)\n    --     vim.api.nvim_feedkeys(k, 'n', false)\n    -- end)\n    -- ```\n    undojoin_updates = false,\n}\n\nmodule.private = {\n    buffers = {},\n    listen_event = \"none\",\n    skip_next_update = false,\n}\n\n---@class core.esupports.metagen\nmodule.public = {\n    --- Returns true if there is a `@document.meta` tag in the current document\n    ---@param buf number #The buffer to check in\n    ---@return boolean,table #Whether the metadata was present, and the range of the metadata node\n    is_metadata_present = function(buf)\n        local query = utils.ts_parse_query(\n            \"norg\",\n            [[\n                 (ranged_verbatim_tag\n                     (tag_name) @name\n                     (#eq? @name \"document.meta\")\n                 ) @meta\n            ]]\n        )\n\n        local root = module.required[\"core.integrations.treesitter\"].get_document_root(buf)\n\n        if not root then\n            return false, {\n                range = { 0, 0 },\n                node = nil,\n            }\n        end\n\n        local _, found = query:iter_matches(root, buf)()\n        local range = { 0, 0 }\n\n        if not found then\n            return false, {\n                range = range,\n                node = nil,\n            }\n        end\n\n        local metadata_node = nil\n\n        for id, nodes in pairs(found) do\n            local name = query.captures[id]\n            -- node is a list in nvim 0.11+\n            ---@type TSNode\n            local node\n            if vim.islist(nodes) then\n                node = nodes[1]\n            end\n            if name == \"meta\" then\n                metadata_node = node\n                range[1], _, range[2], _ = node:range()\n                range[2] = range[2] + 2\n            end\n        end\n\n        return true, {\n            range = range,\n            node = metadata_node,\n        }\n    end,\n\n    --- Skip the next call to update_metadata\n    skip_next_update = function()\n        module.private.skip_next_update = true\n    end,\n\n    ---@class core.esupports.metagen.metadata\n    ---@field title? function|string the title of the note\n    ---@field description? function|string the description of the note\n    ---@field authors? function|string the authors of the note\n    ---@field categories? function|string the categories of the note\n    ---@field created? function|string a timestamp of creation time for the note\n    ---@field updated? function|string a timestamp of last time the note was updated\n    ---@field version? function|string the neorg version\n\n    --- Creates the metadata contents from the provided metadata table (defaulting to the configuration's template).\n    ---@param buf number #The buffer to query potential data from\n    ---@param metadata? core.esupports.metagen.metadata #Table of metadata, overrides defaults if present\n    ---@return table #A table of strings that can be directly piped to `nvim_buf_set_lines`\n    construct_metadata = function(buf, metadata)\n        local template = module.config.public.template\n        local whitespace = type(module.config.public.tab) == \"function\" and module.config.public.tab()\n            or module.config.public.tab\n        local delimiter = type(module.config.public.delimiter) == \"function\" and module.config.public.delimiter()\n            or module.config.public.delimiter\n\n        local result = {\n            \"@document.meta\",\n        }\n\n        for _, data in ipairs(template) do\n            if metadata and metadata[data[1]] then\n                -- override with data from metadata table\n                data = { data[1], metadata[data[1]] }\n            end\n            local lines = whitespace\n                .. data[1]\n                .. delimiter\n                .. tostring(type(data[2]) == \"function\" and data[2]() or data[2])\n            for _, line in ipairs(vim.split(lines, \"\\n\")) do\n                table.insert(result, line)\n            end\n        end\n\n        table.insert(result, \"@end\")\n\n        if vim.api.nvim_buf_get_lines(buf, 0, 1, false)[1]:len() > 0 then\n            table.insert(result, \"\")\n        end\n\n        return result\n    end,\n\n    --- Inject the metadata into a buffer\n    ---@param buf number #The number of the buffer to inject the metadata into\n    ---@param force? boolean #Whether to forcefully override existing metadata\n    ---@param metadata? core.esupports.metagen.metadata #Table of metadata data, overrides defaults if present\n    inject_metadata = function(buf, force, metadata)\n        local present, data = module.public.is_metadata_present(buf)\n\n        if force or not present then\n            local constructed_metadata = module.public.construct_metadata(buf, metadata)\n            vim.api.nvim_buf_set_lines(buf, data.range[1], data.range[2], false, constructed_metadata)\n        end\n    end,\n\n    update_metadata = function(buf)\n        if module.private.skip_next_update then\n            module.private.skip_next_update = false\n            return\n        end\n\n        local present = module.public.is_metadata_present(buf)\n        if not present then\n            return\n        end\n\n        local modified = vim.api.nvim_get_option_value(\"modified\", { buf = buf })\n        if not modified then\n            return\n        end\n\n        -- Extract the root node of the norg_meta language\n        -- This process should be abstracted into a core.integrations.treesitter\n        -- function.\n        local languagetree = vim.treesitter.get_parser(buf, \"norg\")\n\n        if not languagetree then\n            return\n        end\n\n        local meta_root = nil\n\n        for _, tree in pairs(languagetree:children()) do\n            if tree:lang() ~= \"norg_meta\" or meta_root then\n                goto continue\n            end\n\n            local meta_tree = tree:parse()[1]\n\n            if not meta_tree then\n                goto continue\n            end\n\n            meta_root = meta_tree:root()\n            ::continue::\n        end\n\n        if not meta_root then\n            return\n        end\n\n        -- Capture current date from config\n        local current_date = \"\"\n        for _, val in ipairs(module.config.public.template) do\n            if val[1] == \"updated\" then\n                current_date = val[2]()\n            end\n        end\n\n        local query = utils.ts_parse_query(\n            \"norg_meta\",\n            [[\n            (pair\n                (key) @_key\n                (#eq? @_key \"updated\")\n                (value) @updated)\n        ]]\n        )\n\n        for id, node in query:iter_captures(meta_root, buf) do\n            local capture = query.captures[id]\n\n            if capture == \"updated\" then\n                local date = module.required[\"core.integrations.treesitter\"].get_node_text(node)\n\n                if date ~= current_date then\n                    local range = module.required[\"core.integrations.treesitter\"].get_node_range(node)\n\n                    if module.config.public.undojoin_updates then\n                        vim.cmd.undojoin()\n                    end\n                    vim.api.nvim_buf_set_text(\n                        buf,\n                        range.row_start,\n                        range.column_start,\n                        range.row_end,\n                        range.column_end,\n                        { current_date }\n                    )\n                end\n            end\n        end\n    end,\n}\n\nmodule.load = function()\n    -- combine user-defined template with defaults\n    fill_template_defaults()\n\n    modules.await(\"core.neorgcmd\", function(neorgcmd)\n        neorgcmd.add_commands_from_table({\n            [\"inject-metadata\"] = {\n                args = 0,\n                name = \"inject-metadata\",\n                condition = \"norg\",\n            },\n            [\"update-metadata\"] = {\n                args = 0,\n                name = \"update-metadata\",\n                condition = \"norg\",\n            },\n        })\n    end)\n\n    if module.config.public.type == \"auto\" then\n        module.required[\"core.autocommands\"].enable_autocommand(\"BufEnter\")\n        module.private.listen_event = \"bufenter\"\n    elseif module.config.public.type == \"empty\" then\n        module.required[\"core.autocommands\"].enable_autocommand(\"BufNewFile\")\n        module.private.listen_event = \"bufnewfile\"\n    end\n\n    if module.config.public.update_date then\n        vim.api.nvim_create_autocmd(\"BufWritePre\", {\n            pattern = \"*.norg\",\n            callback = function()\n                module.public.update_metadata(vim.api.nvim_get_current_buf())\n            end,\n            desc = \"Update updated date metadata field in norg documents\",\n        })\n    end\nend\n\nmodule.on_event = function(event)\n    if\n        event.type == (\"core.autocommands.events.\" .. module.private.listen_event)\n        and event.content.norg\n        and vim.api.nvim_buf_is_loaded(event.buffer)\n        and vim.api.nvim_get_option_value(\"modifiable\", { buf = event.buffer })\n        and not module.private.buffers[event.buffer]\n        and not vim.startswith(event.filehead, \"neorg://\") -- Do not inject metadata on displays created by neorg by default\n    then\n        module.public.inject_metadata(event.buffer)\n        module.private.buffers[event.buffer] = true\n    elseif event.type == \"core.neorgcmd.events.inject-metadata\" then\n        module.public.inject_metadata(event.buffer, true)\n        module.private.buffers[event.buffer] = true\n    elseif event.type == \"core.neorgcmd.events.update-metadata\" then\n        module.public.update_metadata(event.buffer)\n        module.private.buffers[event.buffer] = true\n    elseif event.type == \"core.dirman.events.file_created\" then\n        if event.content.opts.metadata then\n            module.public.inject_metadata(event.content.buffer, true, event.content.opts.metadata)\n        end\n    end\nend\n\nmodule.events.subscribed = {\n    [\"core.autocommands\"] = {\n        bufenter = true,\n        bufnewfile = true,\n        bufwritepre = true,\n    },\n\n    [\"core.neorgcmd\"] = {\n        [\"inject-metadata\"] = true,\n        [\"update-metadata\"] = true,\n    },\n    [\"core.dirman\"] = {\n        [\"file_created\"] = true,\n    },\n}\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/export/html/module.lua",
    "content": "--[[\n    file: HTML-Export\n    title: Neorg's HTML Exporter\n    summary: Interface for `core.export` to allow exporting to HTML.\n    ---\nThis module exists as an interface for `core.export` to export `.norg` files to HTML.\nAs a user the only reason you would ever have to touch this module is to configure *how* you'd\nlike your markdown to be exported (i.e. do you want to support certain extensions during the export).\nTo learn more about configuration, consult the [relevant section](#configuration).\n--]]\n\n-- TODO: One day this module will need to be restructured or maybe even rewritten.\n-- It's not atrocious, but there are a lot of moving parts that make it difficult to understand\n-- from another person's perspective. Some cleanup and rethinking of certain implementation\n-- details will be necessary.\n\nlocal neorg = require(\"neorg.core\")\nlocal modules = neorg.modules\n\nlocal module = modules.create(\"core.export.html\")\n\n---@type core.dirman, core.esupports.hop\nlocal dirman, hop\n\nmodule.setup = function()\n    return {\n        success = true,\n        requires = {\n            \"core.esupports.hop\",\n            \"core.dirman\",\n        },\n    }\nend\n\n--- Enumeration of different stackk types\n---@enum StackKey\nlocal StackKey = {\n    LIST = \"list\",\n    BLOCK_QUOTE = \"blockquote\",\n    SPAN = \"span\",\n}\n\n--- Enumeration of differnete link target types.\n--- @enum HeadingType\nlocal HeadingType = {\n    HEADING1 = \"h1\",\n    HEADING2 = \"h2\",\n    HEADING3 = \"h3\",\n    HEADING4 = \"h4\",\n    HEADING5 = \"h5\",\n    HEADING6 = \"h6\",\n}\n\n--- @class Location\n--- @field file string\n--- @field text string\n--- @field type HeadingType\n---\n--- @class FragmentArgs\n--- @field type string\n--- @field text string\n\n--> Generic Utility Functions\n\n--- Escapes unsafe characters in the string\n---@param text string string being escaped\n---@return string\nlocal function html_escape(text)\n    local escaped_text =\n        text:gsub(\"&\", \"&amp;\"):gsub(\"<\", \"&lt;\"):gsub(\">\", \"&gt;\"):gsub('\"', \"&quot;\"):gsub(\"'\", \"&#39;\")\n    return escaped_text\nend\n\n--- Applies HTML escaping to a word node\n---@param word string\n---@return  table\nlocal function escape_word(word)\n    return {\n        output = html_escape(word),\n    }\nend\n\n--- Adds opening tag and pushes closing tag onto stack to be popped in a recollector.\n---@param tag string\n---@param level number\n---@param stack_key StackKey\n---@return fun(_: any, _: any, state: table): table\nlocal function nest_tag(tag, level, stack_key)\n    return function(text, _, state)\n        if not state.nested_tag_stacks[stack_key] then\n            state.nested_tag_stacks[stack_key] = {}\n        end\n\n        local attributes = \"\"\n        if stack_key == StackKey.SPAN then\n            attributes = ' id=\"generic-' .. text:lower():gsub(\"<\", \"\"):gsub(\">\", \"\"):gsub(\" \", \"\") .. '\" '\n        end\n\n        local output = \"\"\n        local opening_tag = \"\\n<\" .. tag .. attributes .. \">\\n\"\n        local closing_tag = \"\\n</\" .. tag .. \">\\n\"\n\n        while level > #state.nested_tag_stacks[stack_key] do\n            output = output .. opening_tag\n            table.insert(state.nested_tag_stacks[stack_key], closing_tag)\n        end\n\n        while level < #state.nested_tag_stacks[stack_key] do\n            output = output .. table.remove(state.nested_tag_stacks[stack_key])\n        end\n\n        return {\n            output = output,\n            keep_descending = true,\n        }\n    end\nend\n\n--- Recollects tags by popping them off the stack and appending them to the\n--- output\n---@param stack_key StackKey\n---@return fun(output: table, state: table): table\nlocal function nested_tag_recollector(stack_key)\n    return function(output, state)\n        local suffix = \"\"\n\n        local closing_tag = table.remove(state.nested_tag_stacks[stack_key])\n        while closing_tag do\n            suffix = suffix .. closing_tag\n            closing_tag = table.remove(state.nested_tag_stacks[stack_key])\n        end\n\n        table.insert(output, suffix)\n\n        return output\n    end\nend\n\n--- Return true when a given stack key is empty\n---@param state table\n---@param stack_key StackKey\n---@return boolean\nlocal function is_stack_empty(state, stack_key)\n    return not state.nested_tag_stacks[stack_key] or #state.nested_tag_stacks[stack_key] == 0\nend\n\n---@param heading_type HeadingType\n---@param target_type LinkType\n---@return fun(): table\nlocal function heading(heading_type, target_type)\n    return function()\n        return {\n            output = \"<div>\\n\",\n            keep_descending = true,\n            state = {\n                heading = heading_type,\n                target_type = target_type,\n            },\n        }\n    end\nend\n\n---Appends the closing p tag when required\n---@param output table\n---@param state table\n---@return table\nlocal function add_closing_p_tag(output, state)\n    if not state.link and is_stack_empty(state, StackKey.LIST) and is_stack_empty(state, StackKey.SPAN) then\n        local new_output = { \"\\n<p>\\n\" }\n        for _, value in ipairs(output) do\n            table.insert(new_output, value)\n        end\n        table.insert(new_output, \"\\n</p>\\n\")\n        output = new_output\n    end\n\n    return output\nend\n\n---Appends a given tag to the output\n---@param tag string\n---@param cleanup? fun(state)\n---@return fun(output: table, state: table): table\nlocal function add_closing_tag(tag, cleanup)\n    return function(output, state)\n        table.insert(output, tag)\n        if cleanup then\n            cleanup(state)\n        end\n        return output\n    end\nend\n\n---Builds a link and adds it to the output givne recollected data in the state table.\n---@return fun(_: any, state: table): table\nlocal function wrap_anchor()\n    return function(output, state)\n        local link_builder = module.config.public.link_builders.link_builder\n\n        local href\n        if state.link then\n            href = link_builder(state.link)\n        else\n            href = \"\"\n        end\n\n        local content\n        if #output > 0 and output[1] ~= \"\" then\n            content = output[1]\n        else\n            content = state.link.link_text\n        end\n\n        output = {\n            '<a href=\"' .. href .. '\">',\n            content,\n            \"</a>\",\n        }\n\n        state.link = nil\n\n        return output\n    end\nend\n\nlocal function set_link(_, node)\n    local link = hop.parse_link(node, 0)\n\n    return {\n        keep_descending = true,\n        state = {\n            link = link,\n        },\n    }\nend\n\n---Just keeps swimming\n---@param state_or_fn? table|fun(): table\n---@return fun(): table\nlocal function keep_descending(state_or_fn)\n    return function()\n        local state\n        if type(state_or_fn) == \"function\" then\n            state = state_or_fn()\n        else\n            state = state_or_fn\n        end\n\n        return {\n            output = \"\",\n            keep_descending = true,\n            state = state,\n        }\n    end\nend\n\n---@param output table\n---@return table\nlocal function recollect_footnote(output, state)\n    local title = table.remove(output, 1) .. table.remove(output, 1)\n    local content = table.concat(output)\n    local footnote_number = #state.footnotes + 1\n\n    table.insert(state.footnotes, { title = title, content = content, number = footnote_number })\n\n    return {\n        '<a href=\"#footnote-' .. footnote_number .. '\">[' .. footnote_number .. \"]</a>\",\n    }\nend\n\n---@return fun(text: string, node: TSNode): table\nlocal function ranged_verbatim_tag_content()\n    return function(text, node)\n        local _, start_column = node:range()\n        local indent = \"\"\n        local i = 0\n        while i < start_column do\n            indent = indent .. \" \"\n            i = i + 1\n        end\n\n        return {\n            output = \"\",\n            state = {\n                tag_content = indent .. text,\n                tag_indent_level = start_column,\n            },\n        }\n    end\nend\n\nlocal function init_state()\n    return {\n        todo = nil,\n        tag_params = {},\n        tag_close = nil,\n        heading = nil,\n        ranged_tag_indentation_level = 0,\n        is_url = false,\n        nested_tag_stacks = {},\n        anchors = {},\n        link = nil,\n        footnotes = {},\n    }\nend\n\n---@param text string\n---@param state  table\n---@return table\nlocal function paragraph_segment(text, _, state)\n    local output = \"\\n\"\n    local fragment_builder = module.config.public.link_builders.fragment_builder\n\n    if state.heading then\n        output = \"<\" .. state.heading .. ' id=\"' .. fragment_builder({ type = state.target_type, text = text }) .. '\">'\n        -- Add span to support generic link targets\n        output = output .. '<span id=\"' .. fragment_builder({ type = \"generic\", text = text }) .. '\"></span>'\n    end\n\n    local todo = \"\"\n    if state.todo then\n        todo = '<span class=\"todo-status-' .. state.todo .. '\"></span>'\n        state.todo = nil\n    end\n\n    return {\n        output = output .. todo,\n        keep_descending = true,\n    }\nend\n\n---@param node TSNode\n---@return string\nlocal function get_opening_tag(_, node)\n    local parent_type = node:parent():type()\n    local tag = module.private.open_close_tags[parent_type]\n    if type(tag) == \"table\" then\n        return \"<\" .. tag.tag .. ' class=\"' .. tag.class .. '\">'\n    elseif tag then\n        return \"<\" .. tag .. \">\"\n    else\n        return \"\"\n    end\nend\n\n---@param node TSNode\n---@return string\nlocal function get_closing_tag(_, node)\n    local parent_type = node:parent():type()\n    local tag = module.private.open_close_tags[parent_type]\n\n    if type(tag) == \"table\" then\n        return \"</\" .. tag.tag .. \">\"\n    elseif tag then\n        return \"</\" .. tag .. \">\"\n    else\n        return \"\"\n    end\nend\n\n---@param text string\n---@return table\nlocal function add_tag_name(text)\n    return {\n        output = \"\",\n        state = {\n            tag_name = text,\n        },\n    }\nend\n\n---@param text string\n---@param state table\n---@return table\nlocal function add_tag_param(text, _, state)\n    local tag_params = table.insert(state.tag_params, text)\n\n    return {\n        output = \"\",\n        state = {\n            tag_params = tag_params,\n        },\n    }\nend\n\n---@param output table\n---@param state table\n---@return table\nlocal function add_closing_segement_tags(output, state)\n    if state.heading then\n        table.insert(output, \"</\" .. state.heading .. \">\")\n        state.heading = nil\n        state.target_type = nil\n    end\n\n    return output\nend\n\n---@param output table\n---@param state table\n---@return table\nlocal function apply_ranged_tag_handlers(output, state)\n    local name = state.tag_name\n    local content = state.tag_content\n    local params = state.tag_params\n\n    local ranged_tag_handler = module.config.public.ranged_tag_handler[name]\n        or module.private.ranged_tag_handler[name]\n        or module.private.ranged_tag_handler[\"comment\"]\n\n    table.insert(output, ranged_tag_handler(params, content, state.tag_indent_level))\n\n    state.tag_name = \"\"\n    state.tag_params = {}\n    state.tag_content = \"\"\n    state.tag_indent_level = 0\n\n    return output\nend\n\nlocal function build_footnote(footnote)\n    return table.concat({\n        '\\n<div class=\"footnote\" id=\"footnote-' .. footnote.number .. '\">',\n        '\\n<div class=\"footnote-number\">\\n',\n        footnote.number,\n        \"\\n</div>\",\n        '\\n<div class=\"footnote-title\">\\n',\n        footnote.title,\n        \"\\n</div>\",\n        '\\n<div class=\"footnore-content\">\\n',\n        footnote.content,\n        \"\\n</div>\",\n        \"\\n</div>\",\n        \"\\n\",\n    })\nend\n\nlocal function get_anchor(_, node, _)\n    local target = hop.locate_anchor_declaration_target(node)\n    local link = nil\n    local link_desription = nil\n    local anchor_definition = nil\n    if target then\n        link_desription = target.node:parent()\n    end\n    if link_desription then\n        anchor_definition = link_desription:parent()\n    end\n    if anchor_definition then\n        link = hop.parse_link(anchor_definition, 0)\n    end\n\n    return {\n        keep_descending = true,\n        state = {\n            link = link,\n        },\n    }\nend\n\nmodule.load = function()\n    dirman = module.required[\"core.dirman\"]\n    hop = module.required[\"core.esupports.hop\"]\nend\n\nmodule.config.public = {\n    --- If you'd like to modify the way specific range tabs are handled. For\n    --- example if you wanted to translate document.meta into use-case specific\n    --- HTML, you could so here (see: module.private[ranged_tag_handler\"\"] for\n    --- examples).\n    ranged_tag_handler = {},\n    -- Used by the exporter to know what extension to use\n    -- when creating HTML files.\n    -- The default is recommended, although you can change it.\n    extension = \"html\",\n    link_builders = {\n        --- Function handler for building just the fragment. The fragment is the part\n        --- of the URL that comes after the \"#\" and it's used for linking to specific\n        --- IDs within a file.\n        ---@param args FragmentArgs\n        ---@return string\n        fragment_builder = function(args)\n            if args.type == \"external_file\" or args.type == \"url\" then\n                -- External links and target URLs don't have target support by default.\n                return \"\"\n            end\n            local text = args.text or \"\"\n\n            return args.type .. \"-\" .. text:lower():gsub(\" \", \"\")\n        end,\n        -- Function handler for building just the path URL path.\n        ---@param link Link\n        ---@return string\n        path_builder = function(link)\n            local file = link.link_file_text or \"\"\n            if file:match(\"%$/\") then\n                local workspace_path = \"/\"\n                local current_workspace = dirman.get_current_workspace()\n                if current_workspace then\n                    workspace_path = \"/\" .. current_workspace[1] .. \"/\"\n                end\n                return (file:gsub(\"%$/\", workspace_path):gsub(\".norg\", \".html\"))\n            elseif #file > 0 then\n                return (file:gsub(\"%$\", \"/\"):gsub(\".norg\", \".html\"))\n            else\n                return \"\"\n            end\n        end,\n        --- Function handler for building the entire link. If you change this handler\n        --- you'll need to change\n        ---@param link Link\n        ---@return string\n        link_builder = function(link)\n            if link.link_type == \"external_file\" then\n                local file = link.link_location_text or \"\"\n                return \"file://\" .. file:gsub(\" \", \"\")\n            end\n\n            if link.link_type == \"url\" then\n                return link.link_location_text\n            end\n\n            local fragment_builder = module.config.public.link_builders.fragment_builder\n            local path_builder = module.config.public.link_builders.path_builder\n\n            return path_builder(link)\n                .. \"#\"\n                .. fragment_builder({ type = link.link_type, text = link.link_location_text })\n        end,\n    },\n}\n\nmodule.private = {\n    ranged_tag_handler = {\n        [\"code\"] = function(params, content, indent_level)\n            local language = params[1] or \"\"\n\n            local indent_regex = \"^\" .. string.rep(\"%s\", indent_level)\n            local lines_of_code = {}\n\n            for line in string.gmatch(content, \"[^\\n]+\") do\n                local normalized_line = line:gsub(indent_regex, \"\")\n                table.insert(lines_of_code, normalized_line)\n            end\n\n            local code_block = html_escape(table.concat(lines_of_code, \"\\n\"))\n\n            return '\\n<pre>\\n<code class=\"' .. language .. '\">\\n' .. code_block .. \"\\n</code>\\n</pre>\\n\"\n        end,\n\n        [\"comment\"] = function(_, content)\n            return \"\\n<!--\\n\" .. content .. \"\\n-->\\n\"\n        end,\n    },\n    open_close_tags = {\n        [\"bold\"] = \"b\",\n        [\"italic\"] = \"i\",\n        [\"underline\"] = \"u\",\n        [\"strikethrough\"] = \"s\",\n        [\"spoiler\"] = { tag = \"span\", class = \"spoiler\" },\n        [\"verbatim\"] = { tag = \"code\", class = \"verbatim\" },\n        [\"superscript\"] = \"sup\",\n        [\"subscript\"] = \"sub\",\n        [\"inline_math\"] = { tag = \"code\", class = \"inline-math\" },\n    },\n}\n\n--- @class core.export.html\nmodule.public = {\n    export = {\n        init_state = init_state,\n        functions = {\n            [\"_word\"] = escape_word,\n            [\"_space\"] = escape_word,\n            [\"_open\"] = get_opening_tag,\n            [\"_close\"] = get_closing_tag,\n            [\"_begin\"] = \"\",\n            [\"_end\"] = \"\",\n            [\"escape_sequence\"] = keep_descending(),\n            [\"any_char\"] = true,\n\n            [\"paragraph_segment\"] = paragraph_segment,\n            [\"paragraph\"] = keep_descending(),\n\n            [\"heading1\"] = heading(HeadingType.HEADING1, \"heading1\"),\n            [\"heading2\"] = heading(HeadingType.HEADING2, \"heading2\"),\n            [\"heading3\"] = heading(HeadingType.HEADING3, \"heading3\"),\n            [\"heading4\"] = heading(HeadingType.HEADING4, \"heading4\"),\n            [\"heading5\"] = heading(HeadingType.HEADING5, \"heading5\"),\n            [\"heading6\"] = heading(HeadingType.HEADING6, \"heading6\"),\n\n            [\"inline_link_target\"] = nest_tag(\"span\", 1, StackKey.SPAN),\n\n            [\"unordered_list1\"] = nest_tag(\"ul\", 1, StackKey.LIST),\n            [\"unordered_list2\"] = nest_tag(\"ul\", 2, StackKey.LIST),\n            [\"unordered_list3\"] = nest_tag(\"ul\", 3, StackKey.LIST),\n            [\"unordered_list4\"] = nest_tag(\"ul\", 4, StackKey.LIST),\n            [\"unordered_list5\"] = nest_tag(\"ul\", 5, StackKey.LIST),\n            [\"unordered_list6\"] = nest_tag(\"ul\", 6, StackKey.LIST),\n            [\"unordered_list1_prefix\"] = \"\\n<li>\\n\",\n            [\"unordered_list2_prefix\"] = \"\\n<li>\\n\",\n            [\"unordered_list3_prefix\"] = \"\\n<li>\\n\",\n            [\"unordered_list4_prefix\"] = \"\\n<li>\\n\",\n            [\"unordered_list5_prefix\"] = \"\\n<li>\\n\",\n            [\"unordered_list6_prefix\"] = \"\\n<li>\\n\",\n\n            [\"ordered_list1\"] = nest_tag(\"ol\", 1, StackKey.LIST),\n            [\"ordered_list2\"] = nest_tag(\"ol\", 2, StackKey.LIST),\n            [\"ordered_list3\"] = nest_tag(\"ol\", 3, StackKey.LIST),\n            [\"ordered_list4\"] = nest_tag(\"ol\", 4, StackKey.LIST),\n            [\"ordered_list5\"] = nest_tag(\"ol\", 5, StackKey.LIST),\n            [\"ordered_list6\"] = nest_tag(\"ol\", 6, StackKey.LIST),\n            [\"ordered_list1_prefix\"] = \"\\n<li>\\n\",\n            [\"ordered_list2_prefix\"] = \"\\n<li>\\n\",\n            [\"ordered_list3_prefix\"] = \"\\n<li>\\n\",\n            [\"ordered_list4_prefix\"] = \"\\n<li>\\n\",\n            [\"ordered_list5_prefix\"] = \"\\n<li>\\n\",\n            [\"ordered_list6_prefix\"] = \"\\n<li>\\n\",\n\n            [\"quote1\"] = nest_tag(\"blockquote\", 1, StackKey.BLOCK_QUOTE),\n            [\"quote2\"] = nest_tag(\"blockquote\", 2, StackKey.BLOCK_QUOTE),\n            [\"quote3\"] = nest_tag(\"blockquote\", 3, StackKey.BLOCK_QUOTE),\n            [\"quote4\"] = nest_tag(\"blockquote\", 4, StackKey.BLOCK_QUOTE),\n            [\"quote5\"] = nest_tag(\"blockquote\", 5, StackKey.BLOCK_QUOTE),\n            [\"quote6\"] = nest_tag(\"blockquote\", 6, StackKey.BLOCK_QUOTE),\n\n            [\"tag_parameters\"] = keep_descending(function()\n                return { tag_params = {} }\n            end),\n            [\"tag_name\"] = add_tag_name,\n            [\"tag_param\"] = add_tag_param,\n            [\"ranged_verbatim_tag_content\"] = ranged_verbatim_tag_content(),\n\n            [\"todo_item_done\"] = keep_descending({ todo = \"done\" }),\n            [\"todo_item_undone\"] = keep_descending({ todo = \"undone\" }),\n            [\"todo_item_pending\"] = keep_descending({ todo = \"pending\" }),\n            [\"todo_item_urgent\"] = keep_descending({ todo = \"urgent\" }),\n            [\"todo_item_cancelled\"] = keep_descending({ todo = \"cancelled\" }),\n            [\"todo_item_recurring\"] = keep_descending({ todo = \"recurring\" }),\n            [\"todo_item_on_hold\"] = keep_descending({ todo = \"on_hold\" }),\n            [\"todo_item_uncertain\"] = keep_descending({ todo = \"uncertain\" }),\n\n            [\"single_footnote\"] = keep_descending(),\n            [\"multi_footnote\"] = keep_descending(),\n\n            [\"link\"] = set_link,\n            [\"anchor_definition\"] = set_link,\n            [\"anchor_declaration\"] = get_anchor,\n\n            [\"strong_carryover\"] = \"\",\n            [\"weak_carryover\"] = \"\",\n\n            -- [UNSUPPORTED] Infirm Tags are not currently supported, TS parsing\n            -- is returning unexpected ranges for .image tag, specically \"http:\"\n            -- gets included as a param and then the rest of hte URL is moved to\n            -- the following paragraph as content.\n            [\"infirm_tag\"] = \"\",\n        },\n\n        recollectors = {\n            [\"paragraph\"] = add_closing_p_tag,\n            [\"paragraph_segment\"] = add_closing_segement_tags,\n\n            [\"link\"] = wrap_anchor(),\n            [\"anchor_definition\"] = wrap_anchor(),\n            [\"anchor_declaration\"] = wrap_anchor(),\n\n            [\"generic_list\"] = nested_tag_recollector(StackKey.LIST),\n            [\"quote\"] = nested_tag_recollector(StackKey.BLOCK_QUOTE),\n            [\"inline_link_target\"] = nested_tag_recollector(StackKey.SPAN),\n\n            [\"heading1\"] = add_closing_tag(\"\\n</div>\\n\"),\n            [\"heading2\"] = add_closing_tag(\"\\n</div>\\n\"),\n            [\"heading3\"] = add_closing_tag(\"\\n</div>\\n\"),\n            [\"heading4\"] = add_closing_tag(\"\\n</div>\\n\"),\n            [\"heading5\"] = add_closing_tag(\"\\n</div>\\n\"),\n            [\"heading6\"] = add_closing_tag(\"\\n</div>\\n\"),\n\n            [\"unordered_list1\"] = add_closing_tag(\"\\n</li>\\n\"),\n            [\"unordered_list2\"] = add_closing_tag(\"\\n</li>\\n\"),\n            [\"unordered_list3\"] = add_closing_tag(\"\\n</li>\\n\"),\n            [\"unordered_list4\"] = add_closing_tag(\"\\n</li>\\n\"),\n            [\"unordered_list5\"] = add_closing_tag(\"\\n</li>\\n\"),\n            [\"unordered_list6\"] = add_closing_tag(\"\\n</li>\\n\"),\n            [\"ordered_list1\"] = add_closing_tag(\"\\n</li>\\n\"),\n            [\"ordered_list2\"] = add_closing_tag(\"\\n</li>\\n\"),\n            [\"ordered_list3\"] = add_closing_tag(\"\\n</li>\\n\"),\n            [\"ordered_list4\"] = add_closing_tag(\"\\n</li>\\n\"),\n            [\"ordered_list5\"] = add_closing_tag(\"\\n</li>\\n\"),\n            [\"ordered_list6\"] = add_closing_tag(\"\\n</li>\\n\"),\n\n            [\"ranged_verbatim_tag_end\"] = apply_ranged_tag_handlers,\n\n            [\"single_footnote\"] = recollect_footnote,\n            [\"multi_footnote\"] = recollect_footnote,\n        },\n\n        cleanup = function(output, state)\n            if #state.footnotes > 0 then\n                output = output .. \"\\n<hr />\\n\"\n            end\n\n            for _, footnote in ipairs(state.footnotes) do\n                output = output .. \"\\n\" .. build_footnote(footnote)\n            end\n            return output\n        end,\n    },\n}\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/export/markdown/module.lua",
    "content": "--[[\n    file: Markdown-Export\n    title: Neorg's Markdown Exporter\n    summary: Interface for `core.export` to allow exporting to markdown.\n    ---\nThis module exists as an interface for `core.export` to export `.norg` files to Markdown.\nAs a user the only reason you would ever have to touch this module is to configure *how* you'd\nlike your markdown to be exported (i.e. do you want to support certain extensions during the export).\nTo learn more about configuration, consult the [relevant section](#configuration).\n--]]\n\n-- TODO: One day this module will need to be restructured or maybe even rewritten.\n-- It's not atrocious, but there are a lot of moving parts that make it difficult to understand\n-- from another person's perspective. Some cleanup and rethinking of certain implementation\n-- details will be necessary.\n\nlocal neorg = require(\"neorg.core\")\nlocal lib, modules = neorg.lib, neorg.modules\n\nlocal module = modules.create(\"core.export.markdown\")\n\nmodule.setup = function()\n    return {\n        success = true,\n        requires = {\n            \"core.integrations.treesitter\",\n        },\n    }\nend\n\nlocal last_parsed_link_location = \"\"\n\n--> Generic Utility Functions\n\nlocal function unordered_list_prefix(level)\n    return function()\n        return {\n            output = string.rep(\" \", (level - 1) * 4) .. \"- \",\n            keep_descending = true,\n            state = {\n                weak_indent = ((level - 1) * 4) + 2,\n            },\n        }\n    end\nend\n\nlocal function ordered_list_prefix(level)\n    return function(_, node, state)\n        state.ordered_list_level[level] = state.ordered_list_level[level] + 1\n        state.weak_indent = ((level - 1) * 4) + 3 + (tostring(state.ordered_list_level[level]):len() - 1)\n\n        local parent = node:parent()\n        local prev_node = parent:prev_named_sibling()\n        -- If the previous node from the current parent (`ordered_list`) isn't another ordered\n        -- list node, the list was split and the current count should be restarted\n        if prev_node == nil or prev_node:type() ~= parent:type() then\n            state.ordered_list_level[level] = 1\n        end\n\n        return {\n            output = string.rep(\" \", (level - 1) * 4) .. tostring(state.ordered_list_level[level]) .. \". \",\n            keep_descending = true,\n            state = state,\n        }\n    end\nend\n\nlocal function todo_item_extended(replace_text)\n    return function(_, node, state)\n        if not node:parent():parent():type():match(\"_list%d$\") then\n            return\n        end\n\n        return {\n            output = module.config.public.extensions[\"todo-items-extended\"] and replace_text or nil,\n            state = {\n                weak_indent = state.weak_indent + replace_text:len(),\n            },\n        }\n    end\nend\n\nlocal function get_metadata_array_prefix(node, state)\n    return node:parent():type() == \"array\" and string.rep(\" \", state.indent) .. \"- \" or \"\"\nend\n\nlocal function handle_metadata_literal(text, node, state)\n    -- If the parent is an array, we need to indent it and add the `- ` prefix. Otherwise, there will be a key right before which will take care of indentation\n    return get_metadata_array_prefix(node, state) .. text .. \"\\n\"\nend\n\nlocal function update_indent(value)\n    return function(_, _, state)\n        return {\n            state = {\n                indent = state.indent + value,\n            },\n        }\n    end\nend\n\n--> Recollector Utility Functions\n\nlocal function todo_item_recollector()\n    return function(output)\n        return output[2] ~= \"(_) \" and output\n    end\nend\n\nlocal function handle_heading_newlines()\n    return function(output, _, node, ts)\n        local prev = ts.get_previous_node(node, true, true)\n\n        if\n            prev\n            and not vim.tbl_contains({ \"_line_break\", \"_paragraph_break\" }, prev:type())\n            and ((prev:end_()) + 1) ~= (node:start())\n        then\n            output[1] = \"\\n\" .. output[1]\n        end\n\n        if output[3] then\n            output[3] = output[3] .. \"\\n\"\n        end\n\n        return output\n    end\nend\n\nlocal function handle_metadata_composite_element(empty_element)\n    return function(output, state, node)\n        if vim.tbl_isempty(output) then\n            return { get_metadata_array_prefix(node, state), empty_element, \"\\n\" }\n        end\n        local parent = node:parent():type()\n        if parent == \"array\" then\n            -- If the parent is an array, we need to splice an extra `-` prefix to the first element\n            output[1] = output[1]:sub(1, state.indent) .. \"-\" .. output[1]:sub(state.indent + 2)\n        elseif parent == \"pair\" then\n            -- If the parent is a pair, the first element should be on the next line\n            output[1] = \"\\n\" .. output[1]\n        end\n        return output\n    end\nend\n\n---\n\nmodule.load = function()\n    if module.config.public.extensions == \"all\" then\n        module.config.public.extensions = {\n            \"todo-items-basic\",\n            \"todo-items-pending\",\n            \"todo-items-extended\",\n            \"definition-lists\",\n            \"mathematics\",\n            \"metadata\",\n            \"latex\",\n        }\n    end\n\n    module.config.public.extensions = lib.to_keys(module.config.public.extensions, {})\nend\n\nmodule.config.public = {\n    -- Any extensions you may want to use when exporting to markdown. By\n    -- default no extensions are loaded (the exporter is commonmark compliant).\n    -- You can also set this value to `\"all\"` to enable all extensions.\n    -- The full extension list is: `todo-items-basic`, `todo-items-pending`, `todo-items-extended`,\n    -- `definition-lists`, `mathematics`, `metadata` and `latex`.\n    extensions = {},\n\n    -- Data about how to render mathematics.\n    -- The default is recommended as it is the most common, although certain flavours\n    -- of markdown use different syntax.\n    mathematics = {\n        -- Inline mathematics are represented `$like this$`.\n        inline = {\n            start = \"$\",\n            [\"end\"] = \"$\",\n        },\n        -- Block-level mathematics are represented as such:\n        --\n        -- ```md\n        -- $$\n        -- \\frac{3, 2}\n        -- $$\n        -- ```\n        block = {\n            start = \"$$\",\n            [\"end\"] = \"$$\",\n        },\n    },\n\n    -- Data about how to render metadata\n    -- There are a few ways to render metadata blocks, but this is the most\n    -- common.\n    metadata = {\n        start = \"---\",\n        [\"end\"] = \"---\", -- Is usually also \"...\"\n    },\n\n    -- Used by the exporter to know what extension to use\n    -- when creating markdown files.\n    -- The default is recommended, although you can change it.\n    extension = \"md\",\n}\n\n--- @class core.export.markdown\nmodule.public = {\n    export = {\n        init_state = function()\n            return {\n                weak_indent = 0,\n                indent = 0,\n                ordered_list_level = {\n                    0,\n                    0,\n                    0,\n                    0,\n                    0,\n                    0,\n                },\n                tag_indent = 0,\n                tag_close = nil,\n                ranged_tag_indentation_level = 0,\n                is_url = false,\n                footnote_count = 0,\n            }\n        end,\n\n        functions = {\n\n            [\"single_footnote\"] = function(_, node, state)\n                state[\"footnote_count\"] = state[\"footnote_count\"] + 1\n                for nd in node:iter_children() do\n                    if nd:type() == \"paragraph\" then\n                        local n = state[\"footnote_count\"]\n                        return \"[^\"\n                            .. n\n                            .. \"]\\n\\n\\n[^\"\n                            .. n\n                            .. \"]: \"\n                            .. module.required[\"core.integrations.treesitter\"].get_node_text(nd)\n                    end\n                end\n                return \"\"\n            end,\n\n            [\"_word\"] = true,\n            [\"_space\"] = true,\n\n            [\"_line_break\"] = function(_, node, state)\n                local next_sibling = node:next_sibling()\n\n                return \"\\n\"\n                    .. ((next_sibling and next_sibling:type() == \"paragraph_segment\" and string.rep(\n                        \" \",\n                        state.weak_indent\n                    )) or \"\")\n                    .. string.rep(\" \", state.indent)\n            end,\n\n            [\"_paragraph_break\"] = function(newlines, _, state)\n                return {\n                    output = string.rep(\"\\n\\n\", newlines:len()) .. string.rep(\" \", state.indent),\n                    state = {\n                        weak_indent = 0,\n                        ordered_list_level = { 0, 0, 0, 0, 0, 0 },\n                    },\n                }\n            end,\n\n            [\"_segment\"] = function(text, node, state)\n                return string.rep(\" \", state.indent + (({ node:range() })[2] - state.ranged_tag_indentation_level))\n                    .. text\n            end,\n\n            [\"heading1_prefix\"] = \"# \",\n            [\"heading2_prefix\"] = \"## \",\n            [\"heading3_prefix\"] = \"### \",\n            [\"heading4_prefix\"] = \"#### \",\n            [\"heading5_prefix\"] = \"##### \",\n            [\"heading6_prefix\"] = \"###### \",\n\n            [\"_open\"] = function(_, node)\n                local type = node:parent():type()\n\n                if type == \"bold\" then\n                    return \"**\"\n                elseif type == \"italic\" then\n                    return \"_\"\n                elseif type == \"underline\" then\n                    return \"<u>\"\n                elseif type == \"strikethrough\" then\n                    return \"~~\"\n                elseif type == \"spoiler\" then\n                    return \"|\"\n                elseif type == \"verbatim\" then\n                    return \"`\"\n                elseif type == \"superscript\" then\n                    return \"<sup>\"\n                elseif type == \"subscript\" then\n                    return \"<sub>\"\n                elseif type == \"inline_comment\" then\n                    return \"<!-- \"\n                elseif type == \"inline_math\" and module.config.public.extensions[\"mathematics\"] then\n                    return module.config.public.mathematics.inline[\"start\"]\n                end\n            end,\n\n            [\"_close\"] = function(_, node)\n                local type = node:parent():type()\n\n                if type == \"bold\" then\n                    return \"**\"\n                elseif type == \"italic\" then\n                    return \"_\"\n                elseif type == \"underline\" then\n                    return \"</u>\"\n                elseif type == \"strikethrough\" then\n                    return \"~~\"\n                elseif type == \"spoiler\" then\n                    return \"|\"\n                elseif type == \"verbatim\" then\n                    return \"`\"\n                elseif type == \"superscript\" then\n                    return \"</sup>\"\n                elseif type == \"subscript\" then\n                    return \"</sub>\"\n                elseif type == \"inline_comment\" then\n                    return \" -->\"\n                elseif type == \"inline_math\" and module.config.public.extensions[\"mathematics\"] then\n                    return module.config.public.mathematics.inline[\"end\"]\n                end\n            end,\n\n            [\"_begin\"] = function(text, node)\n                local type = node:parent():type()\n\n                if type == \"link_location\" then\n                    return text == \"{\" and \"(\"\n                elseif type == \"link_description\" then\n                    return \"[\"\n                end\n            end,\n\n            [\"_end\"] = function(text, node)\n                local type = node:parent():type()\n\n                if type == \"link_location\" then\n                    return text == \"}\" and \")\"\n                elseif type == \"link_description\" then\n                    return \"]\"\n                end\n            end,\n\n            [\"link_file_text\"] = function(text)\n                return vim.uri_from_fname(text .. \".md\"):sub(string.len(\"file://\") + 1)\n            end,\n\n            [\"link_target_url\"] = function()\n                return {\n                    state = {\n                        is_url = true,\n                    },\n                }\n            end,\n\n            [\"escape_sequence\"] = function(text)\n                local escaped_char = text:sub(-1)\n                return escaped_char:match(\"%p\") and text or escaped_char\n            end,\n\n            [\"unordered_list1_prefix\"] = unordered_list_prefix(1),\n            [\"unordered_list2_prefix\"] = unordered_list_prefix(2),\n            [\"unordered_list3_prefix\"] = unordered_list_prefix(3),\n            [\"unordered_list4_prefix\"] = unordered_list_prefix(4),\n            [\"unordered_list5_prefix\"] = unordered_list_prefix(5),\n            [\"unordered_list6_prefix\"] = unordered_list_prefix(6),\n\n            [\"ordered_list1_prefix\"] = ordered_list_prefix(1),\n            [\"ordered_list2_prefix\"] = ordered_list_prefix(2),\n            [\"ordered_list3_prefix\"] = ordered_list_prefix(3),\n            [\"ordered_list4_prefix\"] = ordered_list_prefix(4),\n            [\"ordered_list5_prefix\"] = ordered_list_prefix(5),\n            [\"ordered_list6_prefix\"] = ordered_list_prefix(6),\n\n            [\"tag_parameters\"] = function(text, _, state)\n                if state.ignore_tag_parameters then\n                    state.ignore_tag_parameters = nil\n                    return {\n                        output = \"\",\n                        state = state,\n                    }\n                end\n\n                return text\n            end,\n\n            [\"tag_name\"] = function(text, node, _, _)\n                local _, tag_start_column = node:range()\n\n                if text == \"code\" then\n                    return {\n                        output = \"```\",\n                        state = {\n                            -- Minus one to account for the `@`\n                            tag_indent = tag_start_column - 1,\n                            tag_close = \"```\",\n                        },\n                    }\n                elseif text == \"comment\" then\n                    return {\n                        output = \"<!--\",\n                        state = {\n                            tag_indent = tag_start_column - 1,\n                            tag_close = \"-->\",\n                        },\n                    }\n                elseif text == \"table\" then\n                    return {\n                        output = \"\",\n                        state = {\n                            tag_indent = tag_start_column - 1,\n                            tag_close = \"\",\n                        },\n                    }\n                elseif text == \"math\" and module.config.public.extensions[\"mathematics\"] then\n                    return {\n                        output = module.config.public.mathematics.block[\"start\"],\n                        state = {\n                            tag_indent = tag_start_column - 1,\n                            tag_close = module.config.public.mathematics.block[\"end\"],\n                        },\n                    }\n                elseif text == \"document.meta\" then\n                    local allows_metadata = module.config.public.extensions[\"metadata\"]\n\n                    return {\n                        output = allows_metadata and module.config.public.metadata[\"start\"] or nil,\n                        state = {\n                            tag_indent = tag_start_column - 1,\n                            tag_close = allows_metadata and module.config.public.metadata[\"end\"] or nil,\n                            is_meta = true,\n                        },\n                    }\n                elseif\n                    text == \"embed\"\n                    and node:next_named_sibling()\n                    and vim.tbl_contains(\n                        { \"markdown\", \"html\", module.config.public.extensions[\"latex\"] and \"latex\" or nil },\n                        module.required[\"core.integrations.treesitter\"].get_node_text(node:next_named_sibling())\n                    )\n                then\n                    return {\n                        state = {\n                            tag_indent = tag_start_column - 1,\n                            tag_close = \"\",\n                            ignore_tag_parameters = true,\n                        },\n                    }\n                end\n\n                return {\n                    state = {\n                        ignore_tag_parameters = true,\n                        tag_close = nil,\n                    },\n                }\n            end,\n\n            [\"ranged_verbatim_tag_content\"] = function(text, node, state)\n                if state.is_meta then\n                    state.is_meta = false\n                    if module.config.public.extensions[\"metadata\"] then\n                        return {\n                            keep_descending = true,\n                            state = {\n                                parse_as = \"norg_meta\",\n                            },\n                        }\n                    else\n                        return\n                    end\n                end\n\n                local _, ranged_tag_content_column_start = node:range()\n\n                local split_text = vim.split(text, \"\\n\")\n\n                split_text[1] = string.rep(\" \", ranged_tag_content_column_start - state.tag_indent) .. split_text[1]\n\n                for i = 2, #split_text do\n                    split_text[i] = split_text[i]:sub(state.tag_indent + 1)\n                end\n\n                return state.tag_close and (table.concat(split_text, \"\\n\") .. \"\\n\")\n            end,\n\n            [\"ranged_verbatim_tag_end\"] = function(_, _, state)\n                local tag_close = state.tag_close\n                state.tag_close = nil\n                return tag_close\n            end,\n\n            [\"quote1_prefix\"] = true,\n            [\"quote2_prefix\"] = true,\n            [\"quote3_prefix\"] = true,\n            [\"quote4_prefix\"] = true,\n            [\"quote5_prefix\"] = true,\n            [\"quote6_prefix\"] = true,\n\n            [\"todo_item_done\"] = function(_, node, state)\n                if not node:parent():parent():type():match(\"_list%d$\") then\n                    return\n                end\n\n                return {\n                    output = module.config.public.extensions[\"todo-items-basic\"] and \"[x]\",\n                    state = {\n                        weak_indent = state.weak_indent + 4,\n                    },\n                }\n            end,\n\n            [\"todo_item_undone\"] = function(_, node, state)\n                if not node:parent():parent():type():match(\"_list%d$\") then\n                    return\n                end\n\n                return {\n                    output = module.config.public.extensions[\"todo-items-basic\"] and \"[ ]\",\n                    state = {\n                        weak_indent = state.weak_indent + 4,\n                    },\n                }\n            end,\n\n            [\"todo_item_pending\"] = function(_, node, state)\n                if not node:parent():parent():type():match(\"_list%d$\") then\n                    return\n                end\n\n                return {\n                    output = module.config.public.extensions[\"todo-items-pending\"] and \"[*]\",\n                    state = {\n                        weak_indent = state.weak_indent + 4,\n                    },\n                }\n            end,\n\n            [\"todo_item_urgent\"] = todo_item_extended(\"[ ]\"),\n            [\"todo_item_cancelled\"] = todo_item_extended(\"[_]\"),\n            [\"todo_item_recurring\"] = todo_item_extended(\"[ ]\"),\n            [\"todo_item_on_hold\"] = todo_item_extended(\"[ ]\"),\n            [\"todo_item_uncertain\"] = todo_item_extended(\"[ ]\"),\n\n            [\"single_definition_prefix\"] = function()\n                return module.config.public.extensions[\"definition-lists\"] and \": \"\n            end,\n\n            [\"multi_definition_prefix\"] = function(_, _, state)\n                if not module.config.public.extensions[\"definition-lists\"] then\n                    return\n                end\n\n                return {\n                    output = \": \",\n                    state = {\n                        indent = state.indent + 2,\n                    },\n                }\n            end,\n\n            [\"multi_definition_suffix\"] = function(_, _, state)\n                if not module.config.public.extensions[\"definition-lists\"] then\n                    return\n                end\n\n                return {\n                    state = {\n                        indent = state.indent - 2,\n                    },\n                }\n            end,\n\n            [\"_prefix\"] = function(_, node)\n                return {\n                    state = {\n                        ranged_tag_indentation_level = ({ node:range() })[2],\n                    },\n                }\n            end,\n\n            [\"capitalized_word\"] = function(text, node)\n                if node:parent():type() == \"insertion\" then\n                    if text == \"Image\" then\n                        return \"![\"\n                    end\n                end\n            end,\n\n            [\"strong_carryover\"] = \"\",\n            [\"weak_carryover\"] = \"\",\n\n            [\"key\"] = function(text, _, state)\n                return string.rep(\" \", state.indent) .. (text == \"authors\" and \"author\" or text)\n            end,\n\n            [\":\"] = \": \",\n\n            [\"[\"] = update_indent(2),\n            [\"]\"] = update_indent(-2),\n            [\"{\"] = update_indent(2),\n            [\"}\"] = update_indent(-2),\n\n            [\"string\"] = handle_metadata_literal,\n            [\"number\"] = handle_metadata_literal,\n            [\"horizontal_line\"] = \"___\",\n        },\n\n        recollectors = {\n            [\"link_location\"] = function(output, state)\n                last_parsed_link_location = output[#output - 1]\n\n                if state.is_url then\n                    state.is_url = false\n                    return output\n                end\n\n                table.insert(output, #output - 1, \"#\")\n                output[#output - 1] = output[#output - 1]:lower():gsub(\"-\", \" \"):gsub(\"%p+\", \"\"):gsub(\"%s+\", \"-\")\n\n                return output\n            end,\n\n            [\"link\"] = function(output)\n                return {\n                    output[2] or (\"[\" .. last_parsed_link_location .. \"]\"),\n                    output[1],\n                }\n            end,\n\n            [\"ranged_verbatim_tag\"] = function(output)\n                if output[2] and output[2]:match(\"^[ \\t]+$\") then\n                    table.remove(output, 2)\n                end\n\n                return output\n            end,\n\n            [\"unordered_list1\"] = todo_item_recollector(),\n            [\"unordered_list2\"] = todo_item_recollector(),\n            [\"unordered_list3\"] = todo_item_recollector(),\n            [\"unordered_list4\"] = todo_item_recollector(),\n            [\"unordered_list5\"] = todo_item_recollector(),\n            [\"unordered_list6\"] = todo_item_recollector(),\n\n            [\"single_definition\"] = function(output)\n                return {\n                    output[2],\n                    output[3],\n                    output[1],\n                    output[4],\n                }\n            end,\n\n            [\"multi_definition\"] = function(output)\n                output[3] = output[3]:gsub(\"^\\n+  \", \"\\n\") .. output[1]\n                table.remove(output, 1)\n\n                return output\n            end,\n\n            -- TODO\n            [\"insertion\"] = function(output)\n                if output[1] == \"![\" then\n                    table.insert(output, 1, \"\\n\")\n\n                    local split = vim.split(output[3], \"/\", { plain = true })\n                    table.insert(output, 3, (split[#split]:match(\"^(.+)%..+$\") or split[#split]) .. \"](\")\n                    table.insert(output, \")\\n\")\n                end\n\n                return output\n            end,\n\n            [\"heading1\"] = handle_heading_newlines(),\n            [\"heading2\"] = handle_heading_newlines(),\n            [\"heading3\"] = handle_heading_newlines(),\n            [\"heading4\"] = handle_heading_newlines(),\n            [\"heading5\"] = handle_heading_newlines(),\n            [\"heading6\"] = handle_heading_newlines(),\n\n            [\"object\"] = handle_metadata_composite_element(\"{}\"),\n            [\"array\"] = handle_metadata_composite_element(\"[]\"),\n        },\n\n        cleanup = function()\n            last_parsed_link_location = \"\"\n        end,\n    },\n}\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/export/module.lua",
    "content": "--[[\n    file: Exporting-Files\n    title: Convert Neorg Files to other Filetypes with `core.export`\n    summary: Exports Neorg documents into any other supported filetype.\n    ---\nWhen sending files to people not invested in Neorg it's nice to be able to use a format\nthat *they* use. The `core.export` module is a backend module providing functionality\nto write your own exporter for any given filetype.\n\nAll export functionality is provided by the `:Neorg export` command.\n\nTo export the currently opened buffer to another file format, you should use the `:Neorg export to-file` command.\nThe command takes the following arguments:\n- `path` - the path to export to. Examples are: `my-file.md`, `~/output.md`.\n  If the second argument is not provided Neorg will try to infer the filetype to convert to through\n  the file extension.\n- `filetype` (optional) - the filetype to export to. Useful if you want to use a non-standard extension, or\n  if the filetype you're using cannot be inferred automatically. Note that this filetype *must* be a filetype\n  that Neovim itself provides and/or understands, i.e. `md` or `markd` is not a valid filetype, however `markdown` is.\n\nNeorg also supports exporting a directory of files: this is where the `:Neorg export directory` command comes into play.\nIt takes 3 arguments:\n- `directory` - the directory to export\n- `filetype` - the filetype to export to\n- `output-dir` (optional) - a custom output directory to use. If not provided will fall back to `config.public.export_dir`\n  (see [configuration](#configuration)).\n\nAnd if you just want to export a snippet from a single document, you can use `:Neorg export\nto-clipboard <filetype>`. Filetype is required, at the time of writing the only option is \"markdown\".\nThis copies the range given to the command to the `+` register.\n--]]\n\nlocal neorg = require(\"neorg.core\")\nlocal lib, log, modules, utils = neorg.lib, neorg.log, neorg.modules, neorg.utils\nlocal module = modules.create(\"core.export\")\n\nmodule.setup = function()\n    return {\n        success = true,\n        requires = {\n            \"core.integrations.treesitter\",\n        },\n    }\nend\n\n---@type core.integrations.treesitter\nlocal ts\n\nmodule.load = function()\n    ts = module.required[\"core.integrations.treesitter\"]\n\n    modules.await(\"core.neorgcmd\", function(neorgcmd)\n        neorgcmd.add_commands_from_table({\n            export = {\n                args = 1,\n                condition = \"norg\",\n\n                subcommands = {\n                    [\"to-file\"] = {\n                        min_args = 1,\n                        max_args = 2,\n                        name = \"export.to-file\",\n                    },\n                    [\"directory\"] = {\n                        min_args = 2,\n                        max_args = 3,\n                        name = \"export.directory\",\n                    },\n                    [\"to-clipboard\"] = {\n                        args = 1,\n                        name = \"export.to-clipboard\",\n                    },\n                },\n            },\n        })\n    end)\nend\n\nmodule.config.public = {\n    -- The directory to export to when running `:Neorg export directory`.\n    -- The string can be formatted with the special keys: `<export-dir>` and `<language>`.\n    export_dir = \"<export-dir>/<language>-export\",\n}\n\n---@class core.export\nmodule.public = {\n    --- Returns a module that can handle conversion from `.norg` to the target filetype\n    ---@param ftype string #The filetype to export to\n    ---@return table?,table? #The export module and its configuration, else nil\n    get_converter = function(ftype)\n        if not modules.is_module_loaded(\"core.export.\" .. ftype) then\n            if not modules.load_module(\"core.export.\" .. ftype) then\n                return\n            end\n        end\n\n        return modules.get_module(\"core.export.\" .. ftype), modules.get_module_config(\"core.export.\" .. ftype)\n    end,\n\n    ---export part of a buffer\n    ---@param buffer number\n    ---@param start_row number 1 indexed\n    ---@param end_row number 1 indexed, inclusive\n    ---@param filetype string\n    ---@return string? content, string? extension exported content as a string, and the extension\n    ---used for the export\n    export_range = function(buffer, start_row, end_row, filetype)\n        local converter = module.private.get_converter_checked(filetype)\n        if not converter then\n            return\n        end\n        local content = vim.iter(vim.api.nvim_buf_get_lines(buffer, start_row - 1, end_row, false)):join(\"\\n\")\n        local root = ts.get_document_root(content)\n        if not root then\n            return\n        end\n\n        return module.public.export_from_root(root, converter, content)\n    end,\n\n    --- Takes a buffer and exports it to a specific file\n    ---@param buffer number #The buffer ID to read the contents from\n    ---@param filetype string #A Neovim filetype to specify which language to export to\n    ---@return string?, string? #The entire buffer parsed, converted and returned as a string, as well as the extension used for the export.\n    export = function(buffer, filetype)\n        local converter, converter_config = module.private.get_converter_checked(filetype)\n        if not converter or not converter_config then\n            return\n        end\n\n        local document_root = ts.get_document_root(buffer)\n\n        if not document_root then\n            return\n        end\n\n        local content = module.public.export_from_root(document_root, converter, buffer)\n\n        return content, converter_config.extension\n    end,\n\n    ---Do the work of exporting the given TS node via the given converter\n    ---@param root TSNode\n    ---@param converter table\n    ---@param source number | string\n    ---@return string\n    export_from_root = function(root, converter, source)\n        -- Initialize the state. The state is a table that exists throughout the entire duration\n        -- of the export, and can be used to e.g. retain indent levels and/or keep references.\n        local state = converter.export.init_state and converter.export.init_state() or {}\n\n        --- Descends down a node and its children\n        ---@param start table #The TS node to begin at\n        ---@return string #The exported/converted node as a string\n        local function descend(start)\n            -- We do not want to parse erroneous nodes, so we skip them instead\n            if start:type() == \"ERROR\" then\n                return \"\"\n            end\n\n            local output = {}\n\n            for node in start:iter_children() do\n                -- See if there is a conversion function for the specific node type we're dealing with\n                local exporter = converter.export.functions[node:type()]\n\n                if exporter then\n                    -- The value of `exporter` can be of 3 different types:\n                    --  a function, in which case it should be executed\n                    --  a boolean (true), which signifies to use the content of the node as-is without changing anything\n                    --  a string, in which case every time the node is encountered it will always be converted to a static value\n                    if type(exporter) == \"function\" then\n                        -- An exporter function can return output string or table with 3 values:\n                        --  `output` - the converted text\n                        --  `keep_descending`  - if true will continue to recurse down the current node's children despite the current\n                        --                      node already being parsed\n                        --  `state`   - a modified version of the state that then gets merged into the main state table\n                        local result = exporter(vim.treesitter.get_node_text(node, source), node, state, ts)\n\n                        if type(result) == \"table\" then\n                            state = result.state and vim.tbl_extend(\"force\", state, result.state) or state\n\n                            if result.output then\n                                table.insert(output, result.output)\n                            end\n\n                            if result.keep_descending then\n                                if state.parse_as then\n                                    node = ts.get_document_root(\n                                        \"\\n\" .. vim.treesitter.get_node_text(node, source),\n                                        state.parse_as\n                                    )\n                                    if not node then\n                                        goto continue\n                                    end\n                                    state.parse_as = nil\n                                end\n\n                                local ret = descend(node)\n\n                                if ret then\n                                    table.insert(output, ret)\n                                end\n                            end\n                        elseif type(result) == \"string\" then\n                            table.insert(output, result)\n                        end\n                    elseif exporter == true then\n                        table.insert(output, ts.get_node_text(node, source))\n                    else\n                        table.insert(output, exporter)\n                    end\n                else -- If no exporter exists for the current node then keep descending\n                    local ret = descend(node)\n\n                    if ret then\n                        table.insert(output, ret)\n                    end\n                end\n                ::continue::\n            end\n\n            -- Recollectors exist to collect all the converted children nodes of a parent node\n            -- and to optionally rearrange them into a new layout. Consider the following Neorg markup:\n            --  $ Term\n            --    Definition\n            -- The markdown version looks like this:\n            --  Term\n            --  : Definition\n            -- Without a recollector such a conversion wouldn't be possible, as by simply converting each\n            -- node individually you'd end up with:\n            --  : Term\n            --    Definition\n            --\n            -- The recollector can encounter a `definition` node, see the nodes it is made up of ({ \": \", \"Term\", \"Definition\" })\n            -- and rearrange its components to { \"Term\", \": \", \"Definition\" } to then achieve the desired result.\n            local recollector = converter.export.recollectors[start:type()]\n\n            return recollector and table.concat(recollector(output, state, start, ts) or {})\n                or (not vim.tbl_isempty(output) and table.concat(output))\n        end\n\n        local output = descend(root)\n\n        -- Every converter can also come with a `cleanup` function that performs some final tweaks to the output string\n        return converter.export.cleanup and converter.export.cleanup(output, state) or output\n    end,\n}\n\nmodule.private = {\n    ---get the converter for the given filetype\n    ---@param filetype string\n    ---@return table?, table?\n    get_converter_checked = function(filetype)\n        local converter, converter_config = module.public.get_converter(filetype)\n\n        if not converter or not converter_config then\n            log.error(\"Unable to export file - did not find exporter for filetype '\" .. filetype .. \"'.\")\n            return\n        end\n\n        -- Each converter must have a `extension` field in its public config\n        -- This is done to do a backwards lookup, e.g. `markdown` uses the `.md` file extension.\n        if not converter_config.extension then\n            log.error(\n                \"Unable to export file - exporter for filetype '\"\n                    .. filetype\n                    .. \"' did not return a preferred extension. The exporter is unable to infer extensions.\"\n            )\n            return\n        end\n\n        return converter, converter_config\n    end,\n}\n\n---@param event neorg.event\nmodule.on_event = function(event)\n    if event.type == \"core.neorgcmd.events.export.to-file\" then\n        -- Syntax: Neorg export to-file file.extension forced-filetype?\n        -- Example: Neorg export to-file my-custom-file markdown\n\n        local filepath = vim.fn.expand(event.content[1])\n        local filetype = event.content[2] or vim.filetype.match({ filename = filepath })\n        local exported = module.public.export(event.buffer, filetype)\n\n        vim.loop.fs_open(filepath, \"w\", 438, function(err, fd)\n            assert(not err, lib.lazy_string_concat(\"Failed to open file '\", filepath, \"' for export: \", err))\n            assert(fd)\n\n            vim.loop.fs_write(fd, exported, 0, function(werr)\n                assert(not werr, lib.lazy_string_concat(\"Failed to write to file '\", filepath, \"' for export: \", werr))\n            end)\n\n            vim.schedule(lib.wrap(utils.notify, \"Successfully exported 1 file!\"))\n        end)\n    elseif event.type == \"core.neorgcmd.events.export.to-clipboard\" then\n        -- Syntax: Neorg export to-clipboard filetype\n        -- Example: Neorg export to-clipboard markdown\n\n        local filetype = event.content[1]\n        local data = event.content.data\n        local exported = module.public.export_range(event.buffer, data.line1, data.line2, filetype)\n\n        vim.fn.setreg(\"+\", exported, \"l\")\n    elseif event.type == \"core.neorgcmd.events.export.directory\" then\n        local path = event.content[3] and vim.fn.expand(event.content[3])\n            or module.config.public.export_dir\n                :gsub(\"<language>\", event.content[2])\n                :gsub(\"<export%-dir>\", event.content[1])\n        vim.fn.mkdir(path, \"p\")\n\n        -- The old value of `eventignore` is stored here. This is done because the eventignore\n        -- value is set to ignore BufEnter events before loading all the Neorg buffers, as they can mistakenly\n        -- activate the concealer, which not only slows down performance notably but also causes errors.\n        ---@diagnostic disable-next-line: undefined-field\n        local old_event_ignore = table.concat(vim.opt.eventignore:get(), \",\")\n\n        vim.loop.fs_scandir(event.content[1], function(err, handle)\n            assert(not err, lib.lazy_string_concat(\"Failed to scan directory '\", event.content[1], \"': \", err))\n            assert(handle)\n\n            local file_counter, parsed_counter = 0, 0\n\n            while true do\n                local name, type = vim.loop.fs_scandir_next(handle)\n\n                if not name then\n                    break\n                end\n\n                if type == \"file\" and vim.endswith(name, \".norg\") then\n                    file_counter = file_counter + 1\n\n                    local function check_counters()\n                        parsed_counter = parsed_counter + 1\n\n                        if parsed_counter >= file_counter then\n                            vim.schedule(\n                                lib.wrap(utils.notify, string.format(\"Successfully exported %d files!\", file_counter))\n                            )\n                        end\n                    end\n\n                    vim.schedule(function()\n                        local filepath = vim.fn.expand(event.content[1]) .. \"/\" .. name\n\n                        vim.opt.eventignore = \"BufEnter\"\n\n                        local buffer = assert(vim.fn.bufadd(filepath))\n                        vim.fn.bufload(buffer)\n\n                        vim.opt.eventignore = old_event_ignore\n\n                        local exported, extension = module.public.export(buffer, event.content[2])\n\n                        vim.api.nvim_buf_delete(buffer, { force = true })\n\n                        if not exported then\n                            check_counters()\n                            return\n                        end\n\n                        local write_path = path .. \"/\" .. name:gsub(\"%.%a+$\", \".\" .. extension)\n\n                        vim.loop.fs_open(write_path, \"w+\", 438, function(fs_err, fd)\n                            assert(\n                                not fs_err,\n                                lib.lazy_string_concat(\"Failed to open file '\", write_path, \"' for export: \", fs_err)\n                            )\n                            assert(fd)\n\n                            vim.loop.fs_write(fd, exported, 0, function(werr)\n                                assert(\n                                    not werr,\n                                    lib.lazy_string_concat(\n                                        \"Failed to write to file '\",\n                                        write_path,\n                                        \"' for export: \",\n                                        werr\n                                    )\n                                )\n\n                                check_counters()\n                            end)\n                        end)\n                    end)\n                end\n            end\n        end)\n    end\n\n    vim.api.nvim_exec_autocmds(\"User\", {\n        pattern = \"NeorgExportComplete\",\n    })\nend\n\nmodule.events.subscribed = {\n    [\"core.neorgcmd\"] = {\n        [\"export.to-file\"] = true,\n        [\"export.to-clipboard\"] = true,\n        [\"export.directory\"] = true,\n    },\n}\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/fs/module.lua",
    "content": "--[[\n    file: Filesystem\n    title: Module for Filesystem Operations\n    summary: A cross-platform set of utilities to traverse filesystems.\n    internal: true\n    ---\n`core.fs` is a small module providing functionality to perform common\noperations safely on arbitrary filesystems.\n--]]\n\nlocal neorg = require(\"neorg.core\")\nlocal modules = neorg.modules\n\nlocal module = modules.create(\"core.fs\")\n\n---@class core.fs\nmodule.public = {\n    directory_map = function(path, callback)\n        for name, type in vim.fs.dir(path) do\n            if type == \"directory\" then\n                module.public.directory_map(table.concat({ path, \"/\", name }), callback)\n            else\n                callback(name, type, path)\n            end\n        end\n    end,\n\n    --- Recursively copies a directory from one path to another\n    ---@param old_path string #The path to copy\n    ---@param new_path string #The new location. This function will not\n    --- succeed if the directory already exists.\n    ---@return boolean #If true, the directory copying succeeded\n    copy_directory = function(old_path, new_path)\n        local file_permissions = tonumber(\"744\", 8)\n        local ok, err = vim.loop.fs_mkdir(new_path, file_permissions)\n\n        if not ok then\n            return ok, err ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n        end\n\n        for name, type in vim.fs.dir(old_path) do\n            if type == \"file\" then\n                ok, err =\n                    vim.loop.fs_copyfile(table.concat({ old_path, \"/\", name }), table.concat({ new_path, \"/\", name }))\n\n                if not ok then\n                    return ok, err ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n                end\n            elseif type == \"directory\" and not vim.endswith(new_path, name) then\n                ok, err = module.public.copy_directory(\n                    table.concat({ old_path, \"/\", name }),\n                    table.concat({ new_path, \"/\", name })\n                )\n\n                if not ok then\n                    return ok, err ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n                end\n            end\n        end\n\n        return true, nil ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n    end,\n}\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/highlights/module.lua",
    "content": "--[[\n    file: Core-Highlights\n    title: No Colour Means no Productivity\n    summary: Manages your highlight groups with this module.\n    internal: true\n    ---\n`core.highlights` maps all possible highlight groups available throughout\nNeorg under a single tree of highlights: `@neorg.*`.\n--]]\n\nlocal neorg = require(\"neorg.core\")\nlocal lib, log, modules = neorg.lib, neorg.log, neorg.modules\n\nlocal module = modules.create(\"core.highlights\")\n\n--[[\n--]]\nmodule.config.public = {\n    -- The TS highlights for each Neorg type.\n    --\n    -- The `highlights` table is a large collection of nested trees. At the leaves of each of these\n    -- trees is the final highlight to apply to that tree. For example: `\"+@comment\"` tells Neorg to\n    -- link to an existing highlight group `@comment` (denoted by the `+` prefix). When no prefix is\n    -- found, the string is treated as arguments passed to `:highlight`, for example: `gui=bold\n    -- fg=#000000`.\n    --\n    -- Nested trees concatenate, thus:\n    -- ```lua\n    -- tags = {\n    --     ranged_verbatim = {\n    --         begin = \"+@comment\",\n    --     },\n    -- }\n    -- ```\n    -- matches the highlight group:\n    -- ```lua\n    -- @neorg.tags.ranged_verbatim.begin\n    -- ```\n    -- and converts into the following command:\n    -- ```vim\n    -- highlight! link @neorg.tags.ranged_verbatim.begin @comment\n    -- ```\n    highlights = {\n        -- Highlights displayed in Neorg selection window popups.\n        selection_window = {\n            heading = \"+@annotation\",\n            arrow = \"+@none\",\n            key = \"+@module\",\n            keyname = \"+@constant\",\n            nestedkeyname = \"+@string\",\n        },\n\n        -- Highlights displayed in all sorts of tag types.\n        --\n        -- These include: `@`, `.`, `|`, `#`, `+` and `=`.\n        tags = {\n            -- Highlights for the `@` verbatim tags.\n            ranged_verbatim = {\n                begin = \"+@keyword\",\n\n                [\"end\"] = \"+@keyword\",\n\n                name = {\n                    [\"\"] = \"+@none\",\n                    delimiter = \"+@none\",\n                    word = \"+@keyword\",\n                },\n\n                parameters = \"+@type\",\n\n                document_meta = {\n                    key = \"+@variable.member\",\n                    value = \"+@string\",\n                    number = \"+@number\",\n                    trailing = \"+@keyword.repeat\",\n                    title = \"+@markup.heading\",\n                    description = \"+@label\",\n                    authors = \"+@annotation\",\n                    categories = \"+@keyword\",\n                    created = \"+@number.float\",\n                    updated = \"+@number.float\",\n                    version = \"+@number.float\",\n\n                    object = {\n                        bracket = \"+@punctuation.bracket\",\n                    },\n\n                    array = {\n                        bracket = \"+@punctuation.bracket\",\n                        value = \"+@none\",\n                    },\n                },\n            },\n\n            -- Highlights for the carryover (`#`, `+`) tags.\n            carryover = {\n                begin = \"+@label\",\n\n                name = {\n                    [\"\"] = \"+@none\",\n                    word = \"+@label\",\n                    delimiter = \"+@none\",\n                },\n\n                parameters = \"+@string\",\n            },\n\n            -- Highlights for the content of any tag named `comment`.\n            --\n            -- Most prominent use case is for the `#comment` carryover tag.\n            comment = {\n                content = \"+@comment\",\n            },\n        },\n\n        -- Highlights for each individual heading level.\n        headings = {\n            [\"1\"] = {\n                title = \"+@attribute\",\n                prefix = \"+@attribute\",\n            },\n            [\"2\"] = {\n                title = \"+@label\",\n                prefix = \"+@label\",\n            },\n            [\"3\"] = {\n                title = \"+@constant\",\n                prefix = \"+@constant\",\n            },\n            [\"4\"] = {\n                title = \"+@string\",\n                prefix = \"+@string\",\n            },\n            [\"5\"] = {\n                title = \"+@label\",\n                prefix = \"+@label\",\n            },\n            [\"6\"] = {\n                title = \"+@constructor\",\n                prefix = \"+@constructor\",\n            },\n        },\n\n        -- In case of errors in the syntax tree, use the following highlight.\n        error = \"+Error\",\n\n        -- Highlights for definitions (`$ Definition`).\n        definitions = {\n            prefix = \"+@punctuation.delimiter\",\n            suffix = \"+@punctuation.delimiter\",\n            title = \"+@markup.strong\",\n            content = \"+@markup.italic\",\n        },\n\n        -- Highlights for footnotes (`^ My Footnote`).\n        footnotes = {\n            prefix = \"+@punctuation.delimiter\",\n            suffix = \"+@punctuation.delimiter\",\n            title = \"+@markup.strong\",\n            content = \"+@markup.italic\",\n        },\n\n        -- Highlights for TODO items.\n        --\n        -- This strictly covers the `( )` component of any detached modifier. In other words, these\n        -- highlights only bother with highlighting the brackets and the content within, but not the\n        -- object containing the TODO item itself.\n        todo_items = {\n            undone = \"+@punctuation.delimiter\",\n            pending = \"+@module\",\n            done = \"+@string\",\n            on_hold = \"+@comment.note\",\n            cancelled = \"+NonText\",\n            urgent = \"+@comment.error\",\n            uncertain = \"+@boolean\",\n            recurring = \"+@keyword.repeat\",\n        },\n\n        -- Highlights for all the possible levels of ordered and unordered lists.\n        lists = {\n            unordered = { prefix = \"+@markup.list\" },\n\n            ordered = { prefix = \"+@keyword.repeat\" },\n        },\n\n        -- Highlights for all the possible levels of quotes.\n        quotes = {\n            [\"1\"] = {\n                prefix = \"+@punctuation.delimiter\",\n                content = \"+@punctuation.delimiter\",\n            },\n            [\"2\"] = {\n                prefix = \"+Blue\",\n                content = \"+Blue\",\n            },\n            [\"3\"] = {\n                prefix = \"+Yellow\",\n                content = \"+Yellow\",\n            },\n            [\"4\"] = {\n                prefix = \"+Red\",\n                content = \"+Red\",\n            },\n            [\"5\"] = {\n                prefix = \"+Green\",\n                content = \"+Green\",\n            },\n            [\"6\"] = {\n                prefix = \"+Brown\",\n                content = \"+Brown\",\n            },\n        },\n\n        -- Highlights for the anchor syntax: `[name]{location}`.\n        anchors = {\n            declaration = {\n                [\"\"] = \"+@markup.link.label\",\n                delimiter = \"+NonText\",\n            },\n            definition = {\n                delimiter = \"+NonText\",\n            },\n        },\n\n        links = {\n            description = {\n                [\"\"] = \"+@markup.link.url\",\n                delimiter = \"+NonText\",\n            },\n\n            file = {\n                [\"\"] = \"+@comment\",\n                delimiter = \"+NonText\",\n            },\n\n            location = {\n                delimiter = \"+NonText\",\n\n                url = \"+@markup.link.url\",\n\n                generic = {\n                    [\"\"] = \"+@type\",\n                    prefix = \"+@type\",\n                },\n\n                external_file = {\n                    [\"\"] = \"+@label\",\n                    prefix = \"+@label\",\n                },\n\n                marker = {\n                    [\"\"] = \"+@neorg.markers.title\",\n                    prefix = \"+@neorg.markers.prefix\",\n                },\n\n                definition = {\n                    [\"\"] = \"+@neorg.definitions.title\",\n                    prefix = \"+@neorg.definitions.prefix\",\n                },\n\n                footnote = {\n                    [\"\"] = \"+@neorg.footnotes.title\",\n                    prefix = \"+@neorg.footnotes.prefix\",\n                },\n\n                heading = {\n                    [\"1\"] = {\n                        [\"\"] = \"+@neorg.headings.1.title\",\n                        prefix = \"+@neorg.headings.1.prefix\",\n                    },\n\n                    [\"2\"] = {\n                        [\"\"] = \"+@neorg.headings.2.title\",\n                        prefix = \"+@neorg.headings.2.prefix\",\n                    },\n\n                    [\"3\"] = {\n                        [\"\"] = \"+@neorg.headings.3.title\",\n                        prefix = \"+@neorg.headings.3.prefix\",\n                    },\n\n                    [\"4\"] = {\n                        [\"\"] = \"+@neorg.headings.4.title\",\n                        prefix = \"+@neorg.headings.4.prefix\",\n                    },\n\n                    [\"5\"] = {\n                        [\"\"] = \"+@neorg.headings.5.title\",\n                        prefix = \"+@neorg.headings.5.prefix\",\n                    },\n\n                    [\"6\"] = {\n                        [\"\"] = \"+@neorg.headings.6.title\",\n                        prefix = \"+@neorg.headings.6.prefix\",\n                    },\n                },\n            },\n        },\n\n        -- Highlights for inline markup.\n        --\n        -- This is all the highlights like `bold`, `italic` and so on.\n        markup = {\n            bold = {\n                [\"\"] = \"+@markup.strong\",\n                delimiter = \"+NonText\",\n            },\n            italic = {\n                [\"\"] = \"+@markup.italic\",\n                delimiter = \"+NonText\",\n            },\n            underline = {\n                [\"\"] = \"+@markup.underline\",\n                delimiter = \"+NonText\",\n            },\n            strikethrough = {\n                [\"\"] = \"+@markup.strikethrough\",\n                delimiter = \"+NonText\",\n            },\n            spoiler = {\n                [\"\"] = \"+@comment.error\",\n                delimiter = \"+NonText\",\n            },\n            subscript = {\n                [\"\"] = \"+@label\",\n                delimiter = \"+NonText\",\n            },\n            superscript = {\n                [\"\"] = \"+@number\",\n                delimiter = \"+NonText\",\n            },\n            variable = {\n                [\"\"] = \"+@function.macro\",\n                delimiter = \"+NonText\",\n            },\n            verbatim = {\n                delimiter = \"+NonText\",\n            },\n            inline_comment = {\n                delimiter = \"+NonText\",\n            },\n            inline_math = {\n                [\"\"] = \"+@markup.math\",\n                delimiter = \"+NonText\",\n            },\n\n            free_form_delimiter = \"+NonText\",\n        },\n\n        -- Highlights for all the delimiter types. These include:\n        -- - `---` - the weak delimiter\n        -- - `===` - the strong delimiter\n        -- - `___` - the horizontal rule\n        delimiters = {\n            strong = \"+@punctuation.delimiter\",\n            weak = \"+@punctuation.delimiter\",\n            horizontal_line = \"+@punctuation.delimiter\",\n        },\n\n        -- Inline modifiers.\n        --\n        -- This includes:\n        -- - `~` - the trailing modifier\n        -- - All link characters (`{`, `}`, `[`, `]`, `<`, `>`)\n        -- - The escape character (`\\`)\n        modifiers = {\n            link = \"+NonText\",\n            escape = \"+@type\",\n        },\n\n        -- Rendered Latex, this will dictate the foreground color of latex images rendered via\n        -- core.latex.renderer\n        rendered = {\n            latex = \"+Normal\",\n        },\n    },\n\n    -- Handles the dimming of certain highlight groups.\n    --\n    -- It sometimes is favourable to use an existing highlight group,\n    -- but to dim or brighten it a little bit.\n    --\n    -- To do so, you may use this table, which, similarly to the `highlights` table,\n    -- will concatenate nested trees to form a highlight group name.\n    --\n    -- The difference is, however, that the leaves of the tree are a table, not a single string.\n    -- This table has three possible fields:\n    -- - `reference` - which highlight to use as reference for the dimming.\n    -- - `percentage` - by how much to darken the reference highlight. This value may be between\n    --   `-100` and `100`, where negative percentages brighten the reference highlight, whereas\n    --   positive values dim the highlight by the given percentage.\n    dim = {\n        tags = {\n            ranged_verbatim = {\n                code_block = {\n                    reference = \"Normal\",\n                    percentage = 15,\n                    affect = \"background\",\n                },\n            },\n        },\n\n        markup = {\n            verbatim = {\n                reference = \"Normal\",\n                percentage = 20,\n            },\n\n            inline_comment = {\n                reference = \"Normal\",\n                percentage = 40,\n            },\n        },\n    },\n}\n\nmodule.setup = function()\n    return { success = true, requires = { \"core.autocommands\" } }\nend\n\nmodule.load = function()\n    module.required[\"core.autocommands\"].enable_autocommand(\"BufEnter\")\n    module.required[\"core.autocommands\"].enable_autocommand(\"FileType\")\n    module.required[\"core.autocommands\"].enable_autocommand(\"ColorScheme\", true)\n\n    module.public.trigger_highlights()\n\n    vim.api.nvim_create_autocmd({ \"FileType\", \"ColorScheme\" }, {\n        callback = module.public.trigger_highlights,\n    })\nend\n\n---@class core.highlights\nmodule.public = {\n\n    --- Reads the highlights configuration table and applies all defined highlights\n    trigger_highlights = function()\n        -- NOTE(vhyrro): This code was added here to work around oddities related to nvim-treesitter.\n        -- This code, with modern nvim-treesitter versions, will probably not break as harshly.\n        -- This code should be removed as soon as possible.\n        --\n        -- do\n        --     local query = require(\"nvim-treesitter.query\")\n\n        --     if not query.has_highlights(\"norg\") then\n        --         query.invalidate_query_cache()\n\n        --         if not query.has_highlights(\"norg\") then\n        --             log.error(\n        --                 \"nvim-treesitter has no available highlights for norg! Ensure treesitter is properly loaded in your config.\"\n        --             )\n        --         end\n        --     end\n\n        --     if vim.bo.filetype == \"norg\" then\n        --         require(\"nvim-treesitter.highlight\").attach(vim.api.nvim_get_current_buf(), \"norg\")\n        --     end\n        -- end\n\n        --- Recursively descends down the highlight configuration and applies every highlight accordingly\n        ---@param highlights table #The table of highlights to descend down\n        ---@param callback fun(hl_name: string, highlight: table, prefix: string): boolean? #A callback function to be invoked for every highlight. If it returns true then we should recurse down the table tree further ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n        ---@param prefix string #Should be only used by the function itself, acts as a \"savestate\" so the function can keep track of what path it has descended down\n        local function descend(highlights, callback, prefix)\n            -- Loop through every highlight defined in the provided table\n            for hl_name, highlight in pairs(highlights) do\n                -- If the callback returns true then descend further down the table tree\n                if callback(hl_name, highlight, prefix) then\n                    descend(highlight, callback, prefix .. \".\" .. hl_name)\n                end\n            end\n        end\n\n        -- Begin the descent down the public highlights configuration table\n        descend(module.config.public.highlights, function(hl_name, highlight, prefix)\n            -- If the type of highlight we have encountered is a table\n            -- then recursively descend down it as well\n            if type(highlight) == \"table\" then\n                return true\n            end\n\n            -- Trim any potential leading and trailing whitespace\n            highlight = vim.trim(highlight)\n\n            -- Check whether we are trying to link to an existing hl group\n            -- by checking for the existence of the + sign at the front\n            local is_link = highlight:sub(1, 1) == \"+\"\n\n            local full_highlight_name = \"@neorg\" .. prefix .. (hl_name:len() > 0 and (\".\" .. hl_name) or \"\")\n            local does_hl_exist = lib.inline_pcall(vim.api.nvim_exec, \"highlight \" .. full_highlight_name, true) ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n\n            -- If we are dealing with a link then link the highlights together (excluding the + symbol)\n            if is_link then\n                -- If the highlight already exists then assume the user doesn't want it to be\n                -- overwritten\n                if does_hl_exist and does_hl_exist:len() > 0 and not does_hl_exist:match(\"xxx%s+cleared\") then\n                    return\n                end\n\n                vim.api.nvim_set_hl(0, full_highlight_name, {\n                    link = highlight:sub(2),\n                })\n            else -- Otherwise simply apply the highlight options the user provided\n                -- If the highlight already exists then assume the user doesn't want it to be\n                -- overwritten\n                if does_hl_exist and does_hl_exist:len() > 0 then\n                    return\n                end\n\n                -- We have to use vim.cmd here\n                vim.cmd({\n                    cmd = \"highlight\",\n                    args = { full_highlight_name, highlight },\n                    bang = true,\n                })\n            end\n        end, \"\")\n\n        -- Begin the descent down the dimming configuration table\n        descend(module.config.public.dim, function(hl_name, highlight, prefix)\n            -- If we don't have a percentage value then keep traversing down the table tree\n            if not highlight.percentage then\n                return true\n            end\n\n            local full_highlight_name = \"@neorg\" .. prefix .. (hl_name:len() > 0 and (\".\" .. hl_name) or \"\")\n            local does_hl_exist = lib.inline_pcall(vim.api.nvim_exec, \"highlight \" .. full_highlight_name, true) ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n\n            -- If the highlight already exists then assume the user doesn't want it to be\n            -- overwritten\n            if does_hl_exist and does_hl_exist:len() > 0 and not does_hl_exist:match(\"xxx%s+cleared\") then\n                return\n            end\n\n            -- Apply the dimmed highlight\n            vim.api.nvim_set_hl(0, full_highlight_name, {\n                [highlight.affect == \"background\" and \"bg\" or \"fg\"] = module.public.dim_color(\n                    module.public.get_attribute(\n                        highlight.reference or full_highlight_name,\n                        highlight.affect or \"foreground\"\n                    ),\n                    highlight.percentage\n                ),\n            })\n        end, \"\")\n    end,\n\n    --- Takes in a table of highlights and applies them to the current buffer\n    ---@param highlights table #A table of highlights\n    add_highlights = function(highlights)\n        module.config.public.highlights =\n            vim.tbl_deep_extend(\"force\", module.config.public.highlights, highlights or {})\n        module.public.trigger_highlights()\n    end,\n\n    --- Takes in a table of items to dim and applies the dimming to them\n    ---@param dim table #A table of items to dim\n    add_dim = function(dim)\n        module.config.public.dim = vim.tbl_deep_extend(\"force\", module.config.public.dim, dim or {})\n        module.public.trigger_highlights()\n    end,\n\n    --- Assigns all Neorg* highlights to `clear`\n    clear_highlights = function()\n        --- Recursively descends down the highlight configuration and clears every highlight accordingly\n        ---@param highlights table #The table of highlights to descend down\n        ---@param prefix string #Should be only used by the function itself, acts as a \"savestate\" so the function can keep track of what path it has descended down\n        local function descend(highlights, prefix)\n            -- Loop through every defined highlight\n            for hl_name, highlight in pairs(highlights) do\n                -- If it is a table then recursively traverse down it!\n                if type(highlight) == \"table\" then\n                    descend(highlight, hl_name)\n                else -- Otherwise we're dealing with a string\n                    -- Hence we should clear the highlight\n                    vim.cmd(\"highlight! clear Neorg\" .. prefix .. hl_name)\n                end\n            end\n        end\n\n        -- Begin the descent\n        descend(module.config.public.highlights, \"\")\n    end,\n\n    -- NOTE: Shamelessly taken and tweaked a little from akinsho's nvim-bufferline:\n    -- https://github.com/akinsho/nvim-bufferline.lua/blob/fec44821eededceadb9cc25bc610e5114510a364/lua/bufferline/colors.lua\n    -- <3\n    get_attribute = function(name, attribute)\n        -- Attempt to get the highlight\n        local success, hl = pcall(vim.api.nvim_get_hl_by_name, name, true) ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n\n        -- If we were successful and if the attribute exists then return it\n        if success and hl[attribute] then\n            return bit.tohex(hl[attribute], 6)\n        else -- Else log the message in a regular info() call, it's not an insanely important error\n            log.info(\"Unable to grab highlight for attribute\", attribute, \" - full error:\", hl)\n        end\n\n        return \"NONE\"\n    end,\n\n    hex_to_rgb = function(hex_colour)\n        return tonumber(hex_colour:sub(1, 2), 16), tonumber(hex_colour:sub(3, 4), 16), tonumber(hex_colour:sub(5), 16)\n    end,\n\n    dim_color = function(colour, percent)\n        if colour == \"NONE\" then\n            return colour\n        end\n\n        local function alter(attr)\n            return math.floor(attr * (100 - percent) / 100)\n        end\n\n        local r, g, b = module.public.hex_to_rgb(colour)\n\n        if not r or not g or not b then\n            return \"NONE\"\n        end\n\n        return string.format(\"#%02x%02x%02x\", math.min(alter(r), 255), math.min(alter(g), 255), math.min(alter(b), 255))\n    end,\n\n    -- END of shamelessly ripped off akinsho code\n}\n\nmodule.events.subscribed = {\n    [\"core.autocommands\"] = {\n        colorscheme = true,\n        bufenter = true,\n    },\n}\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/integrations/coq_nvim/module.lua",
    "content": "--[[\n    file: Coq_nvim\n    title: Integrating Neorg with `coq_nvim`\n    summary: A module for integrating coq_nvim with Neorg.\n    internal: true\n    ---\nThis module works with the [`core.completion`](@core.completion) module to attempt to provide\nintelligent completions. Note that integrations like this are second-class citizens and may not work in 100%\nof scenarios. If they don't then please file a bug report!\n--]]\n\nlocal neorg = require(\"neorg.core\")\nlocal log, modules = neorg.log, neorg.modules\n\nlocal module = modules.create(\"core.integrations.coq_nvim\")\n\nmodule.private = {\n    ---@param map table<number, table>\n    new_uid = function(map)\n        vim.validate({\n            map = { map, \"table\" },\n        })\n\n        local key ---@type integer|nil\n        while true do\n            if not key or map[key] then\n                key = math.floor(math.random() * 10000)\n            else\n                return key\n            end\n        end\n    end,\n}\n\nmodule.load = function()\n    local success = pcall(require, \"coq\")\n\n    if not success then\n        log.fatal(\"coq_nvim not found, aborting...\")\n        return\n    end\nend\n\n---@class core.integrations.coq_nvim : neorg.completion_engine\nmodule.public = {\n    create_source = function()\n        module.private.completion_item_mapping = {\n            Directive = vim.lsp.protocol.CompletionItemKind.Keyword,\n            Tag = vim.lsp.protocol.CompletionItemKind.Keyword,\n            Language = vim.lsp.protocol.CompletionItemKind.Property,\n            TODO = vim.lsp.protocol.CompletionItemKind.Event,\n            Property = vim.lsp.protocol.CompletionItemKind.Property,\n            Format = vim.lsp.protocol.CompletionItemKind.Property,\n            Embed = vim.lsp.protocol.CompletionItemKind.Property,\n            Reference = vim.lsp.protocol.CompletionItemKind.Reference,\n            File = vim.lsp.protocol.CompletionItemKind.File,\n        }\n\n        -- luacheck: push ignore 111\n        -- luacheck: push ignore 112\n        -- luacheck: push ignore 113\n        COQsources = COQsources or {} ---@diagnostic disable undefined-global\n        COQsources[module.private.new_uid(COQsources)] = {\n            name = \"Neorg\",\n            fn = function(args, callback)\n                if vim.bo.filetype ~= \"norg\" then\n                    return callback()\n                end\n\n                local row, col = unpack(args.pos)\n                local line = args.line\n                local before_char = line:sub(#line, #line)\n                -- Neorg requires a nvim-compe like context nvim-compe defines\n                -- the following fields. Not all of them are used by Neorg, but\n                -- they are left here for future reference\n                local context = {\n                    start_offset = nil,\n                    char = col + 1,\n                    before_char = before_char,\n                    line = line,\n                    column = nil,\n                    buffer = nil,\n                    line_number = row + 1,\n                    previous_context = nil,\n                    full_line = line,\n                }\n                local completion_cache = module.public.invoke_completion_engine(context)\n                local prev = line:match(\"({.*)$\")\n\n                if completion_cache.options.pre then\n                    completion_cache.options.pre(context)\n                end\n\n                local completions = vim.deepcopy(completion_cache.items)\n\n                for index, element in ipairs(completions) do\n                    -- coq_nvim requries at least 2 exact prefix characters by\n                    -- default. Users could chagnge this setting, but instead\n                    -- we are providing the start of the match (for links) so\n                    -- coq_nvim doesnt' filter our results\n                    local word = (prev or \"\") .. element\n                    local label = (prev or \"\") .. element\n                    if type(element) == \"table\" then\n                        word = element[1]\n                        label = element.label\n                    end\n                    completions[index] = {\n                        word = word,\n                        label = label,\n                        kind = module.private.completion_item_mapping[completion_cache.options.type],\n                    }\n                end\n\n                callback({\n                    isIncomplete = false,\n                    items = completions,\n                })\n            end,\n        }\n        -- luacheck: pop\n        -- luacheck: pop\n        -- luacheck: pop\n    end,\n}\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/integrations/image/module.lua",
    "content": "--[[\n    file: Image\n    title: Images Directly within Neovim.\n    description: The `image` module uses various terminal backends to display images within your Neovim instance.\n    summary: Module for interacting with and managing images in the terminal.\n    internal: true\n    ---\n\n`core.integrations.image` is an internal module that wraps image.nvim, exposing methods to display images in neovim.\n--]]\n\nlocal neorg = require(\"neorg.core\")\nlocal module = neorg.modules.create(\"core.integrations.image\")\n\nmodule.load = function()\n    local success, image = pcall(require, \"image\")\n\n    assert(success, \"Unable to load image.nvim plugin\")\n\n    module.private.image = image\n    module.private.image_utils = require(\"image.utils\")\nend\n\nmodule.private = {\n    image = nil,\n}\n\nmodule.public = {\n    new_image = function(buffernr, png_path, position, window, scale, virtual_padding)\n        local image = require(\"image\").from_file(png_path, {\n            window = window,\n            buffer = buffernr,\n            inline = true, -- let image.nvim track the position for us\n            with_virtual_padding = virtual_padding,\n            x = position.column_start,\n            y = position.row_start + (virtual_padding and 1 or 0),\n            height = scale,\n        })\n        return image\n    end,\n    ---Render an image or list of images\n    ---@param images any[]\n    render = function(images)\n        for _, limage in pairs(images) do\n            limage.image:render({ y = limage.range[1], x = limage.range[2] })\n        end\n    end,\n    clear = function(images)\n        for _, limage in pairs(images) do\n            limage.image:clear()\n        end\n    end,\n    clear_at_cursor = function(images, row)\n        local cleared = {}\n        for id, limage in pairs(images) do\n            local image = limage.image\n            if image.geometry.y == row then\n                image:clear()\n                table.insert(cleared, id)\n            end\n        end\n        return cleared\n    end,\n    ---Compute the image's rendered width/height without rendering\n    ---@param image any an image.nvim image\n    ---@param limit { width: number, height: number }\n    ---@return { width: number, height: number }\n    image_size = function(image, limit)\n        limit = limit or {}\n        local term_size = require(\"image.utils.term\").get_size()\n        local gopts = image.global_state.options\n\n        local true_size = {\n            width = math.min(\n                math.floor(image.image_width / term_size.cell_width), -- max image size (images don't scale up past their true size)\n                gopts.max_width or math.huge, -- image.nvim configured max size\n                limit.width or math.huge -- latex-renderer configured max size\n            ),\n            height = math.min(\n                math.floor(image.image_height / term_size.cell_height),\n                gopts.max_height or math.huge,\n                limit.height or math.huge\n            ),\n        }\n        local width, height = module.private.image_utils.math.adjust_to_aspect_ratio(\n            term_size,\n            image.image_width,\n            image.image_height,\n            true_size.width,\n            true_size.height\n        )\n        return { width = math.ceil(width), height = math.ceil(height) }\n    end,\n}\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/integrations/nvim-cmp/module.lua",
    "content": "--[[\n    file: Nvim-Cmp\n    title: Integrating Neorg with `nvim-cmp`\n    summary: A module for integrating nvim-cmp with Neorg.\n    internal: true\n    ---\nThis module works with the [`core.completion`](@core.completion) module to attempt to provide\nintelligent completions. Note that integrations like this are second-class citizens and may not work in 100%\nof scenarios. If they don't then please file a bug report!\n\nAfter setting up `core.completion` with the `engine` set to `nvim-cmp`, make sure to also set up \"neorg\"\nas a source in `nvim-cmp`:\n```lua\nsources = {\n    { name = \"neorg\" },\n},\n```\n--]]\n\nlocal neorg = require(\"neorg.core\")\nlocal log, modules = neorg.log, neorg.modules\n\nlocal module = modules.create(\"core.integrations.nvim-cmp\")\n\nmodule.private = {\n    source = {},\n    cmp = {},\n    completions = {},\n}\n\nmodule.load = function()\n    local success, cmp = pcall(require, \"cmp\")\n\n    if not success then\n        log.fatal(\"nvim-cmp not found, aborting...\")\n        return\n    end\n\n    module.private.cmp = cmp\nend\n\n---@class core.integrations.nvim-cmp : neorg.completion_engine\nmodule.public = {\n    create_source = function()\n        module.private.completion_item_mapping = {\n            Directive = module.private.cmp.lsp.CompletionItemKind.Keyword,\n            Tag = module.private.cmp.lsp.CompletionItemKind.Keyword,\n            Language = module.private.cmp.lsp.CompletionItemKind.Property,\n            TODO = module.private.cmp.lsp.CompletionItemKind.Event,\n            Property = module.private.cmp.lsp.CompletionItemKind.Property,\n            Format = module.private.cmp.lsp.CompletionItemKind.Property,\n            Embed = module.private.cmp.lsp.CompletionItemKind.Property,\n            Reference = module.private.cmp.lsp.CompletionItemKind.Reference,\n            File = module.private.cmp.lsp.CompletionItemKind.File,\n        }\n\n        module.private.source.new = function()\n            return setmetatable({}, { __index = module.private.source })\n        end\n\n        function module.private.source.complete(_, request, callback)\n            local abstracted_context = module.public.create_abstracted_context(request)\n\n            local completion_cache = module.public.invoke_completion_engine(abstracted_context)\n\n            if completion_cache.options.pre then\n                completion_cache.options.pre(abstracted_context)\n            end\n\n            local completions = vim.deepcopy(completion_cache.items)\n\n            for index, element in ipairs(completions) do\n                local word = element\n                local label = element\n                if type(element) == \"table\" then\n                    word = element[1]\n                    label = element.label\n                end\n                completions[index] = {\n                    word = word,\n                    label = label,\n                    kind = module.private.completion_item_mapping[completion_cache.options.type],\n                }\n            end\n\n            callback(completions)\n        end\n\n        function module.private.source:get_trigger_characters()\n            return { \"@\", \"-\", \"(\", \" \", \".\", \":\", \"#\", \"*\", \"^\", \"[\" }\n        end\n\n        function module.private.source:is_available()\n            return vim.bo.filetype == \"norg\"\n        end\n\n        module.private.cmp.register_source(\"neorg\", module.private.source)\n    end,\n\n    create_abstracted_context = function(request)\n        return {\n            start_offset = request.offset,\n            char = request.context.cursor.character,\n            before_char = request.completion_context.triggerCharacter,\n            line = request.context.cursor_before_line,\n            column = request.context.cursor.col,\n            buffer = request.context.bufnr,\n            line_number = request.context.cursor.line,\n            previous_context = {\n                line = request.context.prev_context.cursor_before_line,\n                column = request.context.prev_context.cursor.col,\n                start_offset = request.offset,\n            },\n            full_line = request.context.cursor_line,\n        }\n    end,\n\n    invoke_completion_engine = function(context)\n        error(\"`invoke_completion_engine` must be set from outside.\")\n        assert(context)\n        return {}\n    end,\n}\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/integrations/nvim-compe/module.lua",
    "content": "--[[\n    file: Nvim-Compe\n    title: Integrating Neorg with `nvim-compe`\n    summary: A module for integrating nvim-compe with Neorg.\n    internal: true\n    ---\nWARNING: This module is **deprecated**, and no further support for the module will be provided.\nThat does not mean it will not work, however any bugs will not be dealt with by the main Neorg team.\n\nA module for integrating nvim-compe with Neorg.\nWorks with [`core.completion`](@core.completion) to provide intelligent completions.\n--]]\n\nlocal neorg = require(\"neorg.core\")\nlocal log, modules = neorg.log, neorg.modules\n\nlocal module = modules.create(\"core.integrations.nvim-compe\")\n\n-- Define some private data that's not supposed to be seen\nmodule.private = {\n    source = {},\n    compe = {},\n\n    completions = {},\n}\n\nmodule.load = function()\n    -- Code to test the existence of nvim-compe\n    local success, compe = pcall(require, \"compe\")\n\n    if not success then\n        log.fatal(\"nvim-compe not found, aborting...\")\n        return\n    end\n\n    module.private.compe = compe\nend\n\n---@class core.integrations.nvim-compe : neorg.completion_engine\nmodule.public = {\n    ---@param user_data table #A table of user data to supply to the source upon creation\n    create_source = function(user_data)\n        user_data = user_data or {}\n\n        local data = {\n            name = \"[Neorg]\",\n            priority = 998,\n            sort = false,\n            dup = 0,\n        }\n\n        data = vim.tbl_deep_extend(\"force\", data, user_data)\n\n        -- Define functions for nvim-compe\n        module.private.source.new = function()\n            return setmetatable({}, { __index = module.private.source })\n        end\n\n        -- Return metadata for nvim-compe to use\n        module.private.source.get_metadata = function()\n            return {\n                priority = data.priority,\n                sort = data.sort,\n                dup = data.dup,\n                filetypes = { \"norg\" },\n                menu = data.name,\n            }\n        end\n\n        -- Used to determine whether or not to provide completions, simply invokes the public determine function\n        module.private.source.determine = function(_, context)\n            return module.public.determine(context)\n        end\n\n        -- Used to actually provide completions, simply invokes the public sibling function\n        module.private.source.complete = function(_, context)\n            module.public.complete(context)\n        end\n\n        -- Invoked whenever a completion is confirmed, calls the public confirm() function\n        module.private.source.confirm = function(_, context)\n            module.public.confirm(context) ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n        end\n\n        -- Actually register the nvim-compe source\n        module.private.compe.register_source(\"neorg\", module.private.source)\n    end,\n\n    --- Looks at the cursor position and tries to determine whether we should provide any completions\n    ---@param context table #The context provided by nvim-compe\n    determine = function(context)\n        -- Abstract away the context to a completion engine agnostic format\n        local abstracted_context = module.public.create_abstracted_context(context)\n\n        -- Update the current completion cache with the data returned by core.completion\n        module.private.completion_cache = module.public.invoke_completion_engine(abstracted_context) ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n\n        -- If we haven't returned any items to complete via that function then return an empty table,\n        -- symbolizing a lack of completions\n        if vim.tbl_isempty(module.private.completion_cache.items) then\n            return {}\n        end\n\n        -- If the current completion that was found has a pre() function then invoke that\n        if module.private.completion_cache.options.pre then\n            module.private.completion_cache.options.pre(abstracted_context)\n        end\n\n        -- Reverse the current line, this is used for a reverse find() call\n        local reversed = vim.trim(context.before_line):reverse()\n        -- Find any occurrence of whitespace\n        local last_whitespace = reversed:find(\"%s\")\n\n        --[[\n            This bit is a bit crazy, however here's the gist of it:\n            It checks the current cursor position and the last occurrence of whitespace in the string to provide completions\n            even if a part of that completion is already present. Say we have:\n            @\n            Compe would have no problem with this and would provide completions instantly, but say we have (| means the current cursor pos):\n            @t|ab\n            And i try pressing C to edit till the end of the line, I'm then left with:\n            @t\n            If I were to try typing here I wouldn't get any completions, because compe wouldn't understand that @t is part of the completion.\n            This below bit of code makes sure that it *does* understand and that it *does* detect properly.\n        --]]\n        last_whitespace = last_whitespace and last_whitespace - 1\n            or (function()\n                local found = module.private.completion_cache.options.completion_start\n                    and reversed:find(module.private.completion_cache.options.completion_start)\n                return found and found - 1 or 0\n            end)()\n\n        return { keyword_pattern_offset = 0, trigger_character_offset = context.col - last_whitespace }\n    end,\n\n    --- Once the completion candidates have been collected from the determine() function it's time to display them\n    ---@param context table #A context as provided by nvim-compe\n    complete = function(context)\n        -- If the completion cache is empty for some reason then don't do anything\n        if vim.tbl_isempty(module.private.completion_cache.items) then\n            return\n        end\n\n        -- Grab a copy of the completions (important, because if it's not copied then values get overwritten)\n        local completions = vim.deepcopy(module.private.completion_cache.items)\n\n        -- Go through each element and convert it into a format that nvim-compe understands\n        for index, element in ipairs(completions) do\n            completions[index] = { word = element, kind = module.private.completion_cache.options.type }\n        end\n\n        -- Display the completions\n        context.callback({\n            items = completions,\n        })\n    end,\n\n    confirm = function()\n        -- If the defined completion has a post function then invoke it\n        if module.private.completion_cache.options.post then\n            module.private.completion_cache.options.post()\n        end\n\n        -- Reset the completion cache\n        module.private.completion_cache = {}\n    end,\n\n    --- Returns a new context based off of nvim-compe's \"proprietary\" context and converts it into a universal context\n    ---@param context table #A context as provided by nvim-compe\n    create_abstracted_context = function(context)\n        return {\n            start_offset = context.start_offset,\n            char = context.char,\n            before_char = context.before_char,\n            line = context.before_line,\n            column = context.col,\n            buffer = context.bufnr,\n            line_number = context.lnum,\n            previous_context = {\n                line = context.prev_context.before_line,\n                column = context.prev_context.col,\n                start_offset = context.prev_context.start_offset,\n            },\n            full_line = context.line,\n        }\n    end,\n\n    invoke_completion_engine = function(context)\n        error(\"`invoke_completion_engine` must be set from outside.\")\n        assert(context)\n        return {}\n    end,\n}\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/integrations/otter/module.lua",
    "content": "--[[\n    file: Otter\n    title: LSP Features in Code Cells\n    description: Integrates with otter.nvim to get LSP features directly in norg code cells.\n    ---\n\nFrom the otter README:\n\n> Otter.nvim provides lsp features and a code completion source for code embedded in other documents.\n\nThis includes:\n- auto completion\n- diagnostics\n- hover\n- rename symbol\n- go to definition\n- go to references\n- go to type definition\n- range formatting (not document formatting)\n\n## Setup\nYou need to install otter.nvim and make sure it's loaded before Neorg, you can configure it yourself\nby reading [the Otter.nvim README](https://github.com/jmbuhr/otter.nvim). You do not need to\nconfigure `handle_leading_whitespace`, neorg will do that for you.\n\nIf you want auto complete, make sure you add `\"otter\"` as a source to nvim-cmp:\n\n```lua\nsources = {\n  -- ... other sources\n  { name = \"otter\" },\n  -- ... other sources\n}\n```\n\n## Commands\n- `:Neorg otter enable` - enable otter in the current buffer\n- `:Neorg otter disable` - disable otter in the current buffer\n--]]\n\nlocal neorg = require(\"neorg.core\")\nlocal module = neorg.modules.create(\"core.integrations.otter\")\n\nmodule.setup = function()\n    return {\n        success = pcall(require, \"otter\"),\n        requires = {\n            \"core.neorgcmd\",\n        },\n    }\nend\n\nlocal otter\nmodule.load = function()\n    local ok\n    ok, otter = pcall(require, \"otter\")\n    assert(ok, \"[Neorg] Failed to load otter.nvim\")\n\n    -- This will not interfere with other otter.nvim configuration\n    otter.setup({ handle_leading_whitespace = true })\n\n    if module.config.public.auto_start then\n        local group = vim.api.nvim_create_augroup(\"neorg.integrations.otter\", {})\n        vim.api.nvim_create_autocmd({ \"BufReadPost\" }, {\n            desc = \"Activate Otter on Buf Enter\",\n            pattern = \"*.norg\",\n            group = group,\n            callback = function(_)\n                module.public.activate()\n            end,\n        })\n    end\n\n    module.required[\"core.neorgcmd\"].add_commands_from_table({\n        otter = {\n            args = 1,\n            name = \"otter\",\n            condition = \"norg\",\n            subcommands = {\n                enable = {\n                    args = 0,\n                    name = \"otter.enable\",\n                },\n                disable = {\n                    args = 0,\n                    name = \"otter.disable\",\n                },\n            },\n        },\n    })\nend\n\nmodule.events.subscribed = {\n    [\"core.neorgcmd\"] = {\n        [\"otter.enable\"] = true,\n        [\"otter.disable\"] = true,\n    },\n}\n\nmodule.on_event = function(event)\n    if module.private[event.split_type[2]] then\n        module.private[event.split_type[2]](event)\n    end\nend\n\nmodule.config.public = {\n    -- list of languages that otter will try to start a language server for.\n    -- `nil` means all languages\n    languages = nil,\n\n    -- Automatically start Otter when a norg buffer is opened\n    auto_start = true,\n\n    completion = {\n        -- enable/disable Otter autocomplete\n        enabled = true,\n    },\n\n    diagnostics = {\n        -- enable/disable Otter diagnostics\n        enabled = true,\n    },\n}\n\nmodule.private = {\n    [\"otter.enable\"] = function(_)\n        module.public.activate()\n    end,\n    [\"otter.disable\"] = function(_)\n        module.public.deactivate()\n    end,\n}\n\nmodule.public = {\n    ---Activate otter in the current buffer, includes setting buffer keymaps\n    activate = function()\n        otter.activate(\n            module.config.public.languages,\n            module.config.public.completion.enabled,\n            module.config.public.diagnostics.enabled,\n            nil -- or a query...\n        )\n    end,\n\n    ---Deactivate otter in the current buffer, including unsetting buffer keymaps\n    deactivate = function()\n        otter.deactivate(module.config.public.completion.enabled, module.config.public.diagnostics.enabled)\n    end,\n}\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/integrations/treesitter/module.lua",
    "content": "--[[\n    file: Treesitter-Integration\n    title: Snazzy Treesitter Integration\n    summary: A module designed to integrate Treesitter into Neorg.\n    embed: https://user-images.githubusercontent.com/76052559/151668244-9805afc4-8c50-4925-85ec-1098aff5ede6.gif\n    internal: true\n    ---\n\n## Keybinds\n\nThis module exposes the following keybinds (see [`core.keybinds`](@core.keybinds) for instructions on\nmapping them):\n\n- `neorg.treesitter.next.heading` - jump to the next heading\n- `neorg.treesitter.next.link` - jump to the next link\n- `neorg.treesitter.previous.heading` - jump to the previous heading\n- `neorg.treesitter.previous.link` - jump to the previous link\n--]]\n\nlocal neorg = require(\"neorg.core\")\nlocal lib, log, modules, utils = neorg.lib, neorg.log, neorg.modules, neorg.utils\n\nlocal module = modules.create(\"core.integrations.treesitter\")\n\nmodule.private = {\n    link_query = [[\n                (link) @next-segment\n                (anchor_declaration) @next-segment\n                (anchor_definition) @next-segment\n             ]],\n    heading_query = [[\n                 [\n                     (heading1\n                         title: (paragraph_segment) @next-segment\n                     )\n                     (heading2\n                         title: (paragraph_segment) @next-segment\n                     )\n                     (heading3\n                         title: (paragraph_segment) @next-segment\n                     )\n                     (heading4\n                         title: (paragraph_segment) @next-segment\n                     )\n                     (heading5\n                         title: (paragraph_segment) @next-segment\n                     )\n                     (heading6\n                         title: (paragraph_segment) @next-segment\n                     )\n                 ]\n             ]],\n}\n\nmodule.setup = function()\n    return { success = true, requires = { \"core.highlights\" } }\nend\n\nmodule.load = function()\n    if module.config.public.configure_parsers then\n        -- luacheck: push ignore\n\n        -- compat: nvim-treesitter master requires the extra function call, main does not\n        local parser_configs = require(\"nvim-treesitter.parsers\")\n        if parser_configs.get_parser_configs then\n            parser_configs = parser_configs.get_parser_configs()\n        end\n\n        parser_configs.norg = {\n            install_info = module.config.public.parser_configs.norg,\n        }\n\n        parser_configs.norg_meta = {\n            install_info = module.config.public.parser_configs.norg_meta,\n        }\n\n        modules.await(\"core.neorgcmd\", function(neorgcmd)\n            neorgcmd.add_commands_from_table({\n                [\"sync-parsers\"] = {\n                    args = 0,\n                    name = \"sync-parsers\",\n                },\n            })\n        end)\n\n        -- luacheck: pop\n\n        vim.api.nvim_create_autocmd(\"BufEnter\", {\n            pattern = \"*.norg\",\n            once = true,\n            callback = function()\n                module.public.parser_path = vim.api.nvim_get_runtime_file(\"parser/norg.so\", false)[1]\n\n                if module.public.parser_path then\n                    return\n                end\n\n                if module.config.public.install_parsers then\n                    require(\"nvim-treesitter.install\").commands.TSInstallSync[\"run!\"](\"norg\", \"norg_meta\")\n                    module.public.parser_path = vim.api.nvim_get_runtime_file(\"parser/norg.so\", false)[1]\n                else\n                    assert(\n                        false,\n                        \"Neorg's parser is not installed! Run `:Neorg sync-parsers` to install it, then restart Neovim.\"\n                    )\n                end\n            end,\n        })\n    end\n\n    vim.keymap.set(\n        \"\",\n        \"<Plug>(neorg.treesitter.next.heading)\",\n        lib.wrap(module.public.goto_next_query_match, module.private.heading_query)\n    )\n    vim.keymap.set(\n        \"\",\n        \"<Plug>(neorg.treesitter.next.link)\",\n        lib.wrap(module.public.goto_next_query_match, module.private.link_query)\n    )\n    vim.keymap.set(\n        \"\",\n        \"<Plug>(neorg.treesitter.previous.heading)\",\n        lib.wrap(module.public.goto_previous_query_match, module.private.heading_query)\n    )\n    vim.keymap.set(\n        \"\",\n        \"<Plug>(neorg.treesitter.previous.link)\",\n        lib.wrap(module.public.goto_previous_query_match, module.private.link_query)\n    )\nend\n\nmodule.config.public = {\n    --- If true will auto-configure the parsers to use the recommended setup.\n    --  Set to false only if you know what you're doing, or if the setting messes\n    --  with your personal configuration.\n    configure_parsers = true,\n    --- If true will automatically install Norg parsers if they are not present.\n    install_parsers = true,\n    --- Configurations for each parser as required by `nvim-treesitter`.\n    --  If you would like to tweak your parser configs you may do so here.\n    parser_configs = {\n        -- Configuration for the mainline norg parser.\n        norg = {\n            url = \"https://github.com/nvim-neorg/tree-sitter-norg\",\n            files = { \"src/parser.c\", \"src/scanner.cc\" },\n            revision = \"6348056b999f06c2c7f43bb0a5aa7cfde5302712\",\n            use_makefile = true,\n        },\n        -- Configuration for the metadata parser (used to parse the contents\n        -- of `@document.meta` blocks).\n        norg_meta = {\n            url = \"https://github.com/nvim-neorg/tree-sitter-norg-meta\",\n            files = { \"src/parser.c\" },\n            revision = \"a479d1ca05848d0b51dd25bc9f71a17e0108b240\",\n            use_makefile = true,\n        },\n    },\n}\n\n---@class core.integrations.treesitter\nmodule.public = {\n    parser_path = nil,\n\n    --- Jumps to the next match of a query in the current buffer\n    ---@param query_string string Query with `@next-segment` captures\n    goto_next_query_match = function(query_string)\n        local cursor = vim.api.nvim_win_get_cursor(0)\n        local line_number, col_number = cursor[1], cursor[2]\n\n        local document_root = module.public.get_document_root(0)\n\n        if not document_root then\n            return\n        end\n        local next_match_query = utils.ts_parse_query(\"norg\", query_string)\n        for id, node in next_match_query:iter_captures(document_root, 0, line_number - 1, -1) do\n            if next_match_query.captures[id] == \"next-segment\" then\n                local start_line, start_col = node:range()\n                -- start_line is 0-based; increment by one so we can compare it to the 1-based line_number\n                start_line = start_line + 1\n\n                -- Skip node if it's inside a closed fold\n                if not vim.tbl_contains({ -1, start_line }, vim.fn.foldclosed(start_line)) then\n                    goto continue\n                end\n\n                -- Find and go to the first matching node that starts after the current cursor position.\n                if (start_line == line_number and start_col > col_number) or start_line > line_number then\n                    module.public.goto_node(node)\n                    return\n                end\n            end\n\n            ::continue::\n        end\n    end,\n    --- Jumps to the previous match of a query in the current buffer\n    ---@param query_string string Query with `@next-segment` captures\n    goto_previous_query_match = function(query_string)\n        local cursor = vim.api.nvim_win_get_cursor(0)\n        local line_number, col_number = cursor[1], cursor[2]\n\n        local document_root = module.public.get_document_root(0)\n\n        if not document_root then\n            return\n        end\n        local previous_match_query = utils.ts_parse_query(\"norg\", query_string)\n        local final_node = nil\n\n        for id, node in previous_match_query:iter_captures(document_root, 0, 0, line_number) do\n            if previous_match_query.captures[id] == \"next-segment\" then\n                local start_line, _, _, end_col = node:range()\n                -- start_line is 0-based; increment by one so we can compare it to the 1-based line_number\n                start_line = start_line + 1\n\n                -- Skip node if it's inside a closed fold\n                if not vim.tbl_contains({ -1, start_line }, vim.fn.foldclosed(start_line)) then\n                    goto continue\n                end\n\n                -- Find the last matching node that ends before the current cursor position.\n                if start_line < line_number or (start_line == line_number and end_col < col_number) then\n                    final_node = node\n                end\n            end\n\n            ::continue::\n        end\n        if final_node then\n            module.public.goto_node(final_node)\n        end\n    end,\n    ---  Gets all nodes of a given type from the AST\n    ---@param node_type string #The type of node to filter out\n    ---@param opts? table #A table of two options: `buf` and `ft`, for the buffer and format to use respectively.\n    get_all_nodes = function(node_type, opts)\n        local result = {}\n        opts = opts or {}\n\n        if not opts.buf then\n            opts.buf = 0\n        end\n\n        if not opts.ft then\n            opts.ft = \"norg\"\n        end\n\n        -- Do we need to go through each tree? lol\n        vim.treesitter.get_parser(opts.buf, opts.ft):for_each_tree(function(tree)\n            table.insert(result, module.public.search_tree(tree, node_type))\n        end)\n\n        return vim.iter(result):flatten():totable()\n    end,\n\n    ---Gets all nodes of a given type from the AST\n    ---@param node_type string #The type of node to filter out\n    ---@param path string path to the file to parse\n    ---@param filetype string? file type of the file or `norg` if omitted\n    get_all_nodes_in_file = function(node_type, path, filetype)\n        path = vim.fs.normalize(path)\n        if not filetype then\n            filetype = \"norg\"\n        end\n\n        local contents = io.open(path, \"r\"):read(\"*a\")\n        local tree = vim.treesitter.get_string_parser(contents, filetype):parse()[1]\n        if not (tree or tree.root) then\n            return {}\n        end\n\n        return module.public.search_tree(tree, node_type)\n    end,\n\n    search_tree = function(tree, node_type)\n        local result = {}\n        local root = tree:root()\n\n        --- Recursively searches for a node of a given type\n        ---@param node TSNode #The starting point for the search\n        local function descend(node)\n            -- Iterate over all children of the node and try to match their type\n            for child, _ in node:iter_children() do\n                if child:type() == node_type then\n                    table.insert(result, child)\n                else\n                    -- If no match is found try descending further down the syntax tree\n                    for _, child_node in ipairs(descend(child) or {}) do\n                        table.insert(result, child_node)\n                    end\n                end\n            end\n        end\n\n        descend(root)\n        return result\n    end,\n\n    --- Executes function callback on each child node of the root\n    ---@param callback function\n    ---@param ts_tree any #Optional syntax tree ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n    tree_map = function(callback, ts_tree)\n        local tree = ts_tree or vim.treesitter.get_parser(0, \"norg\"):parse()[1]\n\n        local root = tree:root()\n\n        for child, _ in root:iter_children() do\n            callback(child)\n        end\n    end,\n    --- Executes callback on each child recursive\n    ---@param callback function Executes with each node as parameter, can return false to stop recursion\n    ---@param ts_tree any #Optional syntax tree ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n    tree_map_rec = function(callback, ts_tree)\n        local tree = ts_tree or vim.treesitter.get_parser(0, \"norg\"):parse()[1]\n\n        local root = tree:root()\n\n        local function descend(start)\n            for child, _ in start:iter_children() do\n                local stop_descending = callback(child)\n                if not stop_descending then\n                    descend(child)\n                end\n            end\n        end\n\n        descend(root)\n    end,\n    get_node_text = function(node, source)\n        if not node then\n            return \"\"\n        end\n\n        -- when source is the string contents of the file\n        if type(source) == \"string\" then\n            local _, _, start_bytes = node:start()\n            local _, _, end_bytes = node:end_()\n            return string.sub(source, start_bytes + 1, end_bytes)\n        end\n\n        source = source or 0\n\n        local start_row, start_col = node:start()\n        local end_row, end_col = node:end_()\n\n        local eof_row = vim.api.nvim_buf_line_count(source)\n\n        if end_row >= eof_row then\n            end_row = eof_row - 1\n            end_col = -1\n        end\n\n        if start_row >= eof_row then\n            return \"\"\n        end\n\n        local lines = vim.api.nvim_buf_get_text(source, start_row, start_col, end_row, end_col, {})\n\n        return table.concat(lines, \"\\n\")\n    end,\n\n    --- Get the range of a TSNode as an LspRange\n    ---@param node TSNode\n    ---@return lsp.Range\n    node_to_lsp_range = function(node)\n        local start_line, start_col, end_line, end_col = node:range()\n        return {\n            start = { line = start_line, character = start_col },\n            [\"end\"] = { line = end_line, character = end_col },\n        }\n    end,\n\n    --- Swap two nodes in the buffer. Ignores newlines at the end of the node\n    ---@param node1 TSNode\n    ---@param node2 TSNode\n    ---@param bufnr number\n    ---@param cursor_to_second boolean move the cursor to the start of the second node (default false)\n    swap_nodes = function(node1, node2, bufnr, cursor_to_second)\n        if not node1 or not node2 then\n            return\n        end\n        local range1 = module.public.node_to_lsp_range(node1)\n        local range2 = module.public.node_to_lsp_range(node2)\n\n        local _text1 = module.public.get_node_text(node1, bufnr)\n        local _text2 = module.public.get_node_text(node2, bufnr)\n\n        if not _text1 or not _text2 then\n            return\n        end\n\n        local text1 = vim.split(_text1, \"\\n\")\n        local text2 = vim.split(_text2, \"\\n\")\n\n        ---remove trailing blank lines from the text, and update the corresponding range appropriately\n        ---@param text string[]\n        ---@param range table\n        local function remove_trailing_blank_lines(text, range)\n            local end_line_offset = 0\n            while text[#text] == \"\" do\n                text[#text] = nil\n                end_line_offset = end_line_offset + 1\n            end\n            range[\"end\"] = {\n                character = string.len(text[#text]),\n                line = range[\"end\"].line - end_line_offset,\n            }\n            if #text == 1 then -- ie. start and end lines are equal\n                range[\"end\"].character = range[\"end\"].character + range.start.character\n            end\n        end\n\n        remove_trailing_blank_lines(text1, range1)\n        remove_trailing_blank_lines(text2, range2)\n\n        local edit1 = { range = range1, newText = table.concat(text2, \"\\n\") }\n        local edit2 = { range = range2, newText = table.concat(text1, \"\\n\") }\n\n        vim.lsp.util.apply_text_edits({ edit1, edit2 }, bufnr, \"utf-8\")\n\n        if cursor_to_second then\n            -- set jump location\n            vim.cmd(\"normal! m'\")\n\n            local char_delta = 0\n            local line_delta = 0\n            if\n                range1[\"end\"].line < range2.start.line\n                or (range1[\"end\"].line == range2.start.line and range1[\"end\"].character <= range2.start.character)\n            then\n                line_delta = #text2 - #text1\n            end\n\n            if range1[\"end\"].line == range2.start.line and range1[\"end\"].character <= range2.start.character then\n                if line_delta ~= 0 then\n                    --- why?\n                    --correction_after_line_change =  -range2.start.character\n                    --text_now_before_range2 = #(text2[#text2])\n                    --space_between_ranges = range2.start.character - range1[\"end\"].character\n                    --char_delta = correction_after_line_change + text_now_before_range2 + space_between_ranges\n                    --- Equivalent to:\n                    char_delta = #text2[#text2] - range1[\"end\"].character\n\n                    -- add range1.start.character if last line of range1 (now text2) does not start at 0\n                    if range1.start.line == range2.start.line + line_delta then\n                        char_delta = char_delta + range1.start.character\n                    end\n                else\n                    char_delta = #text2[#text2] - #text1[#text1]\n                end\n            end\n\n            vim.api.nvim_win_set_cursor(\n                vim.api.nvim_get_current_win(),\n                { range2.start.line + 1 + line_delta, range2.start.character + char_delta }\n            )\n        end\n    end,\n\n    --- Returns the first node of given type if present\n    ---@deprecated use get_first_node_recursive instead\n    ---@param type string #The type of node to search for\n    ---@param buf number #The buffer to search in\n    ---@param parent userdata #The node to start searching in\n    get_first_node = function(type, buf, parent)\n        if not buf then\n            buf = 0\n        end\n\n        local function iterate(parent_node)\n            for child, _ in parent_node:iter_children() do\n                if child:type() == type then\n                    return child\n                end\n            end\n        end\n\n        if parent then\n            return iterate(parent)\n        end\n\n        vim.treesitter.get_parser(buf, \"norg\"):for_each_tree(function(tree)\n            -- FIXME: this return value doesn't do what the original author thinks it does\n            -- Iterate over all top-level children and attempt to find a match\n            return iterate(tree:root()) ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n        end)\n    end,\n    --- Recursively attempts to locate a node of a given type\n    ---@param type string #The type of node to look for\n    ---@param opts {buf: number?, ft: string?, parent: TSNode?} # Buffer, filetype (for TS parsing),\n    ---parent, defaults to root node\n    ---@return TSNode?\n    get_first_node_recursive = function(type, opts)\n        opts = opts or {}\n        local result\n\n        if not opts.buf then\n            opts.buf = 0\n        end\n\n        if not opts.ft then\n            opts.ft = \"norg\"\n        end\n\n        -- Do we need to go through each tree? lol\n        vim.treesitter.get_parser(opts.buf, opts.ft):for_each_tree(function(tree)\n            -- Get the root for that tree\n            local root\n            if opts.parent then\n                root = opts.parent\n            else\n                root = tree:root()\n            end\n\n            if not root then\n                return\n            end\n\n            --- Recursively searches for a node of a given type\n            ---@param node TSNode #The starting point for the search\n            local function descend(node)\n                -- Iterate over all children of the node and try to match their type\n                for child, _ in node:iter_children() do\n                    if child:type() == type then\n                        return child\n                    else\n                        -- If no match is found try descending further down the syntax tree\n                        local descent = descend(child)\n                        if descent then\n                            return descent\n                        end\n                    end\n                end\n\n                return nil\n            end\n\n            result = result or descend(root)\n        end)\n\n        return result\n    end,\n\n    --- Given a node this function will break down the AST elements and return the corresponding text for certain nodes\n    --- @param tag_node TSNode - a node of type tag/carryover_tag\n    --- @param throw boolean? - when true, throw an error instead of logging and returning on failure\n    get_tag_info = function(tag_node, throw)\n        if\n            not tag_node\n            or not vim.tbl_contains(\n                { \"ranged_tag\", \"ranged_verbatim_tag\", \"weak_carryover\", \"strong_carryover\" },\n                tag_node:type()\n            )\n        then\n            return nil\n        end\n\n        local start_row, start_column, end_row, end_column = tag_node:range()\n\n        local attributes = {}\n        local resulting_name, params, content = {}, {}, {}\n        local content_start_column = 0\n\n        -- Iterate over all children of the tag node\n        for child, _ in tag_node:iter_children() do\n            -- If we are dealing with a weak/strong attribute set then parse that set\n            if vim.endswith(child:type(), \"_carryover_set\") then\n                for subchild in child:iter_children() do\n                    if vim.endswith(subchild:type(), \"_carryover\") then\n                        local meta = module.public.get_tag_info(subchild, throw)\n\n                        table.insert(attributes, meta)\n                    end\n                end\n            elseif child:type() == \"tag_name\" then\n                -- If we're dealing with the tag name then append the text of the tag_name node to this table\n                table.insert(resulting_name, vim.split(module.public.get_node_text(child), \"\\n\")[1])\n            elseif child:type() == \"tag_parameters\" then\n                table.insert(params, vim.split(module.public.get_node_text(child), \"\\n\")[1])\n            elseif child:type() == \"ranged_verbatim_tag_content\" then\n                -- If we're dealing with tag content then retrieve that content\n                content = vim.split(module.public.get_node_text(child), \"\\n\")\n                _, content_start_column = child:range()\n            end\n        end\n\n        for i, line in ipairs(content) do\n            if i == 1 then\n                if content_start_column < start_column then\n                    local error_msg = string.format(\n                        \"Unable to query information about tag on line %d: content is indented less than tag start!\",\n                        start_row + 1\n                    )\n\n                    if throw then\n                        error(error_msg)\n                    else\n                        log.error(error_msg)\n                        return nil\n                    end\n                end\n                content[i] = string.rep(\" \", content_start_column - start_column) .. line\n            else\n                content[i] = line:sub(1 + start_column)\n            end\n        end\n\n        content[#content] = nil\n\n        return {\n            name = table.concat(resulting_name, \".\"),\n            parameters = params,\n            content = content,\n            attributes = vim.fn.reverse(attributes),\n            start = { row = start_row, column = start_column },\n            [\"end\"] = { row = end_row, column = end_column },\n        }\n    end,\n    --- Gets the range of the given node\n    ---@param node TSNode\n    ---@return { row_start: number, column_start: number, row_end: number, column_end: number } range\n    get_node_range = function(node)\n        if not node then\n            return {\n                row_start = 0,\n                column_start = 0,\n                row_end = 0,\n                column_end = 0,\n            }\n        end\n\n        local rs, cs, re, ce = lib.when(type(node) == \"table\", function()\n            local brs, bcs, _, _ = node[1]:range()\n            local _, _, ere, ece = node[#node]:range()\n            return brs, bcs, ere, ece\n        end, function()\n            local a, b, c, d = node:range()\n            return a, b, c, d\n        end)\n\n        return {\n            row_start = rs,\n            column_start = cs,\n            row_end = re,\n            column_end = ce,\n        }\n    end,\n    --- Extracts the document root from the current document or from the string\n    ---@param src number|string|nil The number of the buffer to extract or string with code\n    ---@param filetype string? #The filetype of the buffer or the string with code\n    ---@return TSNode? #The root node of the document\n    get_document_root = function(src, filetype)\n        filetype = filetype or \"norg\"\n\n        local parser\n        if type(src) == \"string\" then\n            parser = vim.treesitter.get_string_parser(src, filetype)\n        else\n            if src ~= 0 and src ~= nil then\n                vim.fn.bufload(src)\n            end\n            parser = vim.treesitter.get_parser(src or 0, filetype)\n        end\n\n        if not parser then\n            return\n        end\n\n        local tree = parser:parse()[1]\n\n        if not tree or not tree:root() then\n            return\n        end\n\n        return tree:root()\n    end,\n\n    --- Attempts to find a parent of a node recursively\n    ---@param node TSNode #The node to start at\n    ---@param types table|string #If `types` is a table, this function will attempt to match any of the types present in the table.\n    -- If the type is a string, the function will attempt to pattern match the `types` value with the node type.\n    ---@return TSNode?\n    find_parent = function(node, types)\n        ---@type TSNode?\n        local _node = node\n\n        while _node do\n            if type(types) == \"string\" then\n                if _node:type():match(types) then\n                    return _node\n                end\n            elseif vim.tbl_contains(types, _node:type()) then\n                return _node\n            end\n\n            _node = _node:parent()\n        end\n    end,\n\n    --- Retrieves the first node at a specific line\n    ---@param buf number #The buffer to search in (0 for current)\n    ---@param line number #The line number (0-indexed) to get the node from the same line as `line`.\n    ---@param stop_type string|table? #Don't recurse to the provided type(s)\n    ---@return TSNode|nil #The first node on `line`\n    get_first_node_on_line = function(buf, line, stop_type)\n        if type(stop_type) == \"string\" then\n            stop_type = { stop_type }\n        end\n\n        local document_root = module.public.get_document_root(buf)\n\n        if not document_root then\n            return\n        end\n\n        local first_char = (vim.api.nvim_buf_get_lines(buf, line, line + 1, true)[1] or \"\"):match(\"^(%s+)[^%s]\")\n        first_char = first_char and first_char:len() or 0\n\n        local descendant = document_root:descendant_for_range(line, first_char, line, first_char + 1)\n\n        if not descendant then\n            return\n        end\n\n        while\n            descendant:parent()\n            and (descendant:parent():start()) == line\n            and descendant:parent():symbol() ~= document_root:symbol() ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n        do\n            local parent = descendant:parent()\n\n            if parent and stop_type and vim.tbl_contains(stop_type, parent:type()) then\n                break\n            end\n\n            if not parent then\n                return\n            end\n\n            descendant = parent\n        end\n\n        return descendant\n    end,\n\n    ---get document's metadata\n    ---@param source number | string | PathlibPath\n    ---@param no_trim boolean?\n    ---@return table?\n    get_document_metadata = function(source, no_trim)\n        source = source or 0\n\n        local norg_parser, iter_src = module.public.get_ts_parser(source)\n        if not norg_parser then\n            return\n        end\n\n        local norg_tree = norg_parser:parse()[1]\n        if not norg_tree then\n            return\n        end\n\n        local function trim(value)\n            return no_trim and value or vim.trim(value)\n        end\n\n        local result = {}\n        local function parse_data(node, src)\n            return lib.match(node:type())({\n                string = function()\n                    return trim(module.public.get_node_text(node, src))\n                end,\n                number = function()\n                    return tonumber(module.public.get_node_text(node, src))\n                end,\n                array = function()\n                    local resulting_array = {}\n\n                    for child in node:iter_children() do\n                        if child:named() then\n                            local parsed_data = parse_data(child, src)\n\n                            if parsed_data then\n                                table.insert(resulting_array, parsed_data)\n                            end\n                        end\n                    end\n\n                    return resulting_array\n                end,\n                object = function()\n                    local resulting_object = {}\n\n                    for child in node:iter_children() do\n                        if not child:named() or child:type() ~= \"pair\" then\n                            goto continue\n                        end\n\n                        local key = child:named_child(0)\n                        local value = child:named_child(1)\n\n                        if not key then\n                            goto continue\n                        end\n\n                        local key_content = trim(module.public.get_node_text(key, src))\n\n                        resulting_object[key_content] = (value and parse_data(value, src) or vim.NIL)\n\n                        ::continue::\n                    end\n\n                    return resulting_object\n                end,\n            })\n        end\n\n        local norg_query = utils.ts_parse_query(\n            \"norg\",\n            [[\n                (document\n                  (ranged_verbatim_tag\n                    (tag_name) @tag_name\n                    (ranged_verbatim_tag_content) @tag_content\n                  )\n                )\n            ]]\n        )\n\n        local meta_query = utils.ts_parse_query(\n            \"norg_meta\",\n            [[\n                (metadata\n                  (pair\n                    (key) @key\n                    (value) @value\n                  )\n                )\n            ]]\n        )\n\n        local meta_node\n        for id, node in norg_query:iter_captures(norg_tree:root(), iter_src) do\n            if norg_query.captures[id] == \"tag_name\" then\n                local tag_name = trim(module.public.get_node_text(node, iter_src))\n                if tag_name == \"document.meta\" then\n                    meta_node = node:next_named_sibling() or vim.NIL\n                    break\n                end\n            end\n        end\n\n        if not meta_node then\n            return result\n        end\n\n        local meta_source = module.public.get_node_text(meta_node, iter_src)\n\n        local norg_meta_parser = vim.treesitter.get_string_parser(meta_source, \"norg_meta\")\n\n        local norg_meta_tree = norg_meta_parser:parse()[1]\n\n        if not norg_meta_tree then\n            return\n        end\n\n        for id, node in meta_query:iter_captures(norg_meta_tree:root(), meta_source) do\n            if meta_query.captures[id] == \"key\" then\n                local key = trim(module.public.get_node_text(node, meta_source))\n\n                local val\n                if key == \"title\" then\n                    -- force title's value as string type\n                    val = trim(module.public.get_node_text(node:next_named_sibling(), meta_source))\n                else\n                    val = node:next_named_sibling() and parse_data(node:next_named_sibling(), meta_source) or vim.NIL\n                end\n\n                result[key] = val\n            end\n        end\n\n        return result\n    end,\n\n    --- Parses a query and automatically executes it for Norg\n    ---@param query_string string #The query string\n    ---@param callback function #The callback to execute with all values returned by\n    ---`Query:iter_captures()`. When callback returns true, this function returns early\n    ---@param source number | string | PathlibPath #buf number, or file path or 0 for current buffer\n    ---@param start number? #The start line for the query\n    ---@param finish number? #The end line for the query\n    execute_query = function(query_string, callback, source, start, finish)\n        local query = utils.ts_parse_query(\"norg\", query_string)\n        local norg_parser, iter_src = module.public.get_ts_parser(source)\n\n        if not norg_parser then\n            return false\n        end\n\n        local root = norg_parser:parse()[1]:root()\n        for id, node, metadata in query:iter_captures(root, iter_src, start, finish) do\n            if callback(query, id, node, metadata) == true then\n                return true\n            end\n        end\n\n        return true\n    end,\n\n    ---Create a norg TS parser from the given source\n    ---@param source string | number | PathlibPath file path or buf number or 0 for current buffer\n    ---@return vim.treesitter.LanguageTree? norg_parser\n    ---@return string | number iter_src the corresponding source that you must pass to\n    ---`iter_query()`, either the full file text, or the buffer number\n    get_ts_parser = function(source)\n        local norg_parser\n        local iter_src\n        if type(source) ~= \"string\" and type(source) ~= \"number\" then\n            source = tostring(source)\n        end\n        if type(source) == \"string\" then\n            -- check if the file is open; use the buffer contents if it is\n            if vim.fn.bufnr(source) ~= -1 then ---@diagnostic disable-line\n                source = vim.uri_to_bufnr(vim.uri_from_fname(source))\n            else\n                iter_src = io.open(source, \"r\"):read(\"*a\")\n                norg_parser = vim.treesitter.get_string_parser(iter_src, \"norg\")\n            end\n        end\n        if type(source) == \"number\" then\n            if source == 0 then\n                source = vim.api.nvim_get_current_buf()\n            end\n            norg_parser = vim.treesitter.get_parser(source, \"norg\")\n            iter_src = source\n        end\n\n        return norg_parser, iter_src\n    end,\n}\n\n--[[\n-- attribution notice:\n-- The below public functions are originally licensed under Apache v2 taken from:\n-- https://github.com/nvim-treesitter/nvim-treesitter/blob/master/lua/nvim-treesitter/ts_utils.lua\n--]]\n\n-- Get previous node with same parent\n---@param node                   TSNode\n---@param allow_switch_parents?  boolean allow switching parents if first node\n---@param allow_previous_parent? boolean allow previous parent if first node and previous parent without children\nmodule.public.get_previous_node = function(node, allow_switch_parents, allow_previous_parent)\n    local destination_node ---@type TSNode?\n    local parent = node:parent()\n    if not parent then\n        return\n    end\n\n    local found_pos = 0\n    for i = 0, parent:named_child_count() - 1, 1 do\n        if parent:named_child(i) == node then\n            found_pos = i\n            break\n        end\n    end\n    if 0 < found_pos then\n        destination_node = parent:named_child(found_pos - 1)\n    elseif allow_switch_parents then\n        local previous_node = module.public.get_previous_node(parent)\n        if previous_node and previous_node:named_child_count() > 0 then\n            destination_node = previous_node:named_child(previous_node:named_child_count() - 1)\n        elseif previous_node and allow_previous_parent then\n            destination_node = previous_node\n        end\n    end\n    return destination_node\nend\n\nmodule.public.goto_node = function(node, goto_end, avoid_set_jump)\n    if not node then\n        return\n    end\n    if not avoid_set_jump then\n        vim.cmd(\"normal! m'\")\n    end\n    local range = module.public.get_node_range(node)\n\n    ---@type table<number>\n    local position\n    if not goto_end then\n        position = { range.row_start, range.column_start }\n    else\n        position = { range.row_end, range.column_end }\n    end\n\n    -- Enter visual mode if we are in operator pending mode\n    -- If we don't do this, it will miss the last character.\n    local mode = vim.api.nvim_get_mode()\n    if mode.mode == \"no\" then\n        vim.cmd(\"normal! v\")\n    end\n\n    vim.api.nvim_win_set_cursor(0, { position[1] + 1, position[2] })\nend\n\n-- Get next node with same parent\n---@param node                  TSNode\n---@param allow_switch_parents? boolean allow switching parents if last node\n---@param allow_next_parent?    boolean allow next parent if last node and next parent without children\nmodule.public.get_next_node = function(node, allow_switch_parents, allow_next_parent)\n    local destination_node ---@type TSNode?\n    local parent = node:parent()\n\n    if not parent then\n        return\n    end\n    local found_pos = 0\n    for i = 0, parent:named_child_count() - 1, 1 do\n        if parent:named_child(i) == node then\n            found_pos = i\n            break\n        end\n    end\n    if parent:named_child_count() > found_pos + 1 then\n        destination_node = parent:named_child(found_pos + 1)\n    elseif allow_switch_parents then\n        local next_node = module.public.get_next_node(parent)\n        if next_node and next_node:named_child_count() > 0 then\n            destination_node = next_node:named_child(0)\n        elseif next_node and allow_next_parent then\n            destination_node = next_node\n        end\n    end\n\n    return destination_node\nend\n\nmodule.on_event = function(event)\n    if event.split_type[2] == \"sync-parsers\" then\n        local install = require(\"nvim-treesitter.install\")\n        install.commands.TSInstallSync[\"run!\"](\"norg\")\n        install.commands.TSInstallSync[\"run!\"](\"norg_meta\")\n    end\nend\n\nmodule.events.subscribed = {\n    [\"core.neorgcmd\"] = {\n        [\"sync-parsers\"] = true,\n    },\n}\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/integrations/truezen/module.lua",
    "content": "--[[\n    file: Truezen-Integration\n    title: A TrueZen integration for Neorg\n    summary: Integrates the TrueZen module for use within Neorg.\n    internal: true\n    ---\nThis is a basic wrapper around truezen that allows one to toggle the atraxis mode programmatically.\n--]]\n\nlocal neorg = require(\"neorg.core\")\nlocal modules, log = neorg.modules, neorg.log\n\nlocal module = modules.create(\"core.integrations.truezen\")\n\nmodule.setup = function()\n    local success, truezen = pcall(require, \"true-zen\")\n\n    if not success then\n        log.warn(\"Could not find module: `true-zen`. Please ensure you have true-zen installed.\")\n\n        return { success = false }\n    end\n\n    module.private.truezen = truezen\nend\n\nmodule.private = {\n    truezen = nil,\n}\n\n---@class core.integrations.truezen\nmodule.public = {\n    toggle_ataraxis = function()\n        vim.cmd(\":TZAtaraxis\")\n    end,\n}\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/integrations/zen_mode/module.lua",
    "content": "--[[\n    file: ZenMode-Integration\n    title: An integration for `zen-mode`\n    summary: Integrates and exposes the functionality of `zen-mode` in Neorg.\n    internal: true\n    ---\nThis is a basic wrapper around `zen_mode` that allows one to toggle the zen mode programatically.\n--]]\n\nlocal neorg = require(\"neorg.core\")\nlocal modules = neorg.modules\n\nlocal module = modules.create(\"core.integrations.zen_mode\")\n\nmodule.setup = function()\n    local success, zen_mode = pcall(require, \"zen_mode\")\n\n    if not success then\n        return { success = false }\n    end\n\n    module.private.zen_mode = zen_mode\nend\n\nmodule.private = {\n    zen_mode = nil,\n}\n\n---@class core.integrations.zen_mode\nmodule.public = {\n    toggle = function()\n        vim.cmd(\":ZenMode\")\n    end,\n}\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/itero/module.lua",
    "content": "--[[\n    file: Itero\n    title: Fast List/Heading Continuation\n    description: Fluidness is key, after all.\n    summary: Module designed to continue lists, headings and other iterables.\n    embed: https://user-images.githubusercontent.com/76052559/216777858-14e2036e-acc5-4276-aa7d-9a8a8ba549ba.gif\n    ---\n`core.itero` is a rather small and simple module designed to assist in the creation of many lists,\nheadings and other repeatable (iterable) items.\n\nBy default, the key that is used to iterate on an item is `<M-CR>` (Alt + Enter). If you want to\nchange the bind, remap the `neorg.itero.next-iteration` event.\n\nBegin by writing an initial item you'd like to iterate (in this instance, and unordered list item):\n```md\n- Hello World!\n```\n\nWith your cursor in insert mode at the end of the line, pressing the keybind will continue the item at whatever\nnesting level it is currently at (where `|` is the new cursor position):\n```md\n- Hello World!\n- |\n```\n\nThe same can also be done for headings:\n```md\n* Heading 1\n* |\n```\n\nThis functionality is commonly paired with the [`core.promo`](@core.promo) module to then indent/dedent\nthe item under the cursor with the `<C-t>` and `<C-d>` bindings.\n--]]\n\nlocal neorg = require(\"neorg.core\")\nlocal lib, log, modules = neorg.lib, neorg.log, neorg.modules\n\nlocal module = modules.create(\"core.itero\")\n\nmodule.setup = function()\n    return {\n        requires = {\n            \"core.integrations.treesitter\",\n        },\n    }\nend\n\nmodule.config.public = {\n    -- A list of lua patterns detailing what treesitter nodes can be \"iterated\".\n    -- Usually doesn't need to be changed, unless you want to disable some\n    -- items from being iterable.\n    iterables = {\n        \"unordered_list%d\",\n        \"ordered_list%d\",\n        \"heading%d\",\n        \"quote%d\",\n    },\n\n    -- Which item types to retain extensions for.\n    --\n    -- If the item you are currently iterating has an extension (e.g. `( )`, `(x)` etc.),\n    -- then the following items will also have an extension (by default `( )`) attached\n    -- to them automatically.\n    retain_extensions = {\n        [\"unordered_list%d\"] = true,\n        [\"ordered_list%d\"] = true,\n    },\n}\n\nmodule.config.private = {\n    stop_types = {\n        \"generic_list\",\n        \"quote\",\n    },\n}\n\nmodule.load = function()\n    vim.keymap.set(\"!\", \"<Plug>(neorg.itero.next-iteration)\", module.public.next_iteration_cr)\nend\n\n---@class core.itero\nmodule.public = {\n    next_iteration_cr = function()\n        local cursor = vim.api.nvim_win_get_cursor(0)\n        local buffer = vim.api.nvim_get_current_buf()\n\n        local ts = module.required[\"core.integrations.treesitter\"]\n        local cursor_pos = cursor[1] - 1\n\n        local current = ts.get_first_node_on_line(buffer, cursor_pos, module.config.private.stop_types)\n\n        if not current then\n            log.error(\n                \"Treesitter seems to be high and can't properly grab the node under the cursor. Perhaps try again?\"\n            )\n            return\n        end\n\n        while current and current:parent() do\n            if\n                lib.filter(module.config.public.iterables, function(_, iterable)\n                    return current:type():match(table.concat({ \"^\", iterable, \"$\" })) and iterable or nil\n                end)\n            then\n                break\n            end\n\n            current = current:parent()\n        end\n\n        if not current or current:type() == \"document\" then\n            vim.api.nvim_feedkeys(vim.keycode(\"<CR>\"), \"n\", false)\n            return\n        end\n\n        local should_append_extension = lib.filter(\n            module.config.public.retain_extensions,\n            function(match, should_append)\n                return current:type():match(match) and should_append or nil\n            end\n        ) and current:named_child(1) and current:named_child(1):type() == \"detached_modifier_extension\"\n\n        local text_to_repeat = ts.get_node_text(current:named_child(0), buffer)\n\n        local _, column = current:start()\n\n        local is_on_nonempty_line = vim.api.nvim_buf_get_lines(buffer, cursor_pos, cursor_pos + 1, true)[1]:match(\"%S\")\n        if is_on_nonempty_line then\n            cursor_pos = cursor_pos + 1\n        end\n\n        vim.api.nvim_buf_set_lines(\n            buffer,\n            cursor_pos,\n            cursor_pos + (is_on_nonempty_line and 0 or 1),\n            true,\n            { string.rep(\" \", column) .. text_to_repeat .. (should_append_extension and \"( ) \" or \"\") }\n        )\n        vim.api.nvim_win_set_cursor(\n            0,\n            { cursor_pos + 1, column + text_to_repeat:len() + (should_append_extension and (\"( ) \"):len() or 0) }\n        )\n    end,\n}\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/journal/module.lua",
    "content": "--[[\n    file: Journal\n    title: Dear diary...\n    description: The journal module allows you to take personal notes with zero friction.\n    summary: Easily track a journal within Neorg.\n    ---\nThe journal module exposes a total of six commands.\nThe first three, `:Neorg journal today|yesterday|tomorrow`, allow you to access entries\nfor a given time relative to today. A file will be opened with the respective date as a `.norg` file.\n\nThe fourth command, `:Neorg journal custom`, allows you to specify a custom date as an argument.\nThe date must be formatted according to the `YYYY-mm-dd` format, e.g. `2023-01-01`.\n\nThe `:Neorg journal template` command creates a template file which will be used as the base whenever\na new journal entry is created.\n\nLast but not least, the `:Neorg journal toc open|update` commands open or create/update a Table of Contents\nfile found in the root of the journal. This file contains links to all other journal entries, alongside\ntheir titles.\n--]]\n\nlocal neorg = require(\"neorg.core\")\nlocal config, lib, log, modules = neorg.config, neorg.lib, neorg.log, neorg.modules\n\nlocal module = modules.create(\"core.journal\")\n\nmodule.examples = {\n    [\"Changing TOC format to divide year in quarters\"] = function()\n        -- In your [\"core.journal\"] options, change toc_format to a function like this:\n\n        require(\"neorg\").setup({\n            load = {\n                -- ...\n                [\"core.journal\"] = {\n                    config = {\n                        -- ...\n                        toc_format = function(entries)\n                            -- Convert the entries into a certain format\n\n                            local output = {}\n                            local current_year\n                            local current_quarter\n                            local last_quarter\n                            local current_month\n                            for _, entry in ipairs(entries) do\n                                -- Don't print the year if it hasn't changed\n                                if not current_year or current_year < entry[1] then\n                                    current_year = entry[1]\n                                    current_month = nil\n                                    table.insert(output, \"* \" .. current_year)\n                                end\n\n                                -- Check to which quarter the current month corresponds to\n                                if entry[2] <= 3 then\n                                    current_quarter = 1\n                                elseif entry[2] <= 6 then\n                                    current_quarter = 2\n                                elseif entry[2] <= 9 then\n                                    current_quarter = 3\n                                else\n                                    current_quarter = 4\n                                end\n\n                                -- If the current month corresponds to another quarter, print it\n                                if current_quarter ~= last_quarter then\n                                    table.insert(output, \"** Quarter \" .. current_quarter)\n                                    last_quarter = current_quarter\n                                end\n\n                                -- Don't print the month if it hasn't changed\n                                if not current_month or current_month < entry[2] then\n                                    current_month = entry[2]\n                                    table.insert(output, \"*** Month \" .. current_month)\n                                end\n\n                                -- Prints the file link\n                                table.insert(output, \"   \" .. entry[4] .. string.format(\"[%s]\", entry[5]))\n                            end\n\n                            return output\n                        end,\n                        -- ...\n                    },\n                },\n            },\n        })\n    end,\n}\n\nmodule.setup = function()\n    return {\n        success = true,\n        requires = {\n            \"core.dirman\",\n            \"core.integrations.treesitter\",\n        },\n    }\nend\n\n---@type core.integrations.treesitter\nlocal ts\nmodule.load = function()\n    ts = module.required[\"core.integrations.treesitter\"] --[[@as core.integrations.treesitter]]\n\n    if module.config.private.strategies[module.config.public.strategy] then\n        module.config.public.strategy = module.config.private.strategies[module.config.public.strategy]\n    end\n\n    modules.await(\"core.neorgcmd\", function(neorgcmd)\n        neorgcmd.add_commands_from_table({\n            journal = {\n                min_args = 1,\n                max_args = 2,\n                subcommands = {\n                    tomorrow = { args = 0, name = \"journal.tomorrow\" },\n                    yesterday = { args = 0, name = \"journal.yesterday\" },\n                    today = { args = 0, name = \"journal.today\" },\n                    custom = { max_args = 1, name = \"journal.custom\" }, -- format :yyyy-mm-dd\n                    template = { args = 0, name = \"journal.template\" },\n                    toc = {\n                        args = 1,\n                        name = \"journal.toc\",\n                        subcommands = {\n                            open = { args = 0, name = \"journal.toc.open\" },\n                            update = { args = 0, name = \"journal.toc.update\" },\n                        },\n                    },\n                },\n            },\n        })\n    end)\nend\n\nmodule.config.public = {\n    -- Which workspace to use for the journal files, the default behaviour\n    -- is to use the current workspace.\n    --\n    -- It is recommended to set this to a static workspace, but the most optimal\n    -- behaviour may vary from workflow to workflow.\n    workspace = nil,\n\n    -- The name for the folder in which the journal files are put.\n    journal_folder = \"journal\",\n\n    -- The strategy to use to create directories.\n    -- May be \"flat\" (`2022-03-02.norg`), \"nested\" (`2022/03/02.norg`),\n    -- a lua string with the format given to `os.date()` or a lua function\n    -- that returns a lua string with the same format.\n    strategy = \"nested\",\n\n    -- The name of the template file to use when running `:Neorg journal template`.\n    template_name = \"template.norg\",\n\n    -- Whether to apply the template file to new journal entries.\n    use_template = true,\n\n    -- Formatter function used to generate the toc file.\n    -- Receives a table that contains tables like { yy, mm, dd, link, title }.\n    --\n    -- The function must return a table of strings.\n    toc_format = nil,\n}\n\nmodule.config.private = {\n    strategies = {\n        flat = \"%Y-%m-%d.norg\",\n        nested = \"%Y\" .. config.pathsep .. \"%m\" .. config.pathsep .. \"%d.norg\",\n    },\n}\n\n---@class core.journal\nmodule.public = {\n    version = \"0.0.9\",\n\n    --- Opens a diary entry at the given time\n    ---@param time? number #The time to open the journal entry at as returned by `os.time()`\n    ---@param custom_date? string #A YYYY-mm-dd string that specifies a date to open the diary at instead\n    open_diary = function(time, custom_date)\n        -- TODO(vhyrro): Change this to use Norg dates!\n        local workspace = module.config.public.workspace or module.required[\"core.dirman\"].get_current_workspace()[1]\n        local folder_name = module.config.public.journal_folder\n        local template_name = module.config.public.template_name\n\n        if custom_date then\n            local year, month, day = custom_date:match(\"^(%d%d%d%d)-(%d%d)-(%d%d)$\")\n\n            if not year or not month or not day then\n                log.error(\"Wrong date format: use YYYY-mm-dd\")\n                return\n            end\n\n            time = os.time({\n                year = year,\n                month = month,\n                day = day,\n            })\n        end\n\n        local path = os.date(\n            type(module.config.public.strategy) == \"function\" and module.config.public.strategy(os.date(\"*t\", time))\n                or module.config.public.strategy,\n            time\n        )\n\n        local workspace_path = module.required[\"core.dirman\"].get_workspace(workspace)\n\n        local journal_file_exists =\n            module.required[\"core.dirman\"].file_exists(workspace_path .. \"/\" .. folder_name .. config.pathsep .. path)\n\n        module.required[\"core.dirman\"].create_file(folder_name .. config.pathsep .. path, workspace)\n\n        module.required[\"core.dirman\"].create_file(folder_name .. config.pathsep .. path, workspace)\n\n        if\n            not journal_file_exists\n            and module.config.public.use_template\n            and module.required[\"core.dirman\"].file_exists(workspace_path .. \"/\" .. folder_name .. \"/\" .. template_name)\n        then\n            vim.cmd(\"$read \" .. workspace_path .. \"/\" .. folder_name .. \"/\" .. template_name .. \"| 1d | w\")\n        end\n    end,\n\n    --- Opens a diary entry for tomorrow's date\n    diary_tomorrow = function()\n        module.public.open_diary(os.time() + 24 * 60 * 60)\n    end,\n\n    --- Opens a diary entry for yesterday's date\n    diary_yesterday = function()\n        module.public.open_diary(os.time() - 24 * 60 * 60)\n    end,\n\n    --- Opens a diary entry for today's date\n    diary_today = function()\n        module.public.open_diary()\n    end,\n\n    create_template = function()\n        local workspace = module.config.public.workspace\n        local folder_name = module.config.public.journal_folder\n        local template_name = module.config.public.template_name\n\n        module.required[\"core.dirman\"].create_file(\n            folder_name .. config.pathsep .. template_name,\n            workspace or module.required[\"core.dirman\"].get_current_workspace()[1]\n        )\n    end,\n\n    --- Opens the toc file\n    open_toc = function()\n        local workspace = module.config.public.workspace or module.required[\"core.dirman\"].get_current_workspace()[1]\n        local index = modules.get_module_config(\"core.dirman\").index\n        local folder_name = module.config.public.journal_folder\n\n        -- If the toc exists, open it, if not, create it\n        if module.required[\"core.dirman\"].file_exists(folder_name .. config.pathsep .. index) then\n            module.required[\"core.dirman\"].open_file(workspace, folder_name .. config.pathsep .. index)\n        else\n            module.public.create_toc()\n        end\n    end,\n\n    --- Creates or updates the toc file\n    create_toc = function()\n        local workspace = module.config.public.workspace or module.required[\"core.dirman\"].get_current_workspace()[1]\n        local index = modules.get_module_config(\"core.dirman\").index\n        local workspace_path = module.required[\"core.dirman\"].get_workspace(workspace)\n        local workspace_name_for_links = module.config.public.workspace or \"\"\n        local folder_name = module.config.public.journal_folder\n\n        -- Each entry is a table that contains tables like { yy, mm, dd, link, title }\n        local toc_entries = {}\n\n        -- Get a filesystem handle for the files in the journal folder\n        -- path is for each subfolder\n        local get_fs_handle = function(path)\n            path = path or \"\"\n            local handle =\n                vim.loop.fs_scandir(workspace_path .. config.pathsep .. folder_name .. config.pathsep .. path)\n\n            if type(handle) ~= \"userdata\" then\n                error(lib.lazy_string_concat(\"Failed to scan directory '\", workspace, path, \"': \", handle))\n            end\n\n            return handle\n        end\n\n        ---Gets the title from the metadata of a file, must be called in a vim.schedule\n        ---@param file PathlibPath | string\n        ---@return string?\n        local get_title = function(file)\n            local path = workspace_path / folder_name / file\n            local meta\n            if vim.fn.bufexists(tostring(path)) == 1 then\n                local buf = vim.fn.bufloaded(vim.fn.bufnr(tostring(path)))\n                meta = ts.get_document_metadata(buf)\n            else\n                meta = ts.get_document_metadata(path)\n            end\n            if meta then\n                return meta.title\n            end\n        end\n\n        vim.loop.fs_scandir(workspace_path .. config.pathsep .. folder_name .. config.pathsep, function(err, handle)\n            assert(not err, lib.lazy_string_concat(\"Unable to generate TOC for directory '\", folder_name, \"' - \", err))\n\n            while true do\n                -- Name corresponds to either a YYYY-mm-dd.norg file, or just the year (\"nested\" strategy)\n                local name, type = vim.loop.fs_scandir_next(handle) ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n\n                if not name then\n                    break\n                end\n\n                -- Handle nested entries\n                if type == \"directory\" then\n                    local years_handle = get_fs_handle(name)\n                    while true do\n                        -- mname is the month\n                        local mname, mtype = vim.loop.fs_scandir_next(years_handle)\n\n                        if not mname then\n                            break\n                        end\n\n                        if mtype == \"directory\" then\n                            local months_handle = get_fs_handle(name .. config.pathsep .. mname)\n                            while true do\n                                -- dname is the day\n                                local dname, dtype = vim.loop.fs_scandir_next(months_handle)\n\n                                if not dname then\n                                    break\n                                end\n\n                                -- If it's a .norg file, also ensure it is a day entry\n                                if dtype == \"file\" and string.match(dname, \"%d%d%.norg\") then\n                                    -- Split the file name\n                                    local file = vim.split(dname, \".\", { plain = true })\n\n                                    vim.schedule(function()\n                                        -- Get the title from the metadata, else, it just defaults to the name of the file\n                                        local title = get_title(\n                                            name .. config.pathsep .. mname .. config.pathsep .. dname\n                                        ) or file[1]\n\n                                        -- Insert a new entry\n                                        table.insert(toc_entries, {\n                                            tonumber(name),\n                                            tonumber(mname),\n                                            tonumber(file[1]),\n                                            \"{:$\"\n                                                .. workspace_name_for_links\n                                                .. config.pathsep\n                                                .. module.config.public.journal_folder\n                                                .. config.pathsep\n                                                .. name\n                                                .. config.pathsep\n                                                .. mname\n                                                .. config.pathsep\n                                                .. file[1]\n                                                .. \":}\",\n                                            title,\n                                        })\n                                    end)\n                                end\n                            end\n                        end\n                    end\n                end\n\n                -- Handles flat entries\n                -- If it is a .norg file, but it's not any user generated file.\n                -- The match is here to avoid handling files made by the user, like a template file, or\n                -- the toc file\n                if type == \"file\" and string.match(name, \"%d+-%d+-%d+%.norg\") then\n                    -- Split yyyy-mm-dd to a table\n                    local file = vim.split(name, \".\", { plain = true })\n                    local parts = vim.split(file[1], \"-\")\n\n                    -- Convert the parts into numbers\n                    for k, v in pairs(parts) do\n                        parts[k] = tonumber(v) ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n                    end\n\n                    vim.schedule(function()\n                        -- Get the title from the metadata, else, it just defaults to the name of the file\n                        local title = get_title(name) or parts[3]\n\n                        -- And insert a new entry that corresponds to the file\n                        table.insert(toc_entries, {\n                            parts[1],\n                            parts[2],\n                            parts[3],\n                            \"{:$\"\n                                .. workspace_name_for_links\n                                .. config.pathsep\n                                .. module.config.public.journal_folder\n                                .. config.pathsep\n                                .. file[1]\n                                .. \":}\",\n                            title,\n                        })\n                    end)\n                end\n            end\n\n            vim.schedule(function()\n                -- Gets a default format for the entries\n                local format = module.config.public.toc_format\n                    or function(entries)\n                        local months_text = {\n                            \"January\",\n                            \"February\",\n                            \"March\",\n                            \"April\",\n                            \"May\",\n                            \"June\",\n                            \"July\",\n                            \"August\",\n                            \"September\",\n                            \"October\",\n                            \"November\",\n                            \"December\",\n                        }\n                        -- Convert the entries into a certain format to be written\n                        local output = {}\n                        local current_year\n                        local current_month\n                        for _, entry in ipairs(entries) do\n                            -- Don't print the year and month if they haven't changed\n                            if not current_year or current_year < entry[1] then\n                                current_year = entry[1]\n                                current_month = nil\n                                table.insert(output, \"* \" .. current_year)\n                            end\n                            if not current_month or current_month < entry[2] then\n                                current_month = entry[2]\n                                table.insert(output, \"** \" .. months_text[current_month])\n                            end\n\n                            -- Prints the file link\n                            table.insert(output, \"   \" .. entry[4] .. string.format(\"[%s]\", entry[5]))\n                        end\n\n                        return output\n                    end\n\n                module.required[\"core.dirman\"].create_file(\n                    folder_name .. config.pathsep .. index,\n                    workspace or module.required[\"core.dirman\"].get_current_workspace()[1]\n                )\n\n                -- The current buffer now must be the toc file, so we set our toc entries there\n                vim.api.nvim_buf_set_lines(0, 0, -1, false, format(toc_entries))\n                vim.cmd(\"w\")\n            end)\n        end)\n    end,\n}\n\nmodule.on_event = function(event)\n    if event.split_type[1] == \"core.neorgcmd\" then\n        if event.split_type[2] == \"journal.tomorrow\" then\n            module.public.diary_tomorrow()\n        elseif event.split_type[2] == \"journal.yesterday\" then\n            module.public.diary_yesterday()\n        elseif event.split_type[2] == \"journal.custom\" then\n            if not event.content[1] then\n                local calendar = modules.get_module(\"core.ui.calendar\")\n\n                if not calendar then\n                    log.error(\"[ERROR]: `core.ui.calendar` is not loaded! Said module is required for this operation.\")\n                    return\n                end\n\n                calendar.select_date({\n                    callback = vim.schedule_wrap(function(osdate)\n                        module.public.open_diary(\n                            nil,\n                            string.format(\"%04d\", osdate.year)\n                                .. \"-\"\n                                .. string.format(\"%02d\", osdate.month)\n                                .. \"-\"\n                                .. string.format(\"%02d\", osdate.day)\n                        )\n                    end),\n                })\n            else\n                module.public.open_diary(nil, event.content[1])\n            end\n        elseif event.split_type[2] == \"journal.today\" then\n            module.public.diary_today()\n        elseif event.split_type[2] == \"journal.template\" then\n            module.public.create_template()\n        elseif event.split_type[2] == \"journal.toc.open\" then\n            module.public.open_toc()\n        elseif event.split_type[2] == \"journal.toc.update\" then\n            module.public.create_toc()\n        end\n    end\nend\n\nmodule.events.subscribed = {\n    [\"core.neorgcmd\"] = {\n        [\"journal.yesterday\"] = true,\n        [\"journal.tomorrow\"] = true,\n        [\"journal.today\"] = true,\n        [\"journal.custom\"] = true,\n        [\"journal.template\"] = true,\n        [\"journal.toc.update\"] = true,\n        [\"journal.toc.open\"] = true,\n    },\n}\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/keybinds/module.lua",
    "content": "--[[\n    file: User-Keybinds\n    title: The Language of Neorg\n    description: `core.keybinds` manages mappings for operations on or in `.norg` files.\n    summary: Module for managing keybindings with Neorg mode support.\n    ---\nThe `core.keybinds` module configures an out-of-the-box Neovim experience by providing a default\nset of keys.\n\nTo disable default keybinds, see the next section. To remap the existing keys, see [here](https://github.com/nvim-neorg/neorg/wiki/User-Keybinds#remapping-keys).\n\nTo find common problems, consult the [FAQ](https://github.com/nvim-neorg/neorg/wiki/User-Keybinds#faq).\n\n### Disabling Default Keybinds\n\nBy default when you load the `core.keybinds` module all keybinds will be enabled. If you would like to change this, be sure to set `default_keybinds` to `false`:\n```lua\n[\"core.keybinds\"] = {\n    config = {\n        default_keybinds = false,\n    },\n}\n```\n\n### Remapping Keys\n\nTo understand how to effectively remap keys, one must understand how keybinds are set.\nNeorg binds actions to various `<Plug>` mappings that look like so: `<Plug>(neorg...`.\n\nTo remap a key, simply map an action somewhere in your configuration:\n\n```lua\nvim.keymap.set(\"n\", \"my-key-here\", \"<Plug>(neorg.pivot.list.toggle)\", {})\n```\n\nNeorg will recognize that the key has been bound by you and not bind its own key.\n\n#### Binding Keys for Norg Files Only\n\nThis approach has a downside - all of Neorg's keybinds are set on a per-buffer basis\nso that keybinds don't \"overflow\" into buffers you don't want them active in.\n\nWhen you map a key using `vim.keymap.set`, you set a global key which is always active, even in non-norg\nfiles. There are two ways to combat this:\n- Create a file under `<your-configuration>/ftplugin/norg.lua`:\n  ```lua\n  vim.keymap.set(\"n\", \"my-key-here\", \"<Plug>(neorg.pivot.list.toggle)\", { buffer = true })\n  ```\n- Create an autocommand using `vim.api.nvim_create_autocmd`:\n  ```lua\n  vim.api.nvim_create_autocmd(\"Filetype\", {\n      pattern = \"norg\",\n      callback = function()\n          vim.keymap.set(\"n\", \"my-key-here\", \"<Plug>(neorg.pivot.list.toggle)\", { buffer = true })\n      end,\n  })\n  ```\n\nNotice that in both situations a `{ buffer = true }` was supplied to the function.\nThis way, your remapped keys will never interfere with other files.\n\n### Discovering Keys\n\nA comprehensive list of all keybinds can be found on [this page!](https://github.com/nvim-neorg/neorg/wiki/Default-Keybinds)\n\n## FAQ\n\n### Some (or all) keybinds do not work\n\nNeorg refuses to bind keys when it knows they'll interfere with your configuration.\nRun `:checkhealth neorg` to see a full list of what keys Neorg has considered \"conflicted\"\nor \"rebound\".\n\nIf you see that *all* of your keybinds are in conflict, you're likely using a plugin that is mapping to your\nlocal leader key. This is a known issue with older versions of `which-key.nvim`. Since version `3.0` of which-key the issue has been fixed - we\nrecommend updating to the latest version to resolve the errors.\n\n--]]\n\nlocal neorg = require(\"neorg.core\")\nlocal modules = neorg.modules\n\nlocal module = modules.create(\"core.keybinds\")\n\nlocal bound_keys = {}\n\nmodule.load = function()\n    if module.config.public.default_keybinds then\n        local preset = module.private.presets[module.config.public.preset]\n        assert(preset, string.format(\"keybind preset `%s` does not exist!\", module.config.public.preset))\n\n        module.public.set_keys_for(false, preset.all)\n\n        vim.api.nvim_create_autocmd(\"FileType\", {\n            pattern = \"norg\",\n            callback = function(ev)\n                module.public.set_keys_for(ev.buf, preset.norg)\n            end,\n        })\n    end\nend\n\nmodule.config.public = {\n    -- Whether to enable the default keybinds.\n    default_keybinds = true,\n\n    -- Which keybind preset to use.\n    -- Currently allows only a single value: `\"neorg\"`.\n    preset = \"neorg\",\n}\n\n---@class core.keybinds\nmodule.public = {\n    --- Adds a set of default keys for Neorg to bind.\n    --- Should be used exclusively by external modules wanting to provide their own default keymaps.\n    ---@param name string The name of the preset to extend (allows for providing default keymaps for various presets)\n    ---@param preset neorg.keybinds.preset The preset data itself.\n    extend_preset = function(name, preset)\n        local original_preset = assert(module.private.presets[name], \"provided preset doesn't exist!\")\n\n        local function extend(a, b)\n            for k, v in pairs(b) do\n                if type(v) == \"table\" then\n                    if vim.islist(v) then\n                        vim.list_extend(a[k], v)\n                    else\n                        extend(a[k], v)\n                    end\n                end\n\n                a[k] = v\n            end\n        end\n\n        extend(original_preset, preset)\n        module.public.bind_norg_keys(vim.api.nvim_get_current_buf())\n    end,\n\n    ---@param buffer number|boolean\n    ---@param preset_subdata table\n    set_keys_for = function(buffer, preset_subdata)\n        for mode, keybinds in pairs(preset_subdata) do\n            bound_keys[mode] = bound_keys[mode] or {}\n\n            for _, keybind in ipairs(keybinds) do\n                if\n                    vim.fn.hasmapto(keybind[2], mode, false) == 0\n                    and vim.fn.mapcheck(keybind[1], mode, false):len() == 0\n                then\n                    local opts = vim.tbl_deep_extend(\"force\", { buffer = buffer }, keybind.opts or {})\n                    vim.keymap.set(mode, keybind[1], keybind[2], opts)\n\n                    bound_keys[mode][keybind[1]] = true\n                end\n            end\n        end\n    end,\n\n    --- Checks the health of keybinds. Returns all remaps and all conflicts in a table.\n    ---@return { preset_exists: boolean, remaps: table<string, string>, conflicts: table<string, string> }\n    health = function()\n        local preset = module.private.presets[module.config.public.preset]\n\n        if not preset then\n            return {\n                preset_exists = false,\n            }\n        end\n\n        local remaps = {}\n        local conflicts = {}\n\n        local function check_keys_for(data)\n            for mode, keybinds in pairs(data) do\n                for _, keybind in ipairs(keybinds) do\n                    if not bound_keys[mode] or not bound_keys[mode][keybind[1]] then\n                        if vim.fn.hasmapto(keybind[2], mode, false) ~= 0 then\n                            remaps[keybind[1]] = keybind[2]\n                        elseif vim.fn.mapcheck(keybind[1], mode, false):len() ~= 0 then\n                            conflicts[keybind[1]] = keybind[2]\n                        end\n                    end\n                end\n            end\n        end\n\n        check_keys_for(preset.all)\n        check_keys_for(preset.norg)\n\n        return {\n            preset_exists = true,\n            remaps = remaps,\n            conflicts = conflicts,\n        }\n    end,\n}\n\nmodule.private = {\n\n    -- TODO: Move these to the \"vim\" preset\n    -- { \"gd\", \"<Plug>(neorg.esupports.hop.hop-link)\", opts = { desc = \"[neorg] Jump to Link\" } },\n    -- { \"gf\", \"<Plug>(neorg.esupports.hop.hop-link)\", opts = { desc = \"[neorg] Jump to Link\" } },\n    -- { \"gF\", \"<Plug>(neorg.esupports.hop.hop-link)\", opts = { desc = \"[neorg] Jump to Link\" } },\n    presets = {\n        ---@class neorg.keybinds.preset\n        neorg = {\n            all = {\n                n = {\n                    -- Create a new `.norg` file to take notes in\n                    -- ^New Note\n                    {\n                        \"<LocalLeader>nn\",\n                        \"<Plug>(neorg.dirman.new-note)\",\n                        opts = { desc = \"[neorg] Create New Note\" },\n                    },\n\n                    -- Create a Table of Contents\n                    -- ^Table of Contents\n                    {\n                        \"gO\",\n                        \"<cmd>Neorg toc<CR>\",\n                        opts = { desc = \"[neorg] Create Table of Contents\" },\n                    },\n                },\n            },\n            norg = {\n                n = {\n                    -- Mark the task under the cursor as \"undone\"\n                    -- ^mark Task as Undone\n                    {\n                        \"<LocalLeader>tu\",\n                        \"<Plug>(neorg.qol.todo-items.todo.task-undone)\",\n                        opts = { desc = \"[neorg] Mark as Undone\" },\n                    },\n\n                    -- Mark the task under the cursor as \"pending\"\n                    -- ^mark Task as Pending\n                    {\n                        \"<LocalLeader>tp\",\n                        \"<Plug>(neorg.qol.todo-items.todo.task-pending)\",\n                        opts = { desc = \"[neorg] Mark as Pending\" },\n                    },\n\n                    -- Mark the task under the cursor as \"done\"\n                    -- ^mark Task as Done\n                    {\n                        \"<LocalLeader>td\",\n                        \"<Plug>(neorg.qol.todo-items.todo.task-done)\",\n                        opts = { desc = \"[neorg] Mark as Done\" },\n                    },\n\n                    -- Mark the task under the cursor as \"on-hold\"\n                    -- ^mark Task as on Hold\n                    {\n                        \"<LocalLeader>th\",\n                        \"<Plug>(neorg.qol.todo-items.todo.task-on-hold)\",\n                        opts = { desc = \"[neorg] Mark as On Hold\" },\n                    },\n\n                    -- Mark the task under the cursor as \"cancelled\"\n                    -- ^mark Task as Cancelled\n                    {\n                        \"<LocalLeader>tc\",\n                        \"<Plug>(neorg.qol.todo-items.todo.task-cancelled)\",\n                        opts = { desc = \"[neorg] Mark as Cancelled\" },\n                    },\n\n                    -- Mark the task under the cursor as \"recurring\"\n                    -- ^mark Task as Recurring\n                    {\n                        \"<LocalLeader>tr\",\n                        \"<Plug>(neorg.qol.todo-items.todo.task-recurring)\",\n                        opts = { desc = \"[neorg] Mark as Recurring\" },\n                    },\n\n                    -- Mark the task under the cursor as \"important\"\n                    -- ^mark Task as Important\n                    {\n                        \"<LocalLeader>ti\",\n                        \"<Plug>(neorg.qol.todo-items.todo.task-important)\",\n                        opts = { desc = \"[neorg] Mark as Important\" },\n                    },\n\n                    -- Mark the task under the cursor as \"ambiguous\"\n                    -- ^mark Task as Ambiguous\n                    {\n                        \"<LocalLeader>ta\",\n                        \"<Plug>(neorg.qol.todo-items.todo.task-ambiguous)\",\n                        opts = { desc = \"[neorg] Mark as Ambiguous\" },\n                    },\n\n                    -- Switch the task under the cursor between a select few states\n                    {\n                        \"<C-Space>\",\n                        \"<Plug>(neorg.qol.todo-items.todo.task-cycle)\",\n                        opts = { desc = \"[neorg] Cycle Task\" },\n                    },\n\n                    -- Hop to the destination of the link under the cursor\n                    { \"<CR>\", \"<Plug>(neorg.esupports.hop.hop-link)\", opts = { desc = \"[neorg] Jump to Link\" } },\n\n                    -- Same as `<CR>`, except open the destination in a vertical split\n                    {\n                        \"<M-CR>\",\n                        \"<Plug>(neorg.esupports.hop.hop-link.vsplit)\",\n                        opts = { desc = \"[neorg] Jump to Link (Vertical Split)\" },\n                    },\n\n                    -- Same as `<CR>`, except open the destination in a new tab\n                    -- If destination is already open in an existing tab, just navigate to it\n                    {\n                        \"<M-t>\",\n                        \"<Plug>(neorg.esupports.hop.hop-link.tab-drop)\",\n                        opts = { desc = \"[neorg] Jump to Link (Tab Drop)\" },\n                    },\n\n                    -- Promote an object non-recursively\n                    {\n                        \">.\",\n                        \"<Plug>(neorg.promo.promote)\",\n                        opts = { desc = \"[neorg] Promote Object (Non-Recursively)\" },\n                    },\n                    -- Demote an object non-recursively\n                    { \"<,\", \"<Plug>(neorg.promo.demote)\", opts = { desc = \"[neorg] Demote Object (Non-Recursively)\" } },\n\n                    -- Promote an object recursively\n                    {\n                        \">>\",\n                        \"<Plug>(neorg.promo.promote.nested)\",\n                        opts = { desc = \"[neorg] Promote Object (Recursively)\" },\n                    },\n                    -- Demote an object recursively\n                    {\n                        \"<<\",\n                        \"<Plug>(neorg.promo.demote.nested)\",\n                        opts = { desc = \"[neorg] Demote Object (Recursively)\" },\n                    },\n\n                    -- Toggle a list from ordered <-> unordered\n                    -- ^List Toggle\n                    {\n                        \"<LocalLeader>lt\",\n                        \"<Plug>(neorg.pivot.list.toggle)\",\n                        opts = { desc = \"[neorg] Toggle (Un)ordered List\" },\n                    },\n\n                    -- Invert all items in a list\n                    -- Unlike `<LocalLeader>lt`, inverting a list will respect mixed list\n                    -- items, instead of snapping all list types to a single one.\n                    -- ^List Invert\n                    {\n                        \"<LocalLeader>li\",\n                        \"<Plug>(neorg.pivot.list.invert)\",\n                        opts = { desc = \"[neorg] Invert (Un)ordered List\" },\n                    },\n\n                    -- Insert a link to a date at the given position\n                    -- ^Insert Date\n                    { \"<LocalLeader>id\", \"<Plug>(neorg.tempus.insert-date)\", opts = { desc = \"[neorg] Insert Date\" } },\n\n                    -- Magnifies a code block to a separate buffer.\n                    -- ^Code Magnify\n                    {\n                        \"<LocalLeader>cm\",\n                        \"<Plug>(neorg.looking-glass.magnify-code-block)\",\n                        opts = { desc = \"[neorg] Magnify Code Block\" },\n                    },\n                },\n\n                i = {\n                    -- Promote an object recursively\n                    {\n                        \"<C-t>\",\n                        \"<Plug>(neorg.promo.promote)\",\n                        opts = { desc = \"[neorg] Promote Object (Recursively)\" },\n                    },\n\n                    -- Demote an object recursively\n                    { \"<C-d>\", \"<Plug>(neorg.promo.demote)\", opts = { desc = \"[neorg] Demote Object (Recursively)\" } },\n\n                    -- Create an iteration of e.g. a list item\n                    { \"<M-CR>\", \"<Plug>(neorg.itero.next-iteration)\", opts = { desc = \"[neorg] Continue Object\" } },\n\n                    -- Insert a link to a date at the current cursor position\n                    -- ^Date\n                    {\n                        \"<M-d>\",\n                        \"<Plug>(neorg.tempus.insert-date.insert-mode)\",\n                        opts = { desc = \"[neorg] Insert Date\" },\n                    },\n                },\n\n                v = {\n                    -- Promote objects in range\n                    { \">\", \"<Plug>(neorg.promo.promote.range)\", opts = { desc = \"[neorg] Promote Objects in Range\" } },\n                    -- Demote objects in range\n                    { \"<\", \"<Plug>(neorg.promo.demote.range)\", opts = { desc = \"[neorg] Demote Objects in Range\" } },\n                },\n            },\n        },\n    },\n}\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/latex/renderer/module.lua",
    "content": "--[[\n    file: Core-Latex-Renderer\n    title: Rendering LaTeX with image.nvim\n    summary: An experimental module for rendering latex images inline.\n    ---\n\nThis is an experimental module that requires nvim 0.10+. It renders LaTeX snippets as images\nmaking use of the image.nvim plugin. By default, images are only rendered after running the\ncommand: `:Neorg render-latex`. Rendering can be disabled with `:Neorg render-latex disable`\n\nRequires:\n- The [image.nvim](https://github.com/3rd/image.nvim) neovim plugin.\n- `latex` executable in path with the following packages:\n  - standalone\n  - amsmath\n  - amssymb\n  - graphicx\n- `dvipng` executable in path (normally comes with LaTeX)\n\nThere's a highlight group that controls the foreground color of the rendered latex:\n`@norg.rendered.latex`, configurable in `core.highlights`\n--]]\nlocal nio\nlocal neorg = require(\"neorg.core\")\nlocal module = neorg.modules.create(\"core.latex.renderer\")\nlocal modules = neorg.modules\n\nmodule.setup = function()\n    return {\n        requires = {\n            \"core.integrations.image\",\n            \"core.integrations.treesitter\",\n            \"core.autocommands\",\n            \"core.neorgcmd\",\n            \"core.highlights\",\n        },\n    }\nend\n\nmodule.config.public = {\n    -- When true, images of rendered LaTeX will cover the source LaTeX they were produced from.\n    -- Setting this value to false creates more lag, and can be buggy with large numbers of images.\n    conceal = true,\n\n    -- \"Dots Per Inch\" increasing this value will result in crisper images at the expense of\n    -- performance\n    dpi = 350,\n\n    -- When true, images will render when a `.norg` buffer is entered\n    render_on_enter = false,\n\n    -- Module that renders the images. \"core.integrations.image\" makes use of image.nvim and is\n    -- currently the only option\n    renderer = \"core.integrations.image\",\n\n    -- Don't re-render anything until 200ms after the buffer has stopped changing. Lowering will\n    -- lead to a more seamless experience but will cause more temporary images to be created\n    debounce_ms = 200,\n\n    -- Only render latex snippets that are longer than this many chars. Escaped chars are removed\n    -- spaces are counted, `$` and `$|`/`|$` are not (ie. `$\\\\int$` counts as 4 chars)\n    min_length = 3,\n\n    -- Make the images larger or smaller by adjusting the scale. Will not pad images with virtual\n    -- text when `conceal = true`, so they can overlap text. Images will not be blown up larger than\n    -- their true size, so images may still render one line tall.\n    scale = 1,\n}\n\n---@class Image\n---@field path string\n-- and many other fields that I don't necessarily need\n\n---@class MathRange\n---@field image Image our limited representation of an image\n---@field range Range4 last range of the math block. Updated based on the extmark\n---@field snippet string cleaned latex snippet\n---@field extmark_id number? when rendered, the extmark_id that belongs to this image\n---@field real boolean tag ranges that are confirmed to still exist by TS\n\n---Compute and set the foreground color string\nlocal function compute_foreground()\n    local neorg_hi = neorg.modules.get_module(\"core.highlights\")\n    assert(neorg_hi, \"Failed to load core.highlights\")\n    local hi = vim.api.nvim_get_hl(0, { name = \"@neorg.rendered.latex\", link = false })\n    if not vim.tbl_isempty(hi) then\n        local r, g, b = neorg_hi.hex_to_rgb((\"%06x\"):format(hi.fg))\n        module.private.foreground = (\"rgb %s %s %s\"):format(r / 255., g / 255., b / 255.)\n    else\n        -- grey\n        module.private.foreground = \"rgb 0.5 0.5 0.5\"\n    end\nend\n\nmodule.load = function()\n    local success, image = pcall(neorg.modules.get_module, module.config.public.renderer)\n\n    assert(success, \"Unable to load image module\")\n\n    nio = require(\"nio\")\n\n    -- compute the foreground color in rgb\n    compute_foreground()\n\n    ---@type string[] ids\n    module.private.cleared_at_cursor = {}\n\n    ---Image cache. latex snippet to file path\n    ---@type table<string, string>\n    module.private.image_paths = {}\n\n    ---@type table<number, table<string, MathRange>>\n    module.private.latex_images = {}\n\n    module.private.image_api = image\n    module.private.extmark_ns = vim.api.nvim_create_namespace(\"neorg-latex-concealer\")\n\n    module.private.do_render = module.config.public.render_on_enter\n\n    module.required[\"core.autocommands\"].enable_autocommand(\"BufWinEnter\")\n    module.required[\"core.autocommands\"].enable_autocommand(\"CursorMoved\")\n    module.required[\"core.autocommands\"].enable_autocommand(\"TextChanged\")\n    module.required[\"core.autocommands\"].enable_autocommand(\"TextChangedI\")\n\n    modules.await(\"core.neorgcmd\", function(neorgcmd)\n        neorgcmd.add_commands_from_table({\n            [\"render-latex\"] = {\n                name = \"latex.render.render\",\n                min_args = 0,\n                max_args = 1,\n                subcommands = {\n                    enable = {\n                        args = 0,\n                        name = \"latex.render.enable\",\n                    },\n                    disable = {\n                        args = 0,\n                        name = \"latex.render.disable\",\n                    },\n                    toggle = {\n                        args = 0,\n                        name = \"latex.render.toggle\",\n                    },\n                },\n                condition = \"norg\",\n            },\n        })\n    end)\nend\n\n---Get the key for a given range\n---@param range Range2 | Range4\nmodule.private.get_key = function(range)\n    return (\"%d:%d\"):format(range[1], range[2])\nend\n\n---@class core.latex.renderer\nmodule.public = {\n    ---@async\n    ---@param buf number\n    async_latex_renderer = function(buf)\n        -- Update all the limage keys to their new extmark locations\n        ---@type table<string, MathRange>\n        local new_limages = {}\n        for _, limage in pairs(module.private.latex_images[buf] or {}) do\n            if limage.extmark_id then\n                local extmark =\n                    nio.api.nvim_buf_get_extmark_by_id(buf, module.private.extmark_ns, limage.extmark_id, {})\n                local new_key = module.private.get_key({ extmark[1], extmark[2] })\n                limage.real = false\n                new_limages[new_key] = limage\n            end\n        end\n        module.private.cleared_at_cursor = {}\n        module.required[\"core.integrations.treesitter\"].execute_query(\n            [[\n                (\n                    (inline_math) @latex\n                    (#offset! @latex 0 1 0 -1)\n                )\n            ]],\n            function(query, id, node)\n                if query.captures[id] ~= \"latex\" then\n                    return\n                end\n\n                local original_snippet =\n                    module.required[\"core.integrations.treesitter\"].get_node_text(node, nio.api.nvim_get_current_buf())\n                local clean_snippet = string.gsub(original_snippet, \"^%$|\", \"$\")\n                clean_snippet = string.gsub(clean_snippet, \"|%$$\", \"$\")\n                if clean_snippet == original_snippet then\n                    -- this is a normal math block, we need to remove leading `\\` chars\n                    -- TODO: test that this regex is actually correct\n                    clean_snippet = string.gsub(clean_snippet, \"\\\\(.)\", \"%1\")\n                end\n                -- `- 2` for the two `$`s\n                if string.len(clean_snippet) - 2 < module.config.public.min_length then\n                    return\n                end\n\n                local png_location = module.private.image_paths[clean_snippet]\n                    or module.public.async_generate_image(clean_snippet)\n                if not png_location then\n                    return\n                end\n                module.private.image_paths[clean_snippet] = png_location\n                local range = { node:range() }\n                local key = module.private.get_key(range)\n\n                -- If there's already an image at this location and it's the same snippet, don't do\n                -- anything\n                if new_limages[key] then\n                    if new_limages[key].snippet == clean_snippet then\n                        new_limages[key].range = range\n                        new_limages[key].real = true\n                        return\n                    end\n                end\n\n                local img = module.private.image_api.new_image(\n                    buf,\n                    png_location,\n                    module.required[\"core.integrations.treesitter\"].get_node_range(node),\n                    nio.api.nvim_get_current_win(),\n                    module.config.public.scale,\n                    not module.config.public.conceal\n                )\n                local existing_ext_id = new_limages[key] and new_limages[key].extmark_id\n                new_limages[key] = {\n                    image = img,\n                    range = range,\n                    snippet = clean_snippet,\n                    real = true,\n                    extmark_id = existing_ext_id,\n                }\n            end,\n            buf\n        )\n        nio.scheduler()\n\n        for key, limage in pairs(new_limages) do\n            if not limage.real then\n                module.private.image_api.clear({ [key] = limage })\n                if limage.extmark_id then\n                    nio.api.nvim_buf_del_extmark(0, module.private.extmark_ns, limage.extmark_id)\n                end\n                new_limages[key] = nil\n            end\n        end\n        module.private.latex_images[buf] = new_limages\n    end,\n\n    ---Writes a latex snippet to a file and wraps it with latex headers so it will render nicely\n    ---@async\n    ---@param snippet string latex snippet (if it's math it should include the surrounding $$)\n    ---@return string temp file path\n    async_create_latex_document = function(snippet)\n        local tempname = nio.fn.tempname()\n        local tempfile = nio.file.open(tempname, \"w\")\n\n        local content = table.concat({\n            \"\\\\documentclass[6pt]{standalone}\",\n            \"\\\\usepackage{amsmath}\",\n            \"\\\\usepackage{amssymb}\",\n            \"\\\\usepackage{graphicx}\",\n            \"\\\\begin{document}\",\n            snippet,\n            \"\\\\end{document}\",\n        }, \"\\n\")\n\n        tempfile.write(content)\n        tempfile.close()\n\n        return tempname\n    end,\n\n    ---Returns a filepath where the rendered image sits\n    ---@async\n    ---@param snippet string the full latex snippet to convert to an image\n    ---@return string | nil\n    async_generate_image = function(snippet)\n        local document_name = module.public.async_create_latex_document(snippet)\n\n        if not document_name then\n            return\n        end\n\n        local cwd = nio.fn.fnamemodify(document_name, \":h\")\n        local create_dvi = nio.process.run({\n            cmd = \"latex\",\n            args = {\n                \"--interaction=nonstopmode\",\n                \"--output-format=dvi\",\n                document_name,\n            },\n            cwd = cwd,\n        })\n        if not create_dvi or type(create_dvi) == \"string\" then\n            return\n        end\n        local res = create_dvi.result()\n        if res ~= 0 then\n            return\n        end\n\n        local png_result = nio.fn.tempname()\n        png_result = (\"%s.png\"):format(png_result)\n\n        local dvipng = nio.process.run({\n            cmd = \"dvipng\",\n            args = {\n                \"-D\",\n                module.config.public.dpi,\n                \"-T\",\n                \"tight\",\n                \"-bg\",\n                \"Transparent\",\n                \"-fg\",\n                module.private.foreground,\n                \"-o\",\n                png_result,\n                document_name .. \".dvi\",\n            },\n        })\n\n        if not dvipng or type(dvipng) == \"string\" then\n            return\n        end\n        res = dvipng.result()\n        if res ~= 0 then\n            return\n        end\n\n        return png_result\n    end,\n\n    ---Actually renders the images (along with any extmarks it needs)\n    ---@param images table<string, MathRange>\n    render_inline_math = function(images, buffer)\n        local conceallevel = vim.api.nvim_get_option_value(\"conceallevel\", { win = 0 })\n        local cursor_row = vim.api.nvim_win_get_cursor(0)[1]\n        local conceal_on = conceallevel >= 2 and module.config.public.conceal\n        -- Create all extmarks before rendering images b/c these extmarks will change the\n        -- position of the images\n        for _, limage in pairs(images) do\n            local range = limage.range\n\n            local ext_opts = {\n                end_col = range[4],\n                strict = false,\n                invalidate = true,\n                undo_restore = false,\n                id = limage.extmark_id, -- if it exists, update it, else this is nil so it will create a new one\n            }\n\n            if module.config.public.conceal then\n                local image = limage.image\n                local predicted_image_dimensions =\n                    module.private.image_api.image_size(image, { height = module.config.public.scale })\n                if range[1] ~= cursor_row - 1 then\n                    ext_opts.virt_text = { { (\" \"):rep(predicted_image_dimensions.width) } }\n                    ext_opts.virt_text_pos = \"inline\"\n                end\n            end\n\n            if conceal_on and range[1] ~= cursor_row - 1 then\n                ext_opts.conceal = \"\"\n            end\n\n            limage.extmark_id =\n                vim.api.nvim_buf_set_extmark(buffer, module.private.extmark_ns, range[1], range[2], ext_opts)\n        end\n\n        for key, limage in pairs(images) do\n            local range = limage.range\n            if conceal_on and range[1] == cursor_row - 1 then\n                table.insert(module.private.cleared_at_cursor, key)\n                module.private.image_api.clear({ limage })\n                if limage.extmark_id then\n                    vim.api.nvim_buf_set_extmark(buffer, module.private.extmark_ns, range[1], range[2], {\n                        virt_text = { { \"\" } },\n                    })\n                end\n                goto continue\n            end\n            module.private.image_api.render({ limage })\n            ::continue::\n        end\n    end,\n}\n\nlocal running_proc = nil\nlocal render_timer = nil\nlocal function render_latex()\n    local buf = vim.api.nvim_get_current_buf()\n    if not module.private.do_render then\n        if render_timer then\n            render_timer:stop()\n            render_timer:close()\n            render_timer = nil\n        end\n        return\n    end\n\n    if not render_timer then\n        render_timer = vim.uv.new_timer()\n    end\n\n    render_timer:start(module.config.public.debounce_ms, 0, function()\n        render_timer:stop()\n        render_timer:close()\n        render_timer = nil\n\n        if not running_proc then\n            running_proc = nio.run(\n                function()\n                    nio.scheduler()\n                    module.public.async_latex_renderer(buf)\n                end,\n                vim.schedule_wrap(function()\n                    module.public.render_inline_math(module.private.latex_images[buf] or {}, buf)\n                    running_proc = nil\n                end)\n            )\n        end\n    end)\nend\n\nlocal function clear_at_cursor()\n    local buf = vim.api.nvim_get_current_buf()\n    if not module.private.do_render or render_timer then\n        return\n    end\n\n    if module.config.public.conceal and module.private.latex_images[buf] ~= nil then\n        local cleared = module.private.image_api.clear_at_cursor(\n            module.private.latex_images[buf],\n            vim.api.nvim_win_get_cursor(0)[1] - 1\n        )\n        for _, id in ipairs(cleared) do\n            local limage = module.private.latex_images[buf][id]\n            if limage.extmark_id then\n                vim.api.nvim_buf_set_extmark(0, module.private.extmark_ns, limage.range[1], limage.range[2], {\n                    id = limage.extmark_id,\n                    end_col = limage.range[4],\n                    conceal = \"\",\n                    virt_text = { { \"\", \"\" } },\n                    strict = false,\n                })\n            end\n        end\n        local to_render = {}\n        for _, key in ipairs(module.private.cleared_at_cursor) do\n            if not vim.tbl_contains(cleared, key) then\n                -- this image was cleared b/c it was at our cursor, and now it should be rendered again\n                to_render[key] = module.private.latex_images[buf][key]\n            end\n        end\n\n        local updated_positions = {}\n        for _, limage in pairs(to_render) do\n            if limage.extmark_id then\n                local extmark = vim.api.nvim_buf_get_extmark_by_id(\n                    buf,\n                    module.private.extmark_ns,\n                    limage.extmark_id,\n                    { details = true }\n                )\n                local range = { extmark[1], extmark[2], extmark[3].end_row, extmark[3].end_col }\n                local new_key = module.private.get_key(range)\n                updated_positions[new_key] = limage\n                updated_positions[new_key].range = range\n            end\n        end\n        module.public.render_inline_math(updated_positions, buf)\n        module.private.cleared_at_cursor = cleared\n    end\nend\n\nlocal function enable_rendering()\n    module.private.do_render = true\n    render_latex()\nend\n\nlocal function disable_rendering()\n    module.private.do_render = false\n    for buf, images in pairs(module.private.latex_images) do\n        module.private.image_api.clear(images)\n        vim.api.nvim_buf_clear_namespace(buf, module.private.extmark_ns, 0, -1)\n    end\n    module.private.latex_images = {}\nend\n\nlocal function toggle_rendering()\n    if module.private.do_render then\n        disable_rendering()\n    else\n        enable_rendering()\n    end\nend\n\nlocal function show_hidden()\n    local buf = vim.api.nvim_get_current_buf()\n    if not module.private.do_render then\n        return\n    end\n\n    module.private.image_api.render(module.private.latex_images[buf] or {})\nend\n\nlocal function colorscheme_change()\n    module.private.image_paths = {}\n    if module.private.do_render then\n        disable_rendering()\n        module.private.latex_images = {}\n        vim.schedule(function()\n            compute_foreground()\n            enable_rendering()\n        end)\n    else\n        vim.schedule_wrap(compute_foreground)()\n    end\nend\n\nlocal event_handlers = {\n    [\"core.neorgcmd.events.latex.render.render\"] = enable_rendering,\n    [\"core.neorgcmd.events.latex.render.enable\"] = enable_rendering,\n    [\"core.neorgcmd.events.latex.render.disable\"] = disable_rendering,\n    [\"core.neorgcmd.events.latex.render.toggle\"] = toggle_rendering,\n    [\"core.autocommands.events.bufreadpost\"] = render_latex,\n    [\"core.autocommands.events.bufwinenter\"] = show_hidden,\n    [\"core.autocommands.events.cursormoved\"] = clear_at_cursor,\n    [\"core.autocommands.events.textchanged\"] = render_latex,\n    -- [\"core.autocommands.events.textchangedi\"] = render_latex,\n    [\"core.autocommands.events.insertleave\"] = render_latex,\n    [\"core.autocommands.events.colorscheme\"] = colorscheme_change,\n}\n\nmodule.on_event = function(event)\n    if event.referrer == \"core.autocommands\" and vim.bo[event.buffer].ft ~= \"norg\" then\n        return\n    end\n\n    return event_handlers[event.type]()\nend\n\nmodule.events.subscribed = {\n    [\"core.autocommands\"] = {\n        bufreadpost = module.config.public.render_on_enter,\n        bufwinenter = true,\n        cursormoved = true,\n        textchanged = true,\n        -- textchangedi = true,\n        insertleave = true,\n        colorscheme = true,\n    },\n    [\"core.neorgcmd\"] = {\n        [\"latex.render.render\"] = true,\n        [\"latex.render.enable\"] = true,\n        [\"latex.render.disable\"] = true,\n        [\"latex.render.toggle\"] = true,\n    },\n}\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/links/module.lua",
    "content": "--[[\n    file: Links\n    title: Find links/target in the buffer\n    description: Utility module to handle links/link targets in the buffer\n    internal: true\n    ---\n\nThis module provides utility functions that are used to find links and their targets in the buffer.\n--]]\n\nlocal neorg = require(\"neorg.core\")\nlocal lib, modules = neorg.lib, neorg.modules\n\nlocal module = modules.create(\"core.links\")\n\nmodule.setup = function()\n    return {\n        success = true,\n    }\nend\n\n---@class core.links\nmodule.public = {\n    -- TS query strings for different link targets\n    ---@param link_type \"generic\" | \"definition\" | \"footnote\" | string\n    get_link_target_query_string = function(link_type)\n        return lib.match(link_type)({\n            generic = [[\n                [(_\n                  [(strong_carryover_set\n                     (strong_carryover\n                       name: (tag_name) @tag_name\n                       (tag_parameters) @title\n                       (#eq? @tag_name \"name\")))\n                   (weak_carryover_set\n                     (weak_carryover\n                       name: (tag_name) @tag_name\n                       (tag_parameters) @title\n                       (#eq? @tag_name \"name\")))]?\n                  title: (paragraph_segment) @title)\n                 (inline_link_target\n                   (paragraph) @title)]\n            ]],\n\n            [{ \"definition\", \"footnote\" }] = string.format(\n                [[\n                (%s_list\n                    (strong_carryover_set\n                          (strong_carryover\n                            name: (tag_name) @tag_name\n                            (tag_parameters) @title\n                            (#eq? @tag_name \"name\")))?\n                    .\n                    [(single_%s\n                       (weak_carryover_set\n                          (weak_carryover\n                            name: (tag_name) @tag_name\n                            (tag_parameters) @title\n                            (#eq? @tag_name \"name\")))?\n                       (single_%s_prefix)\n                       title: (paragraph_segment) @title)\n                     (multi_%s\n                       (weak_carryover_set\n                          (weak_carryover\n                            name: (tag_name) @tag_name\n                            (tag_parameters) @title\n                            (#eq? @tag_name \"name\")))?\n                        (multi_%s_prefix)\n                          title: (paragraph_segment) @title)])\n                ]],\n                lib.reparg(link_type, 5)\n            ),\n            _ = string.format(\n                [[\n                    (%s\n                      [(strong_carryover_set\n                         (strong_carryover\n                           name: (tag_name) @tag_name\n                           (tag_parameters) @title\n                           (#eq? @tag_name \"name\")))\n                       (weak_carryover_set\n                         (weak_carryover\n                           name: (tag_name) @tag_name\n                           (tag_parameters) @title\n                           (#eq? @tag_name \"name\")))]?\n                      (%s_prefix)\n                      title: (paragraph_segment) @title)\n                ]],\n                lib.reparg(link_type, 2)\n            ),\n        })\n    end,\n}\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/looking-glass/module.lua",
    "content": "--[[\n    file: Looking-Glass\n    title: Code Blocks + Superpowers\n    description: The `core.looking-glass` module magnifies code blocks and allows you to edit them in a separate buffer.\n    summary: Allows for editing of code blocks within a separate buffer.\n    embed: https://user-images.githubusercontent.com/76052559/216782314-5d82907f-ea6c-44f9-9bd8-1675f1849358.gif\n    ---\nThe looking glass module provides a simple way to edit code blocks in an external buffer,\nwhich allows LSPs and other language-specific tools to kick in.\n\nIf you would like LSP features in code blocks without having to magnify, you can use\n[`core.integrations.otter`](@core.integrations.otter).\n\n## Keybinds\n\nThis module exposes the following keybinds (see [`core.keybinds`](@core.keybinds) for instructions on\nmapping them):\n\n- `neorg.looking-glass.magnify-code-block` - magnify the code block under the cursor\n--]]\n\nlocal neorg = require(\"neorg.core\")\nlocal modules, utils = neorg.modules, neorg.utils\n\nlocal module = modules.create(\"core.looking-glass\")\n\nmodule.setup = function()\n    return {\n        success = true,\n        requires = {\n            \"core.integrations.treesitter\",\n            \"core.ui\",\n        },\n    }\nend\n\nmodule.load = function()\n    vim.keymap.set(\"\", \"<Plug>(neorg.looking-glass.magnify-code-block)\", module.public.magnify_code_block)\nend\n\n---@class core.looking-glass\nmodule.public = {\n    sync_text_segment = function(source, source_window, source_start, source_end, target, target_window)\n        -- Create a unique but deterministic namespace name for the code block\n        local namespace = vim.api.nvim_create_namespace(\n            \"neorg/code-block-\" .. tostring(source) .. tostring(source_start.row) .. tostring(source_end.row)\n        )\n\n        -- Clear any leftover extmarks\n        vim.api.nvim_buf_clear_namespace(source, namespace, 0, -1)\n\n        -- Create two extmarks, one at the beginning of the code block and one at the end.\n        -- This lets us track size changes of the code block (shrinking and enlarging)\n        local start_extmark = vim.api.nvim_buf_set_extmark(source, namespace, source_start.row, source_start.column, {})\n        local end_extmark = vim.api.nvim_buf_set_extmark(source, namespace, source_end.row, source_end.column, {})\n\n        -- This autocommand handles the synchronization from the source buffer to the target buffer\n        -- (from the code block to the split)\n        vim.api.nvim_create_autocmd({ \"TextChanged\", \"TextChangedI\" }, {\n            buffer = source,\n            callback = function()\n                if not vim.api.nvim_buf_is_loaded(target) or not vim.api.nvim_buf_is_loaded(source) then\n                    return true\n                end\n\n                local cursor_pos = vim.api.nvim_win_get_cursor(source_window)\n\n                if vim.api.nvim_get_current_buf() ~= source then\n                    return\n                end\n\n                -- Get the positions of both the extmarks (this has to be in the schedule function else it returns\n                -- outdated information).\n                local extmark_begin = vim.api.nvim_buf_get_extmark_by_id(source, namespace, start_extmark, {})\n                local extmark_end = vim.api.nvim_buf_get_extmark_by_id(source, namespace, end_extmark, {})\n\n                -- Both extmarks will have the same row if the user deletes the whole code block.\n                -- In other words, this is a method to detect when a code block has been deleted.\n                if extmark_end[1] == extmark_begin[1] then\n                    vim.api.nvim_buf_delete(target, { force = true })\n                    vim.api.nvim_buf_clear_namespace(source, namespace, 0, -1)\n                    return true\n                end\n\n                -- Make sure that the cursor is within bounds of the code block\n                if cursor_pos[1] > extmark_begin[1] and cursor_pos[1] <= (extmark_end[1] + 1) then\n                    -- For extra information grab the current node under the cursor\n                    local current_node = vim.treesitter.get_node({ bufnr = source, ignore_injections = true })\n\n                    if not current_node then\n                        vim.api.nvim_buf_delete(target, { force = true })\n                        vim.api.nvim_buf_clear_namespace(source, namespace, 0, -1)\n                        return true\n                    end\n\n                    -- If we are within bounds of the code block but the current node type is not part of a ranged\n                    -- tag then it means the user malformed the code block in some way and we should bail\n                    if\n                        not module.required[\"core.integrations.treesitter\"].find_parent(\n                            current_node,\n                            \"^ranged_verbatim_tag.*\"\n                        )\n                    then\n                        vim.api.nvim_buf_delete(target, { force = true })\n                        vim.api.nvim_buf_clear_namespace(source, namespace, 0, -1)\n                        return true\n                    end\n\n                    local lines = vim.api.nvim_buf_get_lines(source, extmark_begin[1] + 1, extmark_end[1], true)\n\n                    for i, line in ipairs(lines) do\n                        lines[i] = line:sub(extmark_begin[2] + 1)\n                    end\n\n                    -- Now that we have full information that we are in fact in a valid code block\n                    -- take the lines from within the code block and put them in the buffer\n                    vim.api.nvim_buf_set_lines(target, 0, -1, false, lines)\n\n                    local target_line_count = vim.api.nvim_buf_line_count(target)\n\n                    -- Set the cursor in the target window to the place the text is being changed.\n                    -- Useful to keep up with long ranges of text.\n                    --\n                    -- This check exists as sometimes the cursor position can be larger than the size of the\n                    -- target buffer which causes errors.\n                    if cursor_pos[1] - extmark_begin[1] > target_line_count then\n                        vim.api.nvim_win_set_cursor(target_window, { target_line_count, cursor_pos[2] })\n                    else\n                        -- Here we subtract the beginning extmark's row position from the current cursor position\n                        -- in order to create an offset that can be applied to the target buffer.\n                        vim.api.nvim_win_set_cursor(\n                            target_window,\n                            { cursor_pos[1] - extmark_begin[1] - 1, cursor_pos[2] }\n                        )\n                    end\n                end\n            end,\n        })\n\n        -- Target -> source binding\n        -- This binding is much simpler, as it captures changes from the vertical split and applies them\n        -- to the source code block.\n        vim.api.nvim_create_autocmd({ \"TextChanged\", \"TextChangedI\" }, {\n            buffer = target,\n            callback = vim.schedule_wrap(function() -- Schedule wrap is needed for up-to-date extmark information\n                local cursor_pos = vim.api.nvim_win_get_cursor(0)\n\n                local extmark_begin = vim.api.nvim_buf_get_extmark_by_id(source, namespace, start_extmark, {})\n                local extmark_end = vim.api.nvim_buf_get_extmark_by_id(source, namespace, end_extmark, {})\n\n                local lines = vim.api.nvim_buf_get_lines(target, 0, -1, true)\n\n                for i, line in ipairs(lines) do\n                    lines[i] = string.rep(\" \", extmark_begin[2]) .. line\n                end\n\n                vim.api.nvim_buf_set_lines(source, extmark_begin[1] + 1, extmark_end[1], true, lines)\n\n                vim.api.nvim_win_set_cursor(\n                    source_window,\n                    { cursor_pos[1] + 1 + extmark_begin[1], cursor_pos[2] + extmark_begin[2] }\n                )\n            end),\n        })\n\n        -- For when the user closes the target buffer or vertical split.\n        vim.api.nvim_create_autocmd({ \"BufDelete\", \"WinClosed\" }, {\n            buffer = target,\n            once = true,\n            callback = function()\n                pcall(vim.api.nvim_buf_delete, target, { force = true })\n                vim.api.nvim_buf_clear_namespace(source, namespace, 0, -1)\n            end,\n        })\n    end,\n\n    magnify_code_block = function()\n        local buffer = vim.api.nvim_get_current_buf()\n        local window = vim.api.nvim_get_current_win()\n\n        local query = utils.ts_parse_query(\n            \"norg\",\n            [[\n            (ranged_verbatim_tag\n                name: (tag_name) @_name\n                (#any-of? @_name \"code\" \"embed\")) @tag\n        ]]\n        )\n\n        local document_root = module.required[\"core.integrations.treesitter\"].get_document_root(buffer)\n        if not document_root then\n            return\n        end\n\n        --- Table containing information about the code block that is potentially under the cursor\n        local code_block_info\n\n        do\n            local cursor_pos = vim.api.nvim_win_get_cursor(window)\n\n            for id, node in query:iter_captures(document_root, buffer, cursor_pos[1] - 1, cursor_pos[1]) do\n                local capture = query.captures[id]\n\n                if capture == \"tag\" then\n                    local tag_info = module.required[\"core.integrations.treesitter\"].get_tag_info(node)\n\n                    if not tag_info then\n                        utils.notify(\"Unable to magnify current code block :(\", vim.log.levels.WARN)\n                        return\n                    end\n\n                    code_block_info = tag_info\n                end\n            end\n        end\n\n        -- If the query above failed then we know that the user isn't under a code block\n        if not code_block_info then\n            utils.notify(\"No code block found under cursor!\", vim.log.levels.WARN)\n            return\n        end\n\n        -- TODO: Make the vsplit location configurable (i.e. whether it spawns on the left or the right)\n        local vsplit = module.required[\"core.ui\"].create_vsplit(\n            \"code-block-\" .. tostring(code_block_info.start.row) .. tostring(code_block_info[\"end\"].row), -- This is done to make the name of the vsplit unique\n            true,\n            {\n                filetype = (code_block_info.parameters[1] or \"none\"),\n            },\n            { split = \"left\" }\n        )\n\n        if not vsplit then\n            utils.notify(\n                \"Unable to magnify current code block because our split didn't want to open :(\",\n                vim.log.levels.WARN\n            )\n            return\n        end\n\n        -- Set the content of the target buffer to the content of the code block (initial synchronization)\n        vim.api.nvim_buf_set_lines(vsplit, 0, -1, true, code_block_info.content)\n\n        -- iterate over attributes and find the last row of them.\n        local last_attribute = nil\n        if code_block_info.attributes then\n            last_attribute = code_block_info.attributes[1]\n\n            for _, v in ipairs(code_block_info.attributes) do\n                if v[\"end\"].row > last_attribute[\"end\"].row then\n                    last_attribute = v\n                end\n            end\n        end\n\n        local start = last_attribute and last_attribute[\"end\"] or code_block_info.start\n\n        module.public.sync_text_segment(\n            buffer,\n            window,\n            start,\n            code_block_info[\"end\"],\n            vsplit,\n            vim.api.nvim_get_current_win()\n        )\n    end,\n}\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/neorgcmd/commands/return/module.lua",
    "content": "--[[\n    file: Neorgcmd-return\n    title: Provides the `:Neorg return` Command\n    summary: Return to last location before entering Neorg.\n    internal: true\n    ---\nWhen executed (`:Neorg return`), all currently open `.norg` files are deleted from\nthe buffer list, and the current workspace is set to \"default\".\n--]]\n\nlocal neorg = require(\"neorg.core\")\nlocal modules = neorg.modules\n\nlocal module = modules.create(\"core.neorgcmd.commands.return\")\n\nmodule.setup = function()\n    return { success = true, requires = { \"core.neorgcmd\" } }\nend\n\nmodule.public = {\n    neorg_commands = {\n        [\"return\"] = {\n            args = 0,\n            name = \"return\",\n        },\n    },\n}\n\nmodule.on_event = function(event)\n    if event.type == \"core.neorgcmd.events.return\" then\n        -- Get all the buffers\n        local buffers = vim.api.nvim_list_bufs()\n\n        local to_delete = {}\n        for buffer in vim.iter(buffers):rev() do\n            if vim.fn.buflisted(buffer) == 1 then\n                -- If the listed buffer we're working with has a .norg extension then remove it (not forcibly)\n                if not vim.endswith(vim.api.nvim_buf_get_name(buffer), \".norg\") then\n                    vim.api.nvim_win_set_buf(0, buffer)\n                    break\n                else\n                    table.insert(to_delete, buffer)\n                end\n            end\n        end\n\n        for _, buffer in ipairs(to_delete) do\n            vim.api.nvim_buf_delete(buffer, {})\n        end\n    end\nend\n\nmodule.events.subscribed = {\n    [\"core.neorgcmd\"] = {\n        [\"return\"] = true,\n    },\n}\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/neorgcmd/module.lua",
    "content": "--[[\n    file: Neorgcmd-Module\n    title: Does the Heavy Lifting for the `:Neorg` Command\n    summary: This module deals with handling everything related to the `:Neorg` command.\n    internal: true\n    ---\nThis internal module handles everything there is for the `:Neorg` command to function.\n\nDifferent modules can define their own commands, completions and conditions on when they'd\nlike these commands to be avaiable.\n\nFor a full example on how to create your own command, it is recommended to read the\n`core.neorgcmd`'s `module.lua` file. At the beginning of the file is an examples table\nwhich walks you through the necessary steps.\n--]]\n\nlocal neorg = require(\"neorg.core\")\nlocal log, modules = neorg.log, neorg.modules\n\nlocal module = modules.create(\"core.neorgcmd\")\n\nmodule.examples = {\n    [\"Adding a Neorg command\"] = function()\n        -- In your module.setup(), make sure to require core.neorgcmd (requires = { \"core.neorgcmd\" })\n        -- Afterwards in a function of your choice that gets called *after* core.neorgcmd gets intialized e.g. load():\n\n        module.load = function()\n            module.required[\"core.neorgcmd\"].add_commands_from_table({\n                -- The name of our command\n                my_command = {\n                    min_args = 1, -- Tells neorgcmd that we want at least one argument for this command\n                    max_args = 1, -- Tells neorgcmd we want no more than one argument\n                    args = 1, -- Setting this variable instead would be the equivalent of min_args = 1 and max_args = 1\n                    -- This command is only avaiable within `.norg` files.\n                    -- This can also be a function(bufnr, is_in_an_norg_file)\n                    condition = \"norg\",\n\n                    subcommands = { -- Defines subcommands\n                        -- Repeat the definition cycle again\n                        my_subcommand = {\n                            args = 2, -- Force two arguments to be supplied\n                            -- The identifying name of this command\n                            -- Every \"endpoint\" must have a name associated with it\n                            name = \"my.command\",\n\n                            -- If your command takes in arguments versus\n                            -- subcommands you can make a table of tables with\n                            -- completion for those arguments here.\n                            -- This table is optional.\n                            complete = {\n                                { \"first_completion1\", \"first_completion2\" },\n                                { \"second_completion1\", \"second_completion2\" },\n                            },\n\n                            -- We do not define a subcommands table here because we don't have any more subcommands\n                            -- Creating an empty subcommands table will cause errors so don't bother\n                        },\n                    },\n                },\n            })\n        end\n\n        -- Afterwards, you want to subscribe to the corresponding event:\n\n        module.events.subscribed = {\n            [\"core.neorgcmd\"] = {\n                [\"my.command\"] = true, -- Has the same name as our \"name\" variable had in the \"data\" table\n            },\n        }\n\n        -- There's also another way to define your own custom commands that's a lot more automated. Such automation can be achieved\n        -- by putting your code in a special directory. That directory is in core.neorgcmd.commands. Creating your modules in this directory\n        -- will allow users to easily enable you as a \"command module\" without much hassle.\n\n        -- To enable a command in the commands/ directory, do this:\n\n        require(\"neorg\").setup({\n            load = {\n                [\"core.neorgcmd\"] = {\n                    config = {\n                        load = {\n                            \"some.neorgcmd\", -- The name of a valid command\n                        },\n                    },\n                },\n            },\n        })\n\n        -- And that's it! You're good to go.\n        -- Want to find out more? Read the wiki entry! https://github.com/nvim-neorg/neorg/wiki/Neorg-Command\n    end,\n}\n\nmodule.load = function()\n    -- Define the :Neorg command with autocompletion taking any number of arguments (-nargs=*)\n    -- If the user passes no arguments or too few, we'll query them for the remainder using select_next_cmd_arg.\n    vim.api.nvim_create_user_command(\"Neorg\", module.private.command_callback, {\n        nargs = \"*\",\n        complete = module.private.generate_completions,\n        range = 2,\n    })\n\n    -- Loop through all the command modules we want to load and load them\n    for _, command in ipairs(module.config.public.load) do\n        -- If one of the command modules is \"default\" then load all the default modules\n        if command == \"default\" then\n            for _, default_command in ipairs(module.config.public.default) do\n                module.public.add_commands_from_file(default_command)\n            end\n        end\n    end\nend\n\nmodule.config.public = {\n    -- A list of neorgcmd modules to load automatically.\n    -- This feature will soon be deprecated, so it is not recommended to touch it.\n    load = {\n        \"default\",\n    },\n\n    -- A list of default commands to load.\n    --\n    -- This feature will soon be deprecated, so it is not recommended to touch it.\n    default = {\n        \"return\",\n    },\n}\n\n---@class core.neorgcmd\nmodule.public = {\n    -- The table containing all the functions. This can get a tad complex so I recommend you read the wiki entry\n    neorg_commands = {\n        module = {\n            subcommands = {\n                load = {\n                    args = 1,\n                    name = \"module.load\",\n                },\n\n                list = {\n                    args = 0,\n                    name = \"module.list\",\n                },\n            },\n        },\n    },\n\n    --- Recursively merges the contents of the module's config.public.funtions table with core.neorgcmd's module.config.public.neorg_commands table.\n    ---@param module_name string #An absolute path to a loaded module with a module.config.public.neorg_commands table following a valid structure\n    add_commands = function(module_name)\n        local module_config = modules.get_module(module_name)\n\n        if not module_config or not module_config.neorg_commands then\n            return\n        end\n\n        module.public.neorg_commands =\n            vim.tbl_extend(\"force\", module.public.neorg_commands, module_config.neorg_commands)\n    end,\n\n    --- Recursively merges the provided table with the module.config.public.neorg_commands table.\n    ---@param functions table #A table that follows the module.config.public.neorg_commands structure\n    add_commands_from_table = function(functions)\n        module.public.neorg_commands = vim.tbl_extend(\"force\", module.public.neorg_commands, functions)\n    end,\n\n    --- Takes a relative path (e.g \"list.modules\") and loads it from the commands/ directory\n    ---@param name string #The relative path of the module we want to load\n    add_commands_from_file = function(name)\n        -- Attempt to require the file\n        local err, ret = pcall(require, \"neorg.modules.core.neorgcmd.commands.\" .. name .. \".module\")\n\n        -- If we've failed bail out\n        if not err then\n            log.warn(\n                \"Could not load command\",\n                name,\n                \"for module core.neorgcmd - the corresponding module.lua file does not exist.\"\n            )\n            return\n        end\n\n        -- Load the module from table\n        modules.load_module_from_table(ret)\n    end,\n\n    --- Rereads data from all modules and rebuild the list of available autocompletions and commands\n    sync = function()\n        -- Loop through every loaded module and set up all their commands\n        for _, mod in pairs(modules.loaded_modules) do\n            if mod.public.neorg_commands then\n                module.public.add_commands_from_table(mod.public.neorg_commands)\n            end\n        end\n    end,\n\n    --- Defines a custom completion function to use for `core.neorgcmd`.\n    ---@param callback function The same function format as you would receive by being called by `:command -completion=customlist,v:lua.callback Neorg`.\n    set_completion_callback = function(callback)\n        module.private.generate_completions = callback\n    end,\n}\n\nmodule.private = {\n    --- Handles the calling of the appropriate function based on the command the user entered\n    command_callback = function(data)\n        local args = data.fargs\n\n        local current_buf = vim.api.nvim_get_current_buf()\n        local is_norg = vim.bo[current_buf].filetype == \"norg\"\n\n        local function check_condition(condition)\n            if condition == nil then\n                return true\n            end\n\n            if condition == \"norg\" and not is_norg then\n                return false\n            end\n\n            if type(condition) == \"function\" then\n                return condition(current_buf, is_norg)\n            end\n\n            return condition\n        end\n\n        local ref = {\n            subcommands = module.public.neorg_commands,\n        }\n        local argument_index = 0\n\n        for i, cmd in ipairs(args) do\n            if not ref.subcommands or vim.tbl_isempty(ref.subcommands) then\n                break\n            end\n\n            ref = ref.subcommands[cmd]\n\n            if not ref then\n                log.error(\n                    (\"Error when executing `:Neorg %s` - such a command does not exist!\"):format(\n                        table.concat(vim.list_slice(args, 1, i), \" \")\n                    )\n                )\n                return\n            elseif not check_condition(ref.condition) then\n                log.error(\n                    (\"Error when executing `:Neorg %s` - the command is currently disabled. Some commands will only become available under certain conditions, e.g. being within a `.norg` file!\"):format(\n                        table.concat(vim.list_slice(args, 1, i), \" \")\n                    )\n                )\n                return\n            end\n\n            argument_index = i\n        end\n\n        local argument_count = (#args - argument_index)\n\n        if ref.args then\n            ref.min_args = ref.args\n            ref.max_args = ref.args\n        elseif ref.min_args and not ref.max_args then\n            ref.max_args = math.huge\n        else\n            ref.min_args = ref.min_args or 0\n            ref.max_args = ref.max_args or 0\n        end\n\n        if #args == 0 or argument_count < ref.min_args then\n            local completions = module.private.generate_completions(_, table.concat({ \"Neorg \", data.args, \" \" }))\n            module.private.select_next_cmd_arg(data.args, completions)\n            return\n        elseif argument_count > ref.max_args then\n            log.error(\n                (\"Error when executing `:Neorg %s` - too many arguments supplied! The command expects %s argument%s.\"):format(\n                    data.args,\n                    ref.max_args == 0 and \"no\" or ref.max_args,\n                    ref.max_args == 1 and \"\" or \"s\"\n                )\n            )\n            return\n        end\n\n        if not ref.name then\n            log.error(\n                (\"Error when executing `:Neorg %s` - the ending command didn't have a `name` variable associated with it! This is an implementation error on the developer's side, so file a report to the author of the module.\"):format(\n                    data.args\n                )\n            )\n            return\n        end\n\n        if not module.events.defined[ref.name] then\n            module.events.defined[ref.name] = modules.define_event(module, ref.name)\n        end\n\n        local content = vim.list_slice(args, argument_index + 1)\n        content[\"data\"] = data\n        modules.broadcast_event(\n            assert(modules.create_event(module, table.concat({ \"core.neorgcmd.events.\", ref.name }), content))\n        )\n    end,\n\n    --- This function returns all available commands to be used for the :Neorg command\n    ---@param _ nil #Placeholder variable\n    ---@param command string #Supplied by nvim itself; the full typed out command\n    generate_completions = function(_, command)\n        local current_buf = vim.api.nvim_get_current_buf()\n        local is_norg = vim.api.nvim_get_option_value(\"filetype\", { buf = current_buf }) == \"norg\"\n\n        local function check_condition(condition)\n            if condition == nil then\n                return true\n            end\n\n            if condition == \"norg\" and not is_norg then\n                return false\n            end\n\n            if type(condition) == \"function\" then\n                return condition(current_buf, is_norg)\n            end\n\n            return condition\n        end\n\n        command = command:gsub(\"^%s*\", \"\")\n\n        local splitcmd = vim.list_slice(\n            vim.split(command, \" \", {\n                plain = true,\n                trimempty = true,\n            }),\n            2\n        )\n\n        local ref = {\n            subcommands = module.public.neorg_commands,\n        }\n        local last_valid_ref = ref\n        local last_completion_level = 0\n\n        for _, cmd in ipairs(splitcmd) do\n            if not ref or not check_condition(ref.condition) then\n                break\n            end\n\n            ref = ref.subcommands or {}\n            ref = ref[cmd]\n\n            if ref then\n                last_valid_ref = ref\n                last_completion_level = last_completion_level + 1\n            end\n        end\n\n        if not last_valid_ref.subcommands and last_valid_ref.complete then\n            if type(last_valid_ref.complete) == \"function\" then\n                last_valid_ref.complete = last_valid_ref.complete(current_buf, is_norg)\n            end\n\n            if vim.endswith(command, \" \") then\n                local completions = last_valid_ref.complete[#splitcmd - last_completion_level + 1] or {}\n\n                if type(completions) == \"function\" then\n                    completions = completions(current_buf, is_norg) or {}\n                end\n\n                return completions\n            else\n                local completions = last_valid_ref.complete[#splitcmd - last_completion_level] or {}\n\n                if type(completions) == \"function\" then\n                    completions = completions(current_buf, is_norg) or {}\n                end\n\n                return vim.tbl_filter(function(key)\n                    return key:find(splitcmd[#splitcmd])\n                end, completions)\n            end\n        end\n\n        -- TODO: Fix `:Neorg m <tab>` giving invalid completions\n        local keys = ref and vim.tbl_keys(ref.subcommands or {})\n            or (\n                vim.tbl_filter(function(key)\n                    return key:find(splitcmd[#splitcmd])\n                end, vim.tbl_keys(last_valid_ref.subcommands or {}))\n            )\n        table.sort(keys)\n\n        do\n            local subcommands = (ref and ref.subcommands or last_valid_ref.subcommands) or {}\n\n            return vim.tbl_filter(function(key)\n                return check_condition(subcommands[key].condition)\n            end, keys)\n        end\n    end,\n\n    --- Queries the user to select next argument\n    ---@param qargs table #A string of arguments previously supplied to the Neorg command\n    ---@param choices table #all possible choices for the next argument\n    select_next_cmd_arg = function(qargs, choices)\n        local current = table.concat({ \"Neorg \", qargs })\n\n        local query\n\n        if vim.tbl_isempty(choices) then\n            query = function(...)\n                vim.ui.input(...)\n            end\n        else\n            query = function(...)\n                vim.ui.select(choices, ...)\n            end\n        end\n\n        query({\n            prompt = current,\n        }, function(choice)\n            if choice ~= nil then\n                vim.cmd(string.format(\"%s %s\", current, choice))\n            end\n        end)\n    end,\n}\n\nmodule.neorg_post_load = module.public.sync\n\nmodule.on_event = function(event)\n    if event.type == \"core.neorgcmd.events.module.load\" then\n        local ok = pcall(modules.load_module, event.content[1])\n\n        if not ok then\n            vim.notify(string.format(\"Module `%s` does not exist!\", event.content[1]), vim.log.levels.ERROR, {})\n        end\n    end\n\n    if event.type == \"core.neorgcmd.events.module.list\" then\n        local Popup = require(\"nui.popup\")\n\n        local module_list_popup = Popup({\n            position = \"50%\",\n            size = { width = \"50%\", height = \"80%\" },\n            enter = true,\n            buf_options = {\n                filetype = \"norg\",\n                modifiable = true,\n                readonly = false,\n            },\n            win_options = {\n                conceallevel = 3,\n                concealcursor = \"nvi\",\n            },\n        })\n\n        module_list_popup:on(\"VimResized\", function()\n            module_list_popup:update_layout()\n        end)\n\n        local function close()\n            module_list_popup:unmount()\n        end\n\n        module_list_popup:map(\"n\", \"<Esc>\", close, {})\n        module_list_popup:map(\"n\", \"q\", close, {})\n\n        local lines = {}\n\n        for name, _ in pairs(neorg.modules.loaded_modules) do\n            table.insert(lines, \"- `\" .. name .. \"`\")\n        end\n\n        vim.api.nvim_buf_set_lines(module_list_popup.bufnr, 0, -1, true, lines)\n\n        vim.bo[module_list_popup.bufnr].modifiable = false\n\n        module_list_popup:mount()\n    end\nend\n\nmodule.events.subscribed = {\n    [\"core.neorgcmd\"] = {\n        [\"module.load\"] = true,\n        [\"module.list\"] = true,\n    },\n}\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/pivot/module.lua",
    "content": "--[[\n    file: Pivot\n    title: Ordered or Unordered?\n    description: That ~~is~~ was the question. Now you no longer have to ask!\n    summary: Toggles the type of list currently under the cursor.\n    ---\n`core.pivot` allows you to switch (or pivot) between the two list types in Norg with the press of a button.\n\n## Keybinds\n\nThis module exposes the following keybinds (see [`core.keybinds`](@core.keybinds) for instructions on\nmapping them):\n\n- `neorg.pivot.list.toggle` (default binding: `<LocalLeader>lt` [\"list toggle\"]) - takes a\n  list and, based on the opposite type of the first list item, inverts all the other items in that list.\n  Does not respect mixed lists, all items in the list will be converted to the same type.\n- `neorg.pivot.list.invert` (default binding: `<LocalLeader>li` [\"list invert\"]) - same behaviour as\n  the previous keybind, however respects mixed lists - unordered items will become ordered, whereas ordered\n  items will become unordered.\n--]]\n\nlocal neorg = require(\"neorg.core\")\nlocal log, modules, lib = neorg.log, neorg.modules, neorg.lib\n\nlocal module = modules.create(\"core.pivot\")\n\nmodule.setup = function()\n    return {\n        requires = {\n            \"core.integrations.treesitter\",\n        },\n    }\nend\n\nmodule.load = function()\n    vim.keymap.set(\"\", \"<Plug>(neorg.pivot.list.toggle)\", lib.wrap(module.public.change_list, false))\n    vim.keymap.set(\"\", \"<Plug>(neorg.pivot.list.invert)\", lib.wrap(module.public.change_list, true))\nend\n\nmodule.private = {\n    --- Return current node we are on, accounting for possible root of list\n    ---@param bufnr integer\n    ---@return TSNode?\n    get_current_node = function(bufnr)\n        local cursor = vim.api.nvim_win_get_cursor(0)\n        local node = module.required[\"core.integrations.treesitter\"].get_first_node_on_line(bufnr, cursor[1] - 1)\n\n        -- if on root of the list we are actually interested in the first list item not the generic_list node\n        if node and node:type() == \"generic_list\" then\n            node = node:child(0)\n        end\n\n        return node\n    end,\n\n    ---@param node TSNode\n    ---@return TSNode?\n    get_parent_list = function(node)\n        local parent = node:parent()\n        if not parent then\n            return\n        end\n\n        return module.required[\"core.integrations.treesitter\"].find_parent(parent, {\n            \"generic_list\",\n            \"unordered_list1\",\n            \"unordered_list2\",\n            \"unordered_list3\",\n            \"unordered_list4\",\n            \"unordered_list5\",\n            \"unordered_list6\",\n            \"ordered_list1\",\n            \"ordered_list2\",\n            \"ordered_list3\",\n            \"ordered_list4\",\n            \"ordered_list5\",\n            \"ordered_list6\",\n        })\n    end,\n\n    --- Returns the prefix the current list node should be toggled to\n    ---@param node TSNode\n    ---@return string\n    get_target_prefix = function(node)\n        local type = node:type():match(\"^un\") and \"~\" or \"-\"\n        local level = tonumber(node:type():match(\"ordered_list(%d)\")) or 0\n\n        return type:rep(level)\n    end,\n}\n\n---@class core.pivot\nmodule.public = {\n    ---@param invert boolean\n    change_list = neorg.utils.wrap_dotrepeat(function(invert)\n        local buffer = vim.api.nvim_get_current_buf()\n\n        local node = module.private.get_current_node(buffer)\n\n        if not node then\n            log.error(\"No node found under the cursor! Make sure your cursor is in a list.\")\n            return\n        end\n\n        local parent_list = module.private.get_parent_list(node)\n\n        if not parent_list then\n            log.error(\"No list found under the cursor! `toggle-list-type` and `invert-list-type` only work for lists.\")\n            return\n        end\n\n        local first_child = parent_list:iter_children()()\n\n        if not first_child then\n            return\n        end\n\n        local target_prefix = module.private.get_target_prefix(node)\n\n        for child in parent_list:iter_children() do\n            if invert then\n                target_prefix = module.private.get_target_prefix(child)\n            end\n\n            -- We loop over every subchild because list items may have attached\n            -- weak carryover tags which we have to skip.\n            for subchild in child:iter_children() do\n                if subchild:type():match(\"_prefix$\") then\n                    local line, col_start, _, col_end = subchild:range()\n\n                    vim.api.nvim_buf_set_text(buffer, line, col_start, line, col_end - 1, { target_prefix })\n                    break\n                end\n            end\n        end\n    end),\n}\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/presenter/module.lua",
    "content": "--[[\n    file: Core-Presenter\n    title: Powerpoint in Neorg\n    description: The presenter module creates slideshows out of notes or documents.\n    summary: Neorg module to create gorgeous presentation slides.\n    ---\nThe presenter module provides a special Neorg display that resembles an active slideshow\npresentation.\n\nTo set it up, first be sure to set the `zen_mode` variable in the [configuration](#configuration).\nAfterwards, run `:Neorg presenter start` on any Norg file. The presenter will split up your file\nat each level 1 heading, and display each in a different slide.\n\nNOTE: This module is due for a rewrite. All of its behaviour is not fully documented here as it will\nbe overwritten soon anyway.\n\n## Keybinds\n\nThis module exposes the following keybinds (see [`core.keybinds`](@core.keybinds) for instructions on\nmapping them):\n\n- `neorg.presenter.next-page` - go to next page\n- `neorg.presenter.previous-page` - go to previous page\n- `neorg.presenter.close` - close presentation view\n\n--]]\n\nlocal neorg = require(\"neorg.core\")\nlocal log, modules = neorg.log, neorg.modules\n\nlocal module = modules.create(\"core.presenter\")\nlocal api = vim.api\n\nmodule.setup = function()\n    return {\n        success = true,\n        requires = {\n            \"core.queries.native\",\n            \"core.integrations.treesitter\",\n            \"core.ui\",\n        },\n    }\nend\n\nmodule.load = function()\n    local error_loading = false\n\n    if module.config.public.zen_mode == \"truezen\" then\n        modules.load_module(\"core.integrations.truezen\")\n    elseif module.config.public.zen_mode == \"zen-mode\" then\n        modules.load_module(\"core.integrations.zen_mode\")\n    else\n        log.error(\"Unrecognized mode for 'zen_mode' option. Please check your presenter config\")\n        error_loading = true\n    end\n\n    if error_loading then\n        return\n    end\n\n    vim.keymap.set(\"\", \"<Plug>(neorg.presenter.next-page)\", module.public.next_page)\n    vim.keymap.set(\"\", \"<Plug>(neorg.presenter.previous-page)\", module.public.previous_page)\n    vim.keymap.set(\"\", \"<Plug>(neorg.presenter.close)\", module.public.close)\n\n    modules.await(\"core.neorgcmd\", function(neorgcmd)\n        neorgcmd.add_commands_from_table({\n            presenter = {\n                args = 1,\n                condition = \"norg\",\n                subcommands = {\n                    start = { args = 0, name = \"presenter.start\" },\n                    close = { args = 0, name = \"presenter.close\" },\n                },\n            },\n        })\n    end)\nend\n\nmodule.config.public = {\n    -- Zen mode plugin to use. Currenly suppported:\n    --\n    -- - `zen-mode` - https://github.com/folke/zen-mode.nvim\n    -- - `truezen` - https://github.com/Pocco81/TrueZen.nvim\n    zen_mode = \"\",\n}\n\nmodule.private = {\n    data = {},\n    nodes = {},\n    buf = nil,\n    current_page = 1,\n\n    remove_blanklines = function(t)\n        local copy = t\n        for k, _t in pairs(copy) do\n            -- Stops at the first non-blankline text\n            local found_non_blankline = false\n\n            for i = #_t, 1, -1 do\n                if not found_non_blankline then\n                    local value = _t[i]\n                    value = string.gsub(value, \"%s*\", \"\")\n                    if value == \"\" then\n                        table.remove(copy[k], i)\n                    else\n                        found_non_blankline = true\n                    end\n                end\n            end\n        end\n        return copy\n    end,\n}\n\n---@class core.presenter\nmodule.public = {\n    version = \"0.0.8\",\n    present = function()\n        if module.private.buf then\n            log.warn(\"Presentation already started\")\n            return\n        end\n        ---@type core.queries.native\n        local queries = module.required[\"core.queries.native\"]\n\n        -- Get current file and check if it's a norg one\n        local uri = vim.uri_from_bufnr(0)\n        local fname = vim.uri_to_fname(uri)\n\n        if string.sub(fname, -5, -1) ~= \".norg\" then\n            log.error(\"Not on a norg file\")\n            return\n        end\n\n        local tree = {\n            {\n                query = { \"all\", \"heading1\" },\n                recursive = true,\n            },\n        }\n        -- Free the text in memory after reading nodes\n        queries.delete_content(0)\n\n        local results = queries.query_nodes_from_buf(tree, 0)\n\n        if vim.tbl_isempty(results) then\n            log.warn(\"Could not generate the presenter mode (no heading1 present on this file)\")\n            return\n        end\n\n        module.private.nodes = results\n        results = queries.extract_nodes(results, { all_lines = true })\n\n        results = module.private.remove_blanklines(results)\n\n        -- This is a temporary fix because querying the heading1 nodes seems to query the next heading1 node too !\n        for _, res in pairs(results) do\n            if vim.startswith(res[#res], \"* \") then\n                res[#res] = nil\n            end\n        end\n\n        if module.config.public.zen_mode == \"truezen\" and modules.is_module_loaded(\"core.integrations.truezen\") then\n            modules.get_module(\"core.integrations.truezen\").toggle_ataraxis()\n        elseif\n            module.config.public.zen_mode == \"zen-mode\" and modules.is_module_loaded(\"core.integrations.zen_mode\")\n        then\n            modules.get_module(\"core.integrations.zen_mode\").toggle()\n        end\n\n        -- Generate views selection popup\n        local buffer =\n            module.required[\"core.ui\"].create_norg_buffer(\"Norg Presenter\", \"nosplit\", nil, { keybinds = false })\n        if not buffer then\n            return\n        end\n\n        api.nvim_set_option_value(\"modifiable\", true, { buf = buffer })\n        api.nvim_buf_set_lines(buffer, 0, -1, false, results[1])\n        api.nvim_buf_call(buffer, function()\n            vim.cmd(\"set scrolloff=999\")\n        end)\n\n        api.nvim_set_option_value(\"modifiable\", false, { buf = buffer })\n\n        module.private.buf = buffer\n        module.private.data = results\n    end,\n\n    next_page = function()\n        if vim.tbl_isempty(module.private.data) or not module.private.buf then\n            return\n        end\n\n        if vim.tbl_count(module.private.data) == module.private.current_page then\n            api.nvim_set_option_value(\"modifiable\", true, { buf = module.private.buf })\n            api.nvim_buf_set_lines(module.private.buf, 0, -1, false, { \"Press `next` again to close...\" })\n            api.nvim_set_option_value(\"modifiable\", false, { buf = module.private.buf })\n            module.private.current_page = module.private.current_page + 1\n            return\n        elseif vim.tbl_count(module.private.data) < module.private.current_page then\n            module.public.close()\n            return\n        end\n\n        module.private.current_page = module.private.current_page + 1\n\n        api.nvim_set_option_value(\"modifiable\", true, { buf = module.private.buf })\n        api.nvim_buf_set_lines(module.private.buf, 0, -1, false, module.private.data[module.private.current_page])\n        api.nvim_set_option_value(\"modifiable\", false, { buf = module.private.buf })\n    end,\n\n    previous_page = function()\n        if vim.tbl_isempty(module.private.data) or not module.private.buf then\n            return\n        end\n\n        if module.private.current_page == 1 then\n            return\n        end\n\n        module.private.current_page = module.private.current_page - 1\n\n        api.nvim_set_option_value(\"modifiable\", true, { buf = module.private.buf })\n        api.nvim_buf_set_lines(module.private.buf, 0, -1, false, module.private.data[module.private.current_page])\n        api.nvim_set_option_value(\"modifiable\", false, { buf = module.private.buf })\n    end,\n\n    close = function()\n        if not module.private.buf then\n            return\n        end\n\n        if module.config.public.zen_mode == \"truezen\" and modules.is_module_loaded(\"core.integrations.truezen\") then\n            modules.get_module(\"core.integrations.truezen\").toggle_ataraxis()\n        elseif\n            module.config.public.zen_mode == \"zen-mode\" and modules.is_module_loaded(\"core.integrations.zen_mode\")\n        then\n            modules.get_module(\"core.integrations.zen_mode\").toggle()\n        end\n\n        api.nvim_buf_delete(module.private.buf, {})\n        module.private.data = {}\n        module.private.current_page = 1\n        module.private.buf = nil\n        module.private.nodes = {}\n    end,\n}\n\nmodule.on_event = function(event)\n    if event.split_type[1] == \"core.neorgcmd\" then\n        if event.split_type[2] == \"presenter.start\" then\n            module.public.present()\n        end\n    end\nend\n\nmodule.events.subscribed = {\n    [\"core.neorgcmd\"] = {\n        [\"presenter.start\"] = true,\n        [\"presenter.close\"] = true,\n    },\n}\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/promo/module.lua",
    "content": "--[[\n    file: Promo\n    title: You have Received a Promotion!\n    description: The `promo` module increases or decreases the nesting level of nestable items by repeating their characters.\n    summary: Promotes or demotes nestable items within Neorg files.\n    ---\nWhen dealing with Norg, it may sometimes be tedious to continually repeat a single character to increase\nyour nesting level. For example, for a level 6 nested unordered list, you need to repeat the `-` character\nsix times:\n```norg\n------ This is my item!\n```\n\nThe `core.promo` module allows you to indent these object by utilizing the inbuilt Neovim keybinds:\n- `>>` - increase the indentation level for the current object (also dedents children)\n- `<<` - decrease the indentation level for the current object recursively (also dedents children)\n- `>.` - increase the indentation level for the current object (non-recursively)\n- `<,` - decrease the indentation level for the current object (non-recursively)\n\nIn insert mode, you are also provided with two keybinds, also being Neovim defaults:\n- `<C-t>` increase the indentation level for the current object\n- `<C-d>` decrease the indentation level for the current object\n\nThis module is commonly used with the [`core.itero`](@core.itero) module for an effective workflow.\n\n## Keybinds\n\nThis module exposes the following keybinds (see [`core.keybinds`](@core.keybinds) for instructions on\nmapping them):\n\n- `neorg.promo.promote` - Promote item on current line\n- `neorg.promo.promote.nested` - Promote current line and nested items\n- `neorg.promo.promote.range` - Promote all items in range\n- `neorg.promo.demote` - similar\n- `neorg.promo.demote.nested` - similar\n- `neorg.promo.demote.range` - similar\n\n--]]\n\nlocal neorg = require(\"neorg.core\")\nlocal modules = neorg.modules\n\nlocal module = modules.create(\"core.promo\")\nlocal indent\n\nmodule.setup = function()\n    return {\n        success = true,\n        requires = {\n            \"core.integrations.treesitter\",\n            \"core.esupports.indent\",\n        },\n    }\nend\n\nmodule.load = function()\n    ---@type core.esupports.indent\n    indent = module.required[\"core.esupports.indent\"]\n    vim.keymap.set({ \"n\", \"i\" }, \"<Plug>(neorg.promo.promote)\", module.public.promote)\n    vim.keymap.set({ \"n\", \"i\" }, \"<Plug>(neorg.promo.promote.nested)\", module.public.promote_nested)\n    vim.keymap.set({ \"n\", \"i\", \"v\" }, \"<Plug>(neorg.promo.promote.range)\", module.public.promote_range)\n    vim.keymap.set({ \"n\", \"i\" }, \"<Plug>(neorg.promo.demote)\", module.public.demote)\n    vim.keymap.set({ \"n\", \"i\" }, \"<Plug>(neorg.promo.demote.nested)\", module.public.demote_nested)\n    vim.keymap.set({ \"n\", \"i\", \"v\" }, \"<Plug>(neorg.promo.demote.range)\", module.public.demote_range)\nend\n\nmodule.private = {\n    types = {\n        heading = {\n            pattern = \"^heading(%d)$\",\n            prefix = \"*\",\n        },\n        unordered_list = {\n            pattern = \"^unordered_list(%d)$\",\n            prefix = \"-\",\n        },\n        ordered_list = {\n            pattern = \"^ordered_list(%d)$\",\n            prefix = \"~\",\n        },\n        quote = {\n            pattern = \"^quote(%d)$\",\n            prefix = \">\",\n        },\n    },\n\n    ignore_types = {\n        \"generic_list\",\n        \"quote\",\n    },\n\n    get_line = function(buffer, target_row)\n        return vim.api.nvim_buf_get_lines(buffer, target_row, target_row + 1, true)[1]\n    end,\n\n    get_promotable_node_prefix = function(node)\n        for _, data in pairs(module.private.types) do\n            if node:type():match(data.pattern) then\n                return data.prefix\n            end\n        end\n    end,\n\n    promote_or_demote = function(buffer, mode, row, reindent_children, affect_children)\n        -- Treesitter node helpers\n        local function get_header_prefix_node(header_node)\n            local first_child = header_node:child(0)\n            assert(first_child:type() == header_node:type() .. \"_prefix\")\n            return first_child\n        end\n\n        local function get_node_row_range(node)\n            local row_start, _ = node:start()\n            local row_end, _ = node:end_()\n            return row_start, row_end\n        end\n\n        local function is_prefix_node(node)\n            return node:type():match(\"_prefix$\") ~= nil\n        end\n\n        local function get_prefix_position_and_level(prefix_node)\n            assert(is_prefix_node(prefix_node))\n            local row_start, col_start, row_end, col_end = prefix_node:range()\n\n            assert(row_start == row_end)\n            assert(col_start + 2 <= col_end)\n            return row_start, col_start, (col_end - col_start - 1)\n        end\n\n        local function is_quasi_prefix(target_row)\n            local line = module.private.get_line(buffer, target_row)\n            -- NOTE: This is a hardcoded check determined by the limitations of\n            -- the first generation treesitter parser.\n            return line:match(\"^%s*[%-~%*]+%s*$\")\n        end\n\n        local function adjust_quasi_prefix(target_row, count)\n            local line = module.private.get_line(buffer, target_row)\n            local l, r = line:find(\"%S+\")\n            assert(l)\n            assert(count ~= 0)\n            if count > 0 then\n                vim.api.nvim_buf_set_text(buffer, target_row, l - 1, target_row, l - 1, { line:sub(l, l):rep(count) })\n            else\n                local level_remain = math.max(1, r - l + 1 + count)\n                vim.api.nvim_buf_set_text(buffer, target_row, l - 1, target_row, r - level_remain, {})\n            end\n        end\n\n        local root_node = module.required[\"core.integrations.treesitter\"].get_first_node_on_line(\n            buffer,\n            row,\n            module.private.ignore_types\n        )\n\n        local action_count = vim.v.count1\n\n        if not root_node or root_node:has_error() then\n            if is_quasi_prefix(row) then\n                adjust_quasi_prefix(row, action_count * (mode == \"promote\" and 1 or -1))\n            end\n            return\n        end\n\n        local root_prefix_char = module.private.get_promotable_node_prefix(root_node)\n        if not root_prefix_char then\n            local n_space_diff = vim.bo.shiftwidth * action_count\n            if mode == \"demote\" then\n                n_space_diff = -n_space_diff\n            end\n\n            local current_visual_indent = vim.fn.indent(row + 1)\n            local new_indent = math.max(0, current_visual_indent + n_space_diff)\n\n            indent.buffer_set_line_indent(buffer, row, new_indent)\n            return\n        end\n\n        local root_prefix_node = get_header_prefix_node(root_node)\n        local _, _, root_level = get_prefix_position_and_level(root_prefix_node)\n\n        local adjust_prefix\n        if mode == \"promote\" then\n            adjust_prefix = function(prefix_node)\n                local prefix_row, prefix_col, _ = get_prefix_position_and_level(prefix_node)\n                vim.api.nvim_buf_set_text(\n                    buffer,\n                    prefix_row,\n                    prefix_col,\n                    prefix_row,\n                    prefix_col,\n                    { root_prefix_char:rep(action_count) }\n                )\n            end\n        else\n            action_count = math.min(action_count, root_level - 1)\n            assert(action_count >= 0)\n\n            if action_count == 0 then\n                assert(root_level == 1)\n                return\n            end\n\n            adjust_prefix = function(prefix_node)\n                local prefix_row, prefix_col, level = get_prefix_position_and_level(prefix_node)\n                assert(level > action_count)\n                vim.api.nvim_buf_set_text(buffer, prefix_row, prefix_col, prefix_row, prefix_col + action_count, {})\n            end\n        end\n\n        if not affect_children then\n            adjust_prefix(root_prefix_node)\n            return\n        end\n\n        local function apply_recursive_normal(node, is_target, f)\n            if not is_target(node) then\n                return\n            end\n            f(node)\n            for child in node:iter_children() do\n                apply_recursive_normal(child, is_target, f)\n            end\n        end\n\n        local function apply_recursive_verylow(node, is_target, f)\n            local started = false\n            local _, _, level = get_prefix_position_and_level(get_header_prefix_node(node))\n\n            f(node)\n\n            for sibling in node:parent():iter_children() do\n                if started then\n                    if not is_target(sibling) then\n                        break\n                    end\n\n                    local _, _, sibling_level = get_prefix_position_and_level(get_header_prefix_node(sibling))\n\n                    if sibling_level <= level then\n                        break\n                    end\n                    f(sibling)\n                end\n                started = started or (sibling == node)\n            end\n        end\n\n        local HEADING_VERYLOW_LEVEL = 6\n\n        local indent_targets = {}\n        local apply_recursive = root_level < HEADING_VERYLOW_LEVEL and apply_recursive_normal or apply_recursive_verylow\n\n        apply_recursive(root_node, function(node)\n            return module.private.get_promotable_node_prefix(node) == root_prefix_char\n        end, function(node)\n            indent_targets[#indent_targets + 1] = node\n        end)\n\n        local indent_row_start, indent_row_end = get_node_row_range(root_node)\n        if root_level >= HEADING_VERYLOW_LEVEL then\n            local _, last_child_row_end = get_node_row_range(indent_targets[#indent_targets])\n            indent_row_end = math.max(indent_row_end, last_child_row_end)\n        end\n\n        for _, node in ipairs(indent_targets) do\n            adjust_prefix(get_header_prefix_node(node))\n        end\n\n        if not reindent_children then\n            return\n        end\n\n        indent.reindent_range(buffer, indent_row_start, indent_row_end)\n    end,\n}\n\n---@class core.promo\nmodule.public = {\n    promote = neorg.utils.wrap_dotrepeat(function()\n        local buffer = vim.api.nvim_get_current_buf()\n        local row = vim.api.nvim_win_get_cursor(0)[1] - 1\n\n        module.private.promote_or_demote(buffer, \"promote\", row, true, false)\n    end),\n    promote_nested = neorg.utils.wrap_dotrepeat(function()\n        local buffer = vim.api.nvim_get_current_buf()\n        local row = vim.api.nvim_win_get_cursor(0)[1] - 1\n\n        module.private.promote_or_demote(buffer, \"promote\", row, true, true)\n    end),\n    promote_range = neorg.utils.wrap_dotrepeat(function()\n        local buffer = vim.api.nvim_get_current_buf()\n        local start_pos = vim.api.nvim_buf_get_mark(buffer, \"<\")\n        local end_pos = vim.api.nvim_buf_get_mark(buffer, \">\")\n\n        for i = start_pos[1], end_pos[1] do\n            module.private.promote_or_demote(buffer, \"promote\", i - 1, false, false)\n        end\n        indent.reindent_range(buffer, start_pos[1] - 1, end_pos[1])\n    end),\n    demote = neorg.utils.wrap_dotrepeat(function()\n        local buffer = vim.api.nvim_get_current_buf()\n        local row = vim.api.nvim_win_get_cursor(0)[1] - 1\n\n        module.private.promote_or_demote(buffer, \"demote\", row, true, false)\n    end),\n    demote_nested = neorg.utils.wrap_dotrepeat(function()\n        local buffer = vim.api.nvim_get_current_buf()\n        local row = vim.api.nvim_win_get_cursor(0)[1] - 1\n\n        module.private.promote_or_demote(buffer, \"demote\", row, true, true)\n    end),\n    demote_range = neorg.utils.wrap_dotrepeat(function()\n        local buffer = vim.api.nvim_get_current_buf()\n        local start_pos = vim.api.nvim_buf_get_mark(buffer, \"<\")\n        local end_pos = vim.api.nvim_buf_get_mark(buffer, \">\")\n\n        for i = start_pos[1], end_pos[1] do\n            module.private.promote_or_demote(buffer, \"demote\", i - 1, false, false)\n        end\n        indent.reindent_range(buffer, start_pos[1] - 1, end_pos[1])\n    end),\n}\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/qol/toc/module.lua",
    "content": "--[[\n    file: TOC\n    title: A Bird's Eye View of Norg Documents\n    description: The TOC module generates a table of contents for a given Norg buffer.\n    summary: Generates a table of contents for a given Norg buffer.\n    ---\n\nThe TOC module exposes a single command - `:Neorg toc`. This command can be executed with one of three\noptional arguments: `left`, `right` and `qflist`.\n\nWhen `left` or `right` is supplied, the Table of Contents split is placed on that side of the screen.\nWhen the `qflist` argument is provided, the whole table of contents is sent to the Neovim quickfix list,\nshould that be more convenient for you.\n\nWhen in the TOC view, `<CR>` can be pressed on any of the entries to move to that location in the respective\nNorg document. The TOC view updates automatically when switching buffers.\n--]]\n\nlocal neorg = require(\"neorg.core\")\nlocal modules, utils, log = neorg.modules, neorg.utils, neorg.log\n\nlocal module = modules.create(\"core.qol.toc\")\n\nmodule.setup = function()\n    return {\n        requires = { \"core.integrations.treesitter\", \"core.ui\" },\n    }\nend\n\n---Track if the next TOC open was automatic. Used to determine if we should enter the TOC or not.\nlocal next_open_is_auto = false\nmodule.load = function()\n    modules.await(\"core.neorgcmd\", function(neorgcmd)\n        neorgcmd.add_commands_from_table({\n            toc = {\n                name = \"core.qol.toc\",\n                max_args = 1,\n                condition = \"norg\",\n                complete = {\n                    { \"left\", \"right\", \"qflist\" },\n                },\n            },\n        })\n    end)\n\n    if module.config.public.auto_toc.open then\n        vim.api.nvim_create_autocmd(\"BufWinEnter\", {\n            pattern = \"*.norg\",\n            callback = function()\n                vim.schedule(function()\n                    if vim.bo.filetype == \"norg\" then\n                        next_open_is_auto = true\n                        vim.cmd([[Neorg toc]])\n                    end\n                end)\n            end,\n        })\n    end\nend\n\nmodule.config.public = {\n    -- Close the Table of Contents after an entry in the table is picked\n    close_after_use = false,\n\n    -- Width of the Table of Contents window will automatically fit its longest line, up to\n    -- `max_width`\n    fit_width = true,\n\n    -- Max width of the ToC window when `fit_width = true` (in columns)\n    max_width = 30,\n\n    -- When set, the ToC window will always be this many cols wide.\n    -- will override `fit_width` and ignore `max_width`\n    fixed_width = nil,\n\n    -- Enable `cursorline` in the ToC window, and sync the cursor position between ToC and content\n    -- window\n    sync_cursorline = true,\n\n    -- Enter a ToC window opened manually (any ToC window not opened by auto_toc)\n    enter = true,\n\n    -- Options for automatically opening/entering the ToC window\n    auto_toc = {\n        -- Automatically open a ToC window when entering any `norg` buffer\n        open = false,\n        -- Enter an automatically opened ToC window\n        enter = false,\n        -- Automatically close the ToC window when there is no longer an open norg buffer\n        close = true,\n        -- Will exit nvim if the ToC is the last buffer on the screen, similar to help windows\n        exit_nvim = true,\n    },\n}\n\nlocal ui_data_of_tabpage = {}\nlocal data_of_norg_buf = {}\nlocal toc_namespace\n\nlocal function upper_bound(array, v)\n    -- assume array is sorted\n    -- find index of first element in array that is > v\n    local l = 1\n    local r = #array\n\n    while l <= r do\n        local m = math.floor((l + r) / 2)\n        if v >= array[m] then\n            l = m + 1\n        else\n            r = m - 1\n        end\n    end\n\n    return l\nend\n\nlocal function get_target_location_under_cursor(ui_data)\n    local ui_window = vim.fn.bufwinid(ui_data.buffer)\n    local curline = vim.api.nvim_win_get_cursor(ui_window)[1]\n    local offset = ui_data.start_lines.offset\n    local extmark_lookup = data_of_norg_buf[ui_data.norg_buffer].extmarks[curline - offset]\n\n    if not extmark_lookup then\n        return\n    end\n\n    return vim.api.nvim_buf_get_extmark_by_id(ui_data.norg_buffer, toc_namespace, extmark_lookup, {})\nend\n\nlocal toc_query\n\n---@class core.qol.toc\nmodule.public = {\n    parse_toc_macro = function(buffer)\n        local toc, toc_name = false, nil\n\n        local success = module.required[\"core.integrations.treesitter\"].execute_query(\n            [[\n                (infirm_tag\n                    (tag_name) @name\n                    (tag_parameters)? @parameters)\n            ]],\n            function(query, id, node)\n                local capture_name = query.captures[id]\n\n                if\n                    capture_name == \"name\"\n                    and module.required[\"core.integrations.treesitter\"].get_node_text(node, buffer):lower() == \"toc\"\n                then\n                    toc = true\n                elseif capture_name == \"parameters\" and toc then\n                    toc_name = module.required[\"core.integrations.treesitter\"].get_node_text(node, buffer)\n                    return true\n                end\n            end,\n            buffer\n        )\n\n        if not success then\n            return\n        end\n\n        return toc_name\n    end,\n\n    generate_qflist = function(original_buffer)\n        local prefix, title\n        local qflist_data = {}\n\n        local success = module.required[\"core.integrations.treesitter\"].execute_query(\n            [[\n            (_\n              .\n              (_) @prefix\n              .\n              title: (paragraph_segment) @title)\n            ]],\n            function(query, id, node)\n                local capture = query.captures[id]\n\n                if capture == \"prefix\" then\n                    if node:type():match(\"_prefix$\") then\n                        prefix = node\n                    else\n                        prefix = nil\n                    end\n                    title = nil\n                elseif capture == \"title\" then\n                    title = node\n                end\n\n                if prefix and title then\n                    local prefix_text =\n                        module.required[\"core.integrations.treesitter\"].get_node_text(prefix, original_buffer)\n                    local title_text =\n                        module.required[\"core.integrations.treesitter\"].get_node_text(title, original_buffer)\n\n                    if prefix_text:sub(1, 1) ~= \"*\" and prefix_text:match(\"^%W%W\") then\n                        prefix_text = table.concat({ prefix_text:sub(1, 1), \" \" })\n                    end\n\n                    table.insert(qflist_data, {\n                        bufnr = original_buffer,\n                        lnum = (prefix:start()) + 1,\n                        text = table.concat({ prefix_text, title_text }),\n                    })\n\n                    prefix, title = nil, nil\n                end\n            end,\n            original_buffer\n        )\n\n        if not success then\n            return\n        end\n\n        return qflist_data\n    end,\n\n    -- Update ui cursor according to norg cursor\n    update_cursor = function(ui_data)\n        local norg_window = vim.fn.bufwinid(ui_data.norg_buffer)\n        local norg_data = data_of_norg_buf[ui_data.norg_buffer]\n        local ui_window = vim.fn.bufwinid(ui_data.buffer)\n        assert(ui_window ~= -1)\n\n        local current_row_1b = vim.fn.line(\".\", norg_window)\n        if norg_data.last_row == current_row_1b then\n            return\n        end\n        norg_data.last_row = current_row_1b\n\n        local start_lines = ui_data.start_lines\n        assert(start_lines)\n\n        local current_toc_item_idx = upper_bound(start_lines, current_row_1b - 1) - 1\n        local current_toc_row = (\n            current_toc_item_idx == 0 and math.max(1, start_lines.offset)\n            or current_toc_item_idx + start_lines.offset\n        )\n        vim.api.nvim_win_set_cursor(ui_window, { current_toc_row, 0 })\n    end,\n\n    update_toc = function(toc_title, ui_data, norg_buffer)\n        local ui_buffer = ui_data.buffer\n        ui_data.norg_buffer = norg_buffer\n\n        if not vim.api.nvim_buf_is_valid(ui_buffer) then\n            log.error(\"update_toc called with invalid ui buffer\")\n            return\n        end\n\n        vim.bo[ui_buffer].modifiable = true\n        vim.api.nvim_buf_clear_namespace(norg_buffer, toc_namespace, 0, -1)\n\n        table.insert(toc_title, \"\")\n        vim.api.nvim_buf_set_lines(ui_buffer, 0, -1, true, toc_title)\n\n        local norg_data = {}\n        data_of_norg_buf[norg_buffer] = norg_data\n\n        local extmarks = {}\n        norg_data.extmarks = extmarks\n\n        local offset = vim.api.nvim_buf_line_count(ui_buffer)\n        local start_lines = { offset = offset }\n        ui_data.start_lines = start_lines\n\n        ---@type vim.treesitter.Query\n        toc_query = toc_query\n            or utils.ts_parse_query(\n                \"norg\",\n                [[ (\n                  [(heading1_prefix)\n                   (heading2_prefix)\n                   (heading3_prefix)\n                   (heading4_prefix)\n                   (heading5_prefix)\n                   (heading6_prefix)\n                  ] @prefix\n                  state: (detached_modifier_extension . (_)@modifier)?\n                  title: (paragraph_segment) @title\n                ) ]]\n            )\n\n        local norg_root = module.required[\"core.integrations.treesitter\"].get_document_root(norg_buffer)\n        if not norg_root then\n            return\n        end\n\n        local current_capture\n        local heading_nodes = {}\n        for id, node in toc_query:iter_captures(norg_root, norg_buffer) do\n            local type = toc_query.captures[id]\n            if type == \"prefix\" then\n                current_capture = {}\n                table.insert(heading_nodes, current_capture)\n            end\n            current_capture[type] = node\n        end\n\n        local heading_texts = {}\n        for _, capture in ipairs(heading_nodes) do\n            if capture.modifier and capture.modifier:type() == \"todo_item_cancelled\" then\n                goto continue\n            end\n\n            local row_start_0b, col_start_0b, _, _ = capture.prefix:range()\n            local _, _, row_end_0bin, col_end_0bex = capture.title:range()\n\n            table.insert(start_lines, row_start_0b)\n            table.insert(\n                extmarks,\n                vim.api.nvim_buf_set_extmark(norg_buffer, toc_namespace, row_start_0b, col_start_0b, {})\n            )\n\n            for _, line in\n                ipairs(\n                    vim.api.nvim_buf_get_text(norg_buffer, row_start_0b, col_start_0b, row_end_0bin, col_end_0bex, {})\n                )\n            do\n                table.insert(heading_texts, line)\n            end\n\n            ::continue::\n        end\n\n        vim.api.nvim_buf_set_lines(ui_buffer, -1, -1, true, heading_texts)\n\n        vim.bo[ui_buffer].modifiable = false\n\n        vim.api.nvim_buf_set_keymap(ui_buffer, \"n\", \"<CR>\", \"\", {\n            callback = function()\n                local location = get_target_location_under_cursor(ui_data)\n                if not location then\n                    return\n                end\n\n                local norg_window = vim.fn.bufwinid(norg_buffer)\n                if norg_window == -1 then\n                    local toc_window = vim.fn.bufwinid(ui_data.buffer)\n                    local buf_width = nil\n                    if toc_window ~= -1 then\n                        buf_width = vim.api.nvim_win_get_width(toc_window) - module.private.get_toc_width(ui_data)\n                        if buf_width < 1 then\n                            buf_width = nil\n                        end\n                    end\n                    norg_window =\n                        vim.api.nvim_open_win(norg_buffer, true, { win = 0, vertical = true, width = buf_width })\n                else\n                    vim.api.nvim_set_current_win(norg_window)\n                    vim.api.nvim_set_current_buf(norg_buffer)\n                end\n                vim.api.nvim_win_set_cursor(norg_window, { location[1] + 1, location[2] })\n\n                if module.config.public.close_after_use then\n                    vim.api.nvim_buf_delete(ui_buffer, { force = true })\n                end\n            end,\n        })\n\n        if module.config.public.sync_cursorline then\n            module.public.update_cursor(ui_data)\n        end\n    end,\n}\n\nmodule.private = {\n    ---get the width of the ToC window\n    ---@param ui_data table\n    ---@return number\n    get_toc_width = function(ui_data)\n        if type(module.config.public.fixed_width) == \"number\" then\n            return module.config.public.fixed_width\n        end\n        local max_virtcol_1bex = module.private.get_max_virtcol(ui_data.window)\n        local current_winwidth = vim.api.nvim_win_get_width(ui_data.window)\n        local new_winwidth = math.min(current_winwidth, module.config.public.max_width, max_virtcol_1bex - 1)\n        return new_winwidth + 1\n    end,\n\n    get_max_virtcol = function(win)\n        local n_line = vim.fn.line(\"$\", win)\n        local result = 1\n        for i = 1, n_line do\n            result = math.max(result, vim.fn.virtcol({ i, \"$\" }, false, win))\n        end\n        return result\n    end,\n}\n\nlocal function get_norg_ui(norg_buffer)\n    local tabpage = vim.api.nvim_win_get_tabpage(vim.fn.bufwinid(norg_buffer))\n    return ui_data_of_tabpage[tabpage]\nend\n\n---Guard an autocommand callback function with a check that the ToC is still open\n---@param listener function\n---@return function\nlocal function unlisten_if_closed(listener)\n    return function(ev)\n        if vim.tbl_isempty(ui_data_of_tabpage) then\n            return true\n        end\n\n        local norg_buffer = ev.buf\n        local ui_data = get_norg_ui(norg_buffer)\n        if not ui_data or vim.fn.bufwinid(ui_data.buffer) == -1 then\n            return\n        end\n\n        return listener(norg_buffer, ui_data)\n    end\nend\n\n---Create a split window and buffer for the table of contents. Set buffer and window options\n---accordingly\n---@param tabpage number\n---@param split_dir \"left\" | \"right\"\n---@param enter boolean\n---@return table\nlocal function create_ui(tabpage, split_dir, enter)\n    assert(tabpage == vim.api.nvim_get_current_tabpage())\n\n    toc_namespace = toc_namespace or vim.api.nvim_create_namespace(\"neorg/toc\")\n    local ui_buffer, ui_window = module.required[\"core.ui\"].create_vsplit(\n        (\"toc-%d\"):format(tabpage),\n        enter,\n        { ft = \"norg\" },\n        { split = split_dir, win = 0, style = \"minimal\" }\n    )\n\n    local ui_wo = vim.wo[ui_window]\n    ui_wo.scrolloff = 999\n    ui_wo.conceallevel = 0\n    ui_wo.foldmethod = \"expr\"\n    ui_wo.foldexpr = \"v:lua.vim.treesitter.foldexpr()\"\n    ui_wo.foldlevel = 99\n    ui_wo.winfixbuf = true\n    ui_wo.winfixwidth = true\n\n    if module.config.public.sync_cursorline then\n        ui_wo.cursorline = true\n    end\n\n    local ui_data = {\n        buffer = ui_buffer,\n        tabpage = tabpage,\n        window = ui_window,\n    }\n\n    ui_data_of_tabpage[tabpage] = ui_data\n\n    return ui_data\nend\n\n--- should we enter the ToC window?\nlocal function enter_toc_win()\n    local do_enter = module.config.public.enter\n    if next_open_is_auto then\n        do_enter = module.config.public.auto_toc.enter\n    end\n    return do_enter\nend\n\nmodule.on_event = function(event)\n    if event.split_type[2] ~= module.name then\n        return\n    end\n\n    local toc_title = vim.split(module.public.parse_toc_macro(event.buffer) or \"Table of Contents\", \"\\n\")\n    local norg_buffer = event.buffer\n\n    if event.content and event.content[1] == \"qflist\" then\n        local qflist = module.public.generate_qflist(event.buffer)\n\n        if not qflist then\n            utils.notify(\"An error occurred and the qflist could not be generated\", vim.log.levels.WARN)\n            return\n        end\n\n        vim.fn.setqflist(qflist, \"r\")\n        vim.fn.setqflist({}, \"a\", { title = toc_title[1] })\n        vim.cmd.copen()\n\n        return\n    end\n\n    local tabpage = vim.api.nvim_win_get_tabpage(vim.fn.bufwinid(norg_buffer))\n    if ui_data_of_tabpage[tabpage] then\n        if norg_buffer == ui_data_of_tabpage[tabpage].buffer then\n            return\n        end\n        module.public.update_toc(toc_title, ui_data_of_tabpage[tabpage], norg_buffer)\n\n        if enter_toc_win() then\n            vim.api.nvim_set_current_win(ui_data_of_tabpage[tabpage].window)\n        end\n        return\n    end\n\n    local ui_data = create_ui(tabpage, event.content[1] or \"left\", enter_toc_win())\n    next_open_is_auto = false\n\n    module.public.update_toc(toc_title, ui_data_of_tabpage[tabpage], norg_buffer)\n\n    if module.config.public.fit_width then\n        vim.api.nvim_win_set_width(ui_data.window, module.private.get_toc_width(ui_data))\n    end\n\n    local close_buffer_callback = function()\n        -- Check if ui_buffer exists before deleting it\n        if vim.api.nvim_buf_is_loaded(ui_data.buffer) then\n            vim.api.nvim_buf_delete(ui_data.buffer, { force = true })\n        end\n        ui_data_of_tabpage[tabpage] = nil\n    end\n\n    vim.keymap.set(\"n\", \"q\", close_buffer_callback, { buffer = ui_data.buffer })\n\n    --- WinClosed matches against the win number as a string, not the buf number\n    vim.api.nvim_create_autocmd(\"WinClosed\", {\n        pattern = tostring(ui_data.window),\n        callback = close_buffer_callback,\n    })\n\n    vim.api.nvim_create_autocmd(\"BufWritePost\", {\n        pattern = \"*.norg\",\n        callback = unlisten_if_closed(function(buf, ui)\n            toc_title = vim.split(module.public.parse_toc_macro(buf) or \"Table of Contents\", \"\\n\")\n            data_of_norg_buf[buf].last_row = nil -- invalidate cursor cache\n            module.public.update_toc(toc_title, ui, buf)\n        end),\n    })\n\n    vim.api.nvim_create_autocmd(\"BufEnter\", {\n        pattern = \"*.norg\",\n        callback = unlisten_if_closed(function(buf, ui)\n            if buf == ui.buffer or buf == ui.norg_buffer then\n                return\n            end\n\n            toc_title = vim.split(module.public.parse_toc_macro(buf) or \"Table of Contents\", \"\\n\")\n            module.public.update_toc(toc_title, ui, buf)\n        end),\n    })\n\n    -- Sync cursor: ToC -> content\n    if module.config.public.sync_cursorline then\n        -- Ignore the first (fake) CursorMoved coming together with BufEnter of the ToC buffer\n        vim.api.nvim_create_autocmd(\"BufEnter\", {\n            buffer = ui_data.buffer,\n            callback = function(_ev)\n                ui_data.cursor_start_moving = false\n            end,\n        })\n\n        vim.api.nvim_create_autocmd({ \"CursorMoved\", \"CursorMovedI\" }, {\n            buffer = ui_data.buffer,\n            callback = function(ev)\n                assert(ev.buf == ui_data.buffer)\n\n                if vim.fn.bufwinid(ui_data.norg_buffer) == -1 then\n                    return\n                end\n\n                -- Ignore the first (fake) CursorMoved coming together with BufEnter of the ToC buffer\n                if ui_data.cursor_start_moving then\n                    local location = get_target_location_under_cursor(ui_data)\n                    if location then\n                        local norg_window = vim.fn.bufwinid(ui_data.norg_buffer)\n                        vim.api.nvim_win_set_cursor(norg_window, { location[1] + 1, location[2] })\n                        vim.api.nvim_buf_call(ui_data.norg_buffer, function()\n                            vim.cmd(\"normal! zz\")\n                        end)\n                    end\n                end\n                ui_data.cursor_start_moving = true\n            end,\n        })\n\n        -- Sync cursor: content -> ToC\n        vim.api.nvim_create_autocmd({ \"CursorMoved\", \"CursorMovedI\" }, {\n            pattern = \"*.norg\",\n            callback = unlisten_if_closed(function(buf, ui)\n                if buf ~= ui.norg_buffer then\n                    return\n                end\n\n                if not data_of_norg_buf[buf] then\n                    -- toc not yet created because BufEnter is not yet triggered\n                    return\n                end\n\n                module.public.update_cursor(ui)\n            end),\n        })\n\n        -- When leaving the content buffer, add its last cursor position to jump list\n        vim.api.nvim_create_autocmd(\"BufLeave\", {\n            pattern = \"*.norg\",\n            callback = unlisten_if_closed(function(_norg_buffer, _ui_data)\n                vim.cmd(\"normal! m'\")\n            end),\n        })\n    end\n\n    if module.config.public.auto_toc.exit_nvim then\n        vim.api.nvim_create_autocmd(\"WinEnter\", {\n            buffer = ui_data.buffer,\n            callback = unlisten_if_closed(function(_, _)\n                vim.schedule(function()\n                    -- count the number of 'real' (non-floating) windows. This avoids noice popups\n                    -- and nvim notify popups causing nvim to stay open\n                    local real_windows = vim.iter(vim.api.nvim_list_wins())\n                        :filter(function(win)\n                            return vim.api.nvim_win_get_config(win).relative == \"\"\n                        end)\n                        :totable()\n                    if #real_windows == 1 then\n                        vim.schedule(vim.cmd.q)\n                    end\n                end)\n            end),\n        })\n    end\n\n    if module.config.public.auto_toc.close then\n        vim.api.nvim_create_autocmd(\"BufWinLeave\", {\n            pattern = \"*.norg\",\n            callback = unlisten_if_closed(function(_buf, ui)\n                vim.schedule(function()\n                    if vim.fn.winnr(\"$\") > 1 then\n                        local win = vim.fn.bufwinid(ui.buffer)\n                        if win ~= -1 then\n                            vim.api.nvim_win_close(win, true)\n                            close_buffer_callback()\n                        end\n                    end\n                end)\n            end),\n        })\n    end\nend\n\nmodule.events.subscribed = {\n    [\"core.neorgcmd\"] = {\n        [module.name] = true,\n    },\n}\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/qol/todo_items/module.lua",
    "content": "--[[\n    file: Todo-Items\n    title: Todo Item Swiss Army Knife\n    summary: Module for implementing todo lists.\n    ---\nThis module handles the whole concept of toggling TODO items, as well as updating\nparent and/or children items alongside the current item.\n\nThe following keybinds are exposed (with their default binds):\n- `<Plug>(neorg.qol.todo-items.todo.task-done)` (`<LocalLeader>td`)\n- `<Plug>(neorg.qol.todo-items.todo.task-undone)` (`<LocalLeader>tu`)\n- `<Plug>(neorg.qol.todo-items.todo.task-pending)` (`<LocalLeader>tp`)\n- `<Plug>(neorg.qol.todo-items.todo.task-on_hold)` (`<LocalLeader>th`)\n- `<Plug>(neorg.qol.todo-items.todo.task-cancelled)` (`<LocalLeader>tc`)\n- `<Plug>(neorg.qol.todo-items.todo.task-recurring)` (`<LocalLeader>tr`)\n- `<Plug>(neorg.qol.todo-items.todo.task-important)` (`<LocalLeader>ti`)\n- `<Plug>(neorg.qol.todo-items.todo.task-cycle)` (`<C-Space>`)\n- `<Plug>(neorg.qol.todo-items.todo.task-cycle-reverse)` (no default keybind)\n\nWith your cursor on a line that contains an item with a TODO attribute, press\nany of the above keys to toggle the state of that particular item.\nParent items of the same type and children items of the same type are update accordingly.\n--]]\n\nlocal neorg = require(\"neorg.core\")\nlocal log, modules = neorg.log, neorg.modules --[[@as neorg.modules]]\n\nlocal module = modules.create(\"core.qol.todo_items\")\n\nmodule.setup = function()\n    return { success = true, requires = { \"core.integrations.treesitter\" } }\nend\n\nmodule.load = function()\n    for _, task in ipairs({\n        \"done\",\n        \"undone\",\n        \"pending\",\n        \"on-hold\",\n        \"cancelled\",\n        \"important\",\n        \"recurring\",\n        \"ambiguous\",\n        \"cycle\",\n        \"cycle-reverse\",\n    }) do\n        vim.keymap.set(\n            \"\",\n            string.format(\"<Plug>(neorg.qol.todo-items.todo.task-%s)\", task),\n            module.public[\"task-\" .. task]\n        )\n    end\nend\n\nmodule.config.public = {\n    -- The default order of TODO item cycling when cycling via\n    -- `<C-Space>`.\n    --\n    -- Defaults to the following order: `undone`, `done`, `pending`.\n    order = {\n        { \"undone\", \" \" },\n        { \"done\", \"x\" },\n        { \"pending\", \"-\" },\n    },\n\n    -- The default order of TODO item cycling when the item\n    -- has nested children with TODO items.\n    --\n    -- When cycling through TODO items with children it's not\n    -- always sensible to follow the same schema as the `order` table.\n    --\n    -- Defaults to the following order: `undone`, `done`.\n    order_with_children = {\n        { \"undone\", \" \" },\n        { \"done\", \"x\" },\n    },\n\n    -- When set to `true`, will automatically convert parent\n    -- items to TODOs whenever a child item's TODO state is updated.\n    --\n    -- For instance, given the following example:\n    -- ```norg\n    -- - Text\n    -- -- ( ) Child text\n    -- ```\n    --\n    -- When this option is `true` and the child's state is updated to e.g.\n    -- `(x)` via the `<LocaLeader>td` keybind, the new output becomes:\n    -- ```norg\n    -- - (x) Text\n    -- -- (x) Child text\n    -- ```\n    create_todo_parents = false,\n\n    -- Automatically update the parent todo state when a child node is updated.\n    --\n    -- eg:\n    -- ```norg\n    -- - ( ) parent\n    -- -- ( ) child\n    -- ```\n    -- Marking `-- ( ) child` as done (with a keybind) will result in:\n    -- ```norg\n    -- - (-) parent\n    -- -- (x) child\n    -- ```\n    update_todo_parents = true,\n\n    -- When `true`, will automatically create a TODO extension for an item\n    -- if it does not exist and an operation is performed on that item.\n    --\n    -- Given the following example:\n    -- ```norg\n    -- - Test Item\n    -- ```\n    -- With this option set to true, performing an operation (like pressing `<C-space>`\n    -- or what have you) will convert the non-todo item into one:\n    -- ```norg\n    -- - ( ) Test Item\n    -- ```\n    create_todo_items = true,\n}\n\n---@alias TodoItemType \"undone\"\n---|\"pending\"\n---|\"done\"\n---|\"cancelled\"\n---|\"recurring\"\n---|\"on_hold\"\n---|\"urgent\"\n---|\"uncertain\"\n\nmodule.private = {\n    names = {\n        [\"x\"] = \"done\",\n        [\" \"] = \"undone\",\n        [\"-\"] = \"pending\",\n        [\"=\"] = \"on_hold\",\n        [\"_\"] = \"cancelled\",\n        [\"!\"] = \"important\",\n        [\"+\"] = \"recurring\",\n        [\"?\"] = \"ambiguous\",\n    },\n    fire_update_event = function(char, line)\n        local ev =\n            modules.create_event(module, module.events.defined[\"todo-changed\"].type, { char = char, line = line })\n        if ev then\n            modules.broadcast_event(ev)\n        end\n    end,\n    --- Updates the parent todo item for the current todo item if it exists\n    ---@param recursion_level number the index of the parent to change. The higher the number the more the code will traverse up the syntax tree.\n    update_parent = function(buf, line, recursion_level)\n        -- Force a reparse (this is required because otherwise some cached nodes will be incorrect)\n        vim.treesitter.get_parser(buf, \"norg\"):parse()\n\n        -- If present grab the item that is under the cursor\n        local item_at_cursor = module.private.get_todo_item_from_cursor(buf, line)\n\n        if not item_at_cursor then\n            return\n        end\n\n        -- If we set a recursion level then go through and traverse up the syntax tree `recursion_level` times\n        for _ = 0, recursion_level do\n            item_at_cursor = item_at_cursor:parent() ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n        end\n\n        -- If the final item does not exist or the target item is not a detached modifier\n        -- (i.e. it does not have a \"prefix\" node) then it is not a node worth updating.\n        if\n            not item_at_cursor\n            or not item_at_cursor:named_child(0)\n            or not item_at_cursor:named_child(0):type():match(\"prefix\")\n        then\n            return\n        end\n\n        local counts = {\n            undone = 0,\n            pending = 0,\n            done = 0,\n            cancelled = 0,\n            recurring = 0,\n            on_hold = 0,\n            urgent = 0,\n            uncertain = 0,\n        }\n\n        local counter = 0\n\n        -- Go through all the children of the current todo item node and count the amount of \"done\" children\n        for node in item_at_cursor:iter_children() do\n            if node:named_child(1) and node:named_child(1):type() == \"detached_modifier_extension\" then\n                for status in node:named_child(1):iter_children() do\n                    if status:type():match(\"^todo_item_\") then\n                        local type = status:type():match(\"^todo_item_(.+)$\")\n\n                        counts[type] = counts[type] + 1\n\n                        if type == \"cancelled\" then\n                            break\n                        end\n\n                        counter = counter + 1\n                    end\n                end\n            end\n        end\n\n        -- [[\n        --  Compare the counter to the amount of done items.\n        --  If we have even one pending item then set the resulting char to `*`\n        --  If the counter is the same as the done item count then that means all items are complete and we should display a done item in the parent.\n        --  If the done item count is 0 then no task has been completed and we should set an undone item as the parent.\n        --  If all other checks fail and the done item count is less than the total number of todo items then set a pending item.\n        -- ]]\n\n        if counter == 0 then\n            return\n        end\n\n        local resulting_char = \"\"\n\n        if counts.uncertain > 0 and counts.done + counts.uncertain == counter then\n            resulting_char = \"=\"\n        elseif counts.on_hold > 0 and counts.done + counts.on_hold + counts.uncertain == counter then\n            resulting_char = \"=\"\n        elseif counts.pending > 0 then\n            resulting_char = \"-\"\n        elseif counter == counts.done then\n            resulting_char = \"x\"\n        elseif counts.done == 0 then\n            resulting_char = \" \"\n        elseif counts.done < counter then\n            resulting_char = \"-\"\n        end\n\n        local first_status_extension = module.private.find_first_status_extension(item_at_cursor:named_child(1))\n\n        -- TODO(vhyrro):\n        -- Implement a toggleable behaviour where Neorg can automatically convert this:\n        --     * (@ Mon 5th Feb) Test\n        --     ** ( ) Test\n        -- To this:\n        --     * (x|@ Mon 5th Feb) Test\n        --     ** (x) Test\n        if not first_status_extension then\n            if not module.config.public.create_todo_parents then\n                return\n            end\n\n            local row, _, _, column = item_at_cursor:named_child(0):range()\n\n            vim.api.nvim_buf_set_text(buf, row, column, row, column, { \"(\" .. resulting_char .. \") \" })\n\n            module.private.fire_update_event(resulting_char, row)\n\n            module.private.update_parent(buf, line, recursion_level + 1)\n            return\n        end\n\n        local range = module.required[\"core.integrations.treesitter\"].get_node_range(first_status_extension)\n\n        -- Replace the line where the todo item is situated\n        vim.api.nvim_buf_set_text(\n            buf,\n            range.row_start,\n            range.column_start,\n            range.row_end,\n            range.column_end,\n            { resulting_char }\n        )\n\n        module.private.fire_update_event(resulting_char, range.row_start)\n\n        module.private.update_parent(buf, line, recursion_level + 1)\n    end,\n\n    --- Find the first occurence of a status extension within a detached\n    --  modifier extension node.\n    ---@param detached_modifier_extension_node userdata #A valid node of type `detached_modifier_extension`\n    find_first_status_extension = function(detached_modifier_extension_node)\n        if not detached_modifier_extension_node then\n            return\n        end\n\n        for status in detached_modifier_extension_node:iter_children() do ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n            if vim.startswith(status:type(), \"todo_item_\") then\n                return status\n            end\n        end\n    end,\n\n    --- Tries to locate a todo_item node under the cursor\n    ---@return userdata? #The node if it was located, else nil\n    get_todo_item_from_cursor = function(buf, line)\n        local node_at_cursor = module.required[\"core.integrations.treesitter\"].get_first_node_on_line(buf, line)\n\n        if not node_at_cursor then\n            return\n        end\n\n        -- This is done because sometimes the first node can be\n        -- e.g `generic_list`, which only contains the top level list items and\n        -- not their data. It doesn't cost us much to do this operation for other\n        -- nodes anyway.\n        if node_at_cursor:named_child(0) then\n            node_at_cursor = node_at_cursor:named_child(0)\n        end\n\n        while true do\n            if not node_at_cursor then\n                log.trace(\"Could not find TODO item under cursor, aborting...\")\n                return\n            end\n\n            local first_named_child = node_at_cursor:named_child(0)\n\n            if first_named_child and first_named_child:type():match(\"prefix\") then\n                break\n            else\n                node_at_cursor = node_at_cursor:parent()\n            end\n        end\n\n        return node_at_cursor\n    end,\n\n    --- Returns the type of a todo item (either \"done\", \"pending\" or \"undone\")\n    ---@param todo_node userdata #The todo node to extract the data from\n    ---@return TodoItemType? #A todo item type as a string, else nil\n    get_todo_item_type = function(todo_node)\n        if not todo_node or not todo_node:named_child(1) then ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n            return\n        end\n\n        local todo_type = module.private.find_first_status_extension(todo_node:named_child(1)) ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n\n        return todo_type and todo_type:type():sub(string.len(\"todo_item_\") + 1) or nil\n    end,\n\n    --- Converts the current node and all its children to a certain type\n    ---@param buf number the current buffer number\n    ---@param node userdata the node to modify\n    ---@param todo_item_type TodoItemType #The todo item type as a string\n    ---@param char string the character to place within the square brackets of the todo item (one of \"x\", \"*\" or \" \")\n    make_all = function(buf, node, todo_item_type, char)\n        if not node then\n            return\n        end\n\n        local type = node:type():match(\"^(.+)%d+$\") ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n\n        -- If the type of the current todo item differs from the one we want to change to then\n        -- We do this because we don't want to be unnecessarily modifying a line that doesn't need changing\n        if module.private.get_todo_item_type(node) == todo_item_type then\n            return\n        end\n\n        local first_status_extension = module.private.find_first_status_extension(node:named_child(1)) ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n\n        local parent_line\n        if not first_status_extension then\n            if not module.config.public.create_todo_items then\n                return\n            end\n\n            local row, _, _, column = node:named_child(0):range() ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n\n            vim.api.nvim_buf_set_text(buf, row, column, row, column, { \"(\" .. char .. \") \" })\n            parent_line = row\n        else\n            local range = module.required[\"core.integrations.treesitter\"].get_node_range(first_status_extension)\n            parent_line = range.row_start\n\n            module.private.fire_update_event(char, range.row_start)\n\n            vim.api.nvim_buf_set_text(\n                buf,\n                range.row_start,\n                range.column_start,\n                range.row_end,\n                range.column_end,\n                { char }\n            )\n        end\n\n        if module.config.public.update_todo_parents then\n            module.private.update_parent(buf, parent_line, 0)\n        end\n\n        for child in node:iter_children() do ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n            if type == child:type():match(\"^(.+)%d+$\") then\n                module.private.make_all(buf, child, todo_item_type, char)\n            end\n        end\n    end,\n\n    task_cycle = function(buf, linenr, types, alternative_types)\n        local todo_item_at_cursor = module.private.get_todo_item_from_cursor(buf, linenr - 1)\n\n        if not todo_item_at_cursor then\n            return\n        end\n\n        local todo_item_type = module.private.get_todo_item_type(todo_item_at_cursor)\n\n        --- Gets the next item of a flat list based on the first item\n        ---@param type_list table[] #A list of { \"type\", \"char\" } items\n        ---@param item_type string #The `type` field from the `type_list` array\n        ---@return number? #An index into the next item of `type_list`\n        local function get_index(type_list, item_type)\n            for i, element in ipairs(type_list) do\n                if element[1] == item_type then\n                    if i >= #type_list then\n                        return 1\n                    else\n                        return i + 1\n                    end\n                end\n            end\n        end\n\n        if not todo_item_type then\n            if not module.config.public.create_todo_items then\n                return\n            end\n\n            module.private.make_all(buf, todo_item_at_cursor, types[1][1], types[1][2])\n            module.private.update_parent(buf, linenr - 1, 0)\n            return\n        end\n\n        local index = get_index(types, todo_item_type)\n\n        local next = types[index] or types[1]\n\n        for child in todo_item_at_cursor:iter_children() do\n            if module.private.get_todo_item_type(child) then\n                next = alternative_types[get_index(alternative_types, todo_item_type)] or alternative_types[1]\n                break\n            end\n        end\n\n        module.private.make_all(buf, todo_item_at_cursor, next[1], next[2])\n        module.private.update_parent(buf, linenr - 1, 0)\n    end,\n}\n\n---Set the todo item in the given buffer at the given line\n---@param buffer number 0 for current\n---@param line number 1 based line number, 0 for current\n---@param character string\nlocal function task_set_at(buffer, line, character)\n    local name = module.private.names[character]\n    if buffer == 0 then\n        buffer = vim.api.nvim_get_current_buf()\n    end\n    if line == 0 then\n        line = vim.api.nvim_win_get_cursor(0)[1]\n    end\n    local todo_item_at_cursor = module.private.get_todo_item_from_cursor(buffer, line - 1)\n\n    if not todo_item_at_cursor then\n        return\n    end\n\n    module.private.make_all(buffer, todo_item_at_cursor, name, character)\nend\n\nlocal function task_set(character)\n    return neorg.utils.wrap_dotrepeat(function()\n        task_set_at(0, 0, character)\n    end)\nend\n\n---@class core.qol.todo_items\nmodule.public = {\n    [\"set_at\"] = task_set_at,\n    [\"task-done\"] = task_set(\"x\"),\n    [\"task-undone\"] = task_set(\" \"),\n    [\"task-pending\"] = task_set(\"-\"),\n    [\"task-on-hold\"] = task_set(\"=\"),\n    [\"task-cancelled\"] = task_set(\"_\"),\n    [\"task-important\"] = task_set(\"!\"),\n    [\"task-recurring\"] = task_set(\"+\"),\n    [\"task-ambiguous\"] = task_set(\"?\"),\n    [\"task-cycle\"] = function()\n        local buffer = vim.api.nvim_get_current_buf()\n        local cursor = vim.api.nvim_win_get_cursor(0)\n\n        module.private.task_cycle(\n            buffer,\n            cursor[1],\n            module.config.public.order,\n            module.config.public.order_with_children\n        )\n    end,\n    [\"task-cycle-reverse\"] = function()\n        local buffer = vim.api.nvim_get_current_buf()\n        local cursor = vim.api.nvim_win_get_cursor(0)\n\n        module.private.task_cycle(\n            buffer,\n            cursor[1],\n            vim.fn.reverse(module.config.public.order),\n            vim.fn.reverse(module.config.public.order_with_children)\n        )\n    end,\n}\n\nmodule.events.defined = {\n    [\"todo-changed\"] = modules.define_event(module, \"todo-changed\"),\n}\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/queries/native/module.lua",
    "content": "--[[\n    file: Queries-Module\n    title: Queries Made Easy\n    summary: TS wrapper in order to fetch nodes using a custom table.\n    internal: true\n    ---\nThe `core.queries.native` module provides useful Treesitter wrappers\nto query information from Norg documents.\n--]]\n\nlocal neorg = require(\"neorg.core\")\nlocal lib, log, modules = neorg.lib, neorg.log, neorg.modules\n\n---@class core.queries.native.tree_node\n---@field query string[]\n---@field subtree core.queries.native.tree_node[]|nil\n---@field recursive boolean|nil\n\nlocal module = modules.create(\"core.queries.native\")\n\nmodule.setup = function()\n    return {\n        success = true,\n        requires = { \"core.integrations.treesitter\" },\n    }\nend\n\nmodule.examples = {\n    [\"Get the content of all todo_item1 in a norg file\"] = function()\n        local buf = 1 -- The buffer to query informations\n\n        --- @type core.queries.native.tree_node[]\n        local tree = {\n            {\n                query = { \"first\", \"document_content\" },\n                subtree = {\n                    {\n                        query = { \"all\", \"generic_list\" },\n                        recursive = true,\n                        subtree = {\n                            {\n                                query = { \"all\", \"todo_item1\" },\n                            },\n                        },\n                    },\n                },\n            },\n        }\n\n        -- Get a list of { node, buf }\n        local nodes = module.required[\"core.queries.native\"].query_nodes_from_buf(tree, buf)\n        local extracted_nodes = module.required[\"core.queries.native\"].extract_nodes(nodes)\n\n        -- Free the text in memory after reading nodes\n        module.required[\"core.queries.native\"].delete_content(buf)\n\n        print(nodes, extracted_nodes)\n    end,\n}\n\n---@class core.queries.native\nmodule.public = {\n    --- Recursively generates results from a `parent` node, following a `tree` table\n    --- @see First implementation in: https://github.com/danymat/neogen/blob/main/lua/neogen/utilities/nodes.lua\n    ---@param parent userdata\n    ---@param tree core.queries.native.tree_node\n    ---@param results table|nil\n    ---@return table\n    query_from_tree = function(parent, tree, bufnr, results)\n        local res = results or {}\n\n        for _, subtree in pairs(tree) do\n            local matched, how_to_fix = module.private.matching_nodes(parent, subtree, bufnr)\n\n            if type(matched) == \"string\" then\n                log.error(\n                    \"Oh no! There's been an error in the query. It seems that we've received some malformed input at one of the subtrees present in parent node of type\",\n                    parent:type() ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n                )\n                log.error(\"Here's the error message:\", matched)\n\n                if how_to_fix then\n                    log.warn(\"To fix the issue:\", vim.trim(how_to_fix))\n                end\n\n                return ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n            end\n\n            -- We extract matching nodes that doesn't have subtree\n            if not subtree.subtree then\n                for _, v in pairs(matched) do\n                    table.insert(res, { v, bufnr })\n                end\n            else\n                for _, node in pairs(matched) do\n                    local nodes = module.public.query_from_tree(node, subtree.subtree, bufnr, res)\n\n                    if not nodes then\n                        return {}\n                    end\n\n                    res = vim.tbl_extend(\"force\", res, nodes)\n                end\n            end\n        end\n\n        return res\n    end,\n\n    --- Use a `tree` to query all required nodes from a `bufnr`. Returns a list of nodes of type { node, bufnr }\n    ---@param tree core.queries.native.tree_node\n    ---@param bufnr number\n    ---@return table\n    query_nodes_from_buf = function(tree, bufnr)\n        local temp_buf = module.public.get_temp_buf(bufnr)\n        local root_node = module.required[\"core.integrations.treesitter\"].get_document_root(temp_buf)\n        if not root_node then\n            return {}\n        end\n\n        local res = module.public.query_from_tree(root_node, tree, bufnr)\n        return res\n    end,\n\n    --- Extract content from `nodes` of type { node, bufnr }\n    ---@param nodes table\n    ---@param opts table?\n    ---   - opts.all_lines (bool)    if true, will return all lines instead of the first one\n    ---@return table\n    extract_nodes = function(nodes, opts)\n        opts = opts or {}\n        local res = {}\n\n        for _, node in ipairs(nodes) do\n            local temp_buf = module.public.get_temp_buf(node[2])\n            local extracted = vim.split(vim.treesitter.get_node_text(node[1], temp_buf), \"\\n\")\n\n            if opts.all_lines then\n                table.insert(res, extracted)\n            else\n                table.insert(res, extracted[1])\n            end\n        end\n        return res\n    end,\n\n    --- Find the parent `node` that match `node_type`. Returns a node of type { node, bufnr }.\n    --- If `opts.multiple`, returns a table of parent nodes that mached `node_type`\n    --- `node` must be of type { node, bufnr }\n    ---@param node table\n    ---@param node_type string\n    ---@param opts table\n    ---   - opts.multiple (bool):  if true, will return all recursive parent nodes that match `node_type`\n    ---@return table\n    find_parent_node = function(node, node_type, opts)\n        vim.validate({\n            node = { node, \"table\" },\n            node_type = { node_type, \"string\" },\n            opts = { opts, \"table\", true },\n        })\n\n        opts = opts or {}\n        local res = {}\n        local parent = node[1]:parent()\n        while parent do\n            if parent:type() == node_type then\n                table.insert(res, { parent, node[2] })\n                if not opts.multiple then\n                    return res[1]\n                end\n            end\n            parent = parent:parent()\n        end\n        return res\n    end,\n\n    --- Creates an unlisted temp buffer reading from the original bufnr.\n    --- This does prevent triggering norg autocommands\n    ---@param buf number #The bufnr to get text from\n    ---@param opts table? #Custom options\n    ---   - opts.no_force_read boolean? #If true, will not read original buffer if it fails to open\n    ---@return number #The temporary bufnr\n    get_temp_buf = function(buf, opts)\n        opts = opts or {}\n        -- If we don't have any previous private data, get the file text\n        if not module.private.data.temp_bufs[buf] then\n            -- Get the file name from bufnr\n            local uri = vim.uri_from_bufnr(buf)\n            local fname = vim.uri_to_fname(uri)\n\n            -- Open and read all lines in the file\n            local f, err = io.open(fname, \"r\")\n            local lines\n            if not f then\n                log.warn(\"Can't read file \" .. fname)\n                if opts.no_force_read then\n                    log.error(err)\n                    return ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n                end\n                lines = vim.api.nvim_buf_get_lines(buf, 0, -1, false)\n            else\n                lines = f:read(\"*a\") or \"\"\n                lines = vim.split(lines, \"\\n\")\n                if lines[#lines] == \"\" then\n                    --vim.split automatically adds an empty line because the file stops with a newline\n                    table.remove(lines)\n                end\n                f:close()\n            end\n\n            -- Stores the lines in a temp buffer\n            local temp_buf = vim.api.nvim_create_buf(false, true)\n            vim.api.nvim_buf_set_lines(temp_buf, 0, -1, false, lines)\n            vim.api.nvim_buf_attach(temp_buf, false, {\n                on_lines = function()\n                    module.private.data.temp_bufs[buf].changed = true\n                end,\n            })\n            module.private.data.temp_bufs[buf] = { buf = temp_buf, changed = false }\n        end\n\n        return module.private.data.temp_bufs[buf].buf\n    end,\n\n    apply_temp_changes = function(buf)\n        local temp_buf = module.private.data.temp_bufs[buf]\n        if temp_buf and temp_buf.changed then\n            -- Write the lines to original file\n            local lines = vim.api.nvim_buf_get_lines(temp_buf.buf, 0, -1, false)\n            local uri = vim.uri_from_bufnr(buf)\n            local fname = vim.uri_to_fname(uri)\n\n            lib.when(vim.fn.bufloaded(buf) == 1, function()\n                vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines)\n                vim.api.nvim_buf_call(buf, lib.wrap(vim.cmd, \"write!\")) ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n            end, lib.wrap(vim.fn.writefile, lines, fname))\n\n            -- We reset the state as false because we are consistent with the original file\n            temp_buf.changed = false\n        end\n    end,\n\n    --- Deletes the content from data.\n    --- If no buffer is provided, will delete every buffer datas\n    --- @overload fun()\n    ---@param buf number #The content relative to the provided buffer\n    delete_content = function(buf)\n        lib.when(buf, function() ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n            module.private.data.temp_bufs[buf] = nil\n        end, function()\n            module.private.data.temp_bufs = {}\n        end)\n    end,\n}\n\nmodule.private = {\n    data = {\n        -- Must be a table of keys like buffer = string_content\n        temp_bufs = {},\n    },\n\n    --- Returns a list of child nodes (from `parent`) that matches a `tree`\n    ---@param parent userdata\n    ---@param tree core.queries.native.tree_node\n    ---@return table\n    matching_nodes = function(parent, tree, bufnr)\n        local res = {}\n        local where = tree.where ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n        local matched_query, how_to_fix =\n            module.private.matching_query(parent, tree.query, { recursive = tree.recursive })\n\n        if type(matched_query) == \"string\" then\n            return matched_query, how_to_fix ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n        end\n\n        if not where then\n            return matched_query\n        else\n            for _, matched in pairs(matched_query) do\n                local matched_where = module.private.predicate_where(matched, where, { bufnr = bufnr })\n                if matched_where then\n                    table.insert(res, matched)\n                end\n            end\n        end\n\n        return res\n    end,\n\n    --- Get a list of child nodes (from `parent`) that match the provided `query`\n    --- @see First implementation in: https://github.com/danymat/neogen/blob/main/lua/neogen/utilities/nodes.lua\n    ---@param parent userdata\n    ---@param query table\n    ---@param opts table\n    ---   - opts.recursive (bool):      if true will recursively find the matching query\n    ---@return table | any, any ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n    matching_query = function(parent, query, opts)\n        vim.validate({\n            parent = { parent, \"userdata\" },\n            query = { query, \"table\" },\n            opts = { opts, \"table\", true },\n        })\n        opts = opts or {}\n        local res = {}\n\n        if not query then\n            return \"No 'queries' value present in the query object!\",\n                [[\nBe sure to supply a query parameter, one that looks as such:\n{\n    query = { \"all\", \"heading1\" }, -- You can use any node type here\n}\n            ]]\n        end\n\n        if vim.fn.len(query) < 2 then\n            return \"Not enough queries supplied! Expected at least 2 but got \" .. tostring(#query),\n                ([[\nBe sure to supply a second parameter, that being the type of node you would like to operate on.\nYou should change your line to something like:\n{\n    query = { \"%s\", \"heading1\" }\n}\nInstead.\n            ]]):format(query[1] or \"all\")\n        end\n\n        if not vim.tbl_contains({ \"all\", \"first\", \"match\" }, query[1]) then\n            return \"Syntax error: \" .. query[1] .. \" is not a valid node operation.\",\n                ([[\nUse a supported node operation. Neorg currently supports \"all\", \"first\" and \"match\".\nWith that in mind, you can do something like this (for example):\n{\n    query = { \"all\", \"%s\" }\n}\n            ]]):format(query[2])\n        end\n\n        -------------------------\n\n        for node in parent:iter_children() do ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n            if node:type() == query[2] then\n                -- query : { \"first\", \"node_name\"} first child node that match node_name\n                if query[1] == \"first\" then\n                    table.insert(res, node)\n                    break\n                    -- query : { \"match\", \"node_name\", \"test\" } all node_name nodes that match \"test\" content\n                    -- elseif query[1] == \"match\" then\n                    -- TODO Match node content\n                    -- query : { \"all\", \"node_name\" } all child nodes that match node_name\n                elseif query[1] == \"all\" then\n                    table.insert(res, node)\n                end\n            end\n\n            if opts.recursive then\n                local found = module.private.matching_query(node, query, { recursive = true })\n                vim.list_extend(res, found)\n            end\n        end\n\n        return res\n    end,\n\n    --- Checks if `parent` node matches a `where` query and returns a predicate accordingly\n    ---@param parent userdata\n    ---@param where table\n    ---@param opts table\n    ---   - opts.bufnr (number):    used in where[1] == \"child_content\" (in order to get the node's content)\n    ---@return boolean\n    predicate_where = function(parent, where, opts)\n        opts = opts or {}\n\n        if not where or #where == 0 then\n            return true\n        end\n\n        -- Where statements requesting children nodes from parent node\n        for node in parent:iter_children() do ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n            if where[1] == \"child_exists\" then\n                if node:type() == where[2] then\n                    return true\n                end\n            end\n\n            if where[1] == \"child_content\" then\n                local temp_buf = module.public.get_temp_buf(opts.bufnr)\n                if\n                    node:type() == where[2]\n                    and vim.split(vim.treesitter.query.get_node_text(node, temp_buf), \"\\n\")[1] == where[3] ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n                then\n                    return true\n                end\n            end\n        end\n\n        return false\n    end,\n}\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/scanner/module.lua",
    "content": "--[[\n    file: Scanner-Module\n    title: Scanner module for Neorg\n    summary: This module is an implementation of a scanner that can be used anywhere TS can't be used.\n    internal: true\n    ---\nThis module is an implementation of a scanner that can be used anywhere TS can't be used.\nIt is not currently used anywhere, and is small enough to be self-documenting.\n--]]\n\nlocal neorg = require(\"neorg.core\")\nlocal modules = neorg.modules\n\nlocal module = modules.create(\"core.scanner\")\n\n---@class core.scanner\nmodule.public = {\n    initialize_new = function(self, source)\n        self:end_session()\n        self.source = source\n    end,\n\n    end_session = function(self)\n        local tokens = self.tokens\n\n        self.position = 0\n        self.buffer = \"\"\n        self.source = \"\"\n        self.tokens = {}\n\n        return tokens\n    end,\n\n    position = 0,\n    buffer = \"\",\n    source = \"\",\n    tokens = {},\n\n    current = function(self)\n        if self.position == 0 then\n            return nil\n        end\n        return self.source:sub(self.position, self.position)\n    end,\n\n    lookahead = function(self, count)\n        count = count or 1\n\n        if self.position + count > self.source:len() then\n            return nil\n        end\n\n        return self.source:sub(self.position + count, self.position + count)\n    end,\n\n    lookbehind = function(self, count)\n        count = count or 1\n\n        if self.position - count < 0 then\n            return nil\n        end\n\n        return self.source:sub(self.position - count, self.position - count)\n    end,\n\n    backtrack = function(self, amount)\n        self.position = self.position - amount\n    end,\n\n    advance = function(self)\n        self.buffer = self.buffer .. self.source:sub(self.position, self.position)\n        self.position = self.position + 1\n    end,\n\n    skip = function(self)\n        self.position = self.position + 1\n    end,\n\n    mark_end = function(self)\n        if self.buffer:len() ~= 0 then\n            table.insert(self.tokens, self.buffer)\n            self.buffer = \"\"\n        end\n    end,\n\n    halt = function(self, mark_end, continue_till_end)\n        if mark_end then\n            self:mark_end()\n        end\n\n        if continue_till_end then\n            self.buffer = self.source:sub(self.position + 1)\n            self:mark_end()\n        end\n\n        self.position = self.source:len() + 1\n    end,\n}\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/storage/module.lua",
    "content": "--[[\n    File: Storage\n    Title: Store persistent data and query it easily with `core.storage`\n    Summary: Deals with storing persistent data across Neorg sessions.\n    Internal: true\n    ---\n--]]\n\nlocal neorg = require(\"neorg.core\")\nlocal modules = neorg.modules\n\nlocal module = modules.create(\"core.storage\")\n\nmodule.setup = function()\n    return {\n        requires = {\n            \"core.autocommands\",\n        },\n    }\nend\n\nmodule.config.public = {\n    -- Full path to store data (saved in mpack data format)\n    path = vim.fn.stdpath(\"data\") .. \"/neorg.mpack\",\n}\n\nmodule.private = {\n    data = {},\n}\n\n---@class core.storage\nmodule.public = {\n    --- Grabs the data present on disk and overwrites it with the data present in memory\n    sync = function()\n        local file = io.open(module.config.public.path, \"r\")\n\n        if not file then\n            return\n        end\n\n        local content = file:read(\"*a\")\n\n        io.close(file)\n\n        module.private.data = vim.mpack.decode and vim.mpack.decode(content) or vim.mpack.unpack(content)\n    end,\n\n    --- Stores a key-value pair in the storage\n    ---@param key string #The key to index in the storage\n    ---@param data any #The data to store at the specific key\n    store = function(key, data)\n        module.private.data[key] = data\n    end,\n\n    --- Removes a key from storage\n    ---@param key string #The name of the key to remove\n    remove = function(key)\n        module.private.data[key] = nil\n    end,\n\n    --- Retrieves a key from the storage\n    ---@param key string #The name of the key to index\n    ---@return any|table #The data present at the key, or an empty table\n    retrieve = function(key)\n        return module.private.data[key] or {}\n    end,\n\n    --- Flushes the contents in memory to the location specified in the `path` configuration option.\n    flush = function()\n        local file = io.open(module.config.public.path, \"w\")\n\n        if not file then\n            return\n        end\n\n        file:write(vim.mpack.encode and vim.mpack.encode(module.private.data) or vim.mpack.pack(module.private.data))\n\n        io.close(file)\n    end,\n}\n\nmodule.on_event = function(event)\n    -- Synchronize the data in memory with the data on disk after we leave Neovim\n    if event.type == \"core.autocommands.events.vimleavepre\" then\n        module.public.flush()\n    end\nend\n\nmodule.load = function()\n    module.required[\"core.autocommands\"].enable_autocommand(\"VimLeavePre\")\n\n    module.public.sync()\nend\n\nmodule.events.subscribed = {\n    [\"core.autocommands\"] = {\n        vimleavepre = true,\n    },\n}\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/summary/module.lua",
    "content": "--[[\n    file: Summary\n    title: Write notes, not boilerplate.\n    description: The summary module creates links and annotations to all files in a given workspace.\n    summary: Creates links to all files in any workspace.\n    ---\n<!-- TODO: GIF -->\nThe `core.summary` module exposes a single command - `:Neorg generate-workspace-summary`.\n\nWhen executed with the cursor hovering over a heading, `core.summary` will generate, you guessed it,\na summary of the entire workspace, with links to each respective entry in that workspace.\n\nIf arguments are provided then a partial summary is generated containing only categories that\nyou have provided.\nE.g. `:Neorg generate-workspace-summary work todos` would only generate a\nsummary of the categories `work` and `todos`.\n\nThe way the summary is generated relies on the `strategy` configuration option,\nwhich by default consults the document metadata (see also\n[`core.esupports.metagen`](@core.esupports.metagen)) or the first heading title\nas a fallback to build up a tree of categories, titles and descriptions.\n--]]\n\nlocal neorg = require(\"neorg.core\")\nlocal lib, modules, utils = neorg.lib, neorg.modules, neorg.utils\n\nlocal module = modules.create(\"core.summary\")\n\nmodule.setup = function()\n    return {\n        sucess = true,\n        requires = { \"core.integrations.treesitter\" },\n    }\nend\n\nmodule.load = function()\n    modules.await(\"core.neorgcmd\", function(neorgcmd)\n        neorgcmd.add_commands_from_table({\n            [\"generate-workspace-summary\"] = {\n                min_args = 0,\n                condition = \"norg\",\n                name = \"summary.summarize\",\n            },\n        })\n    end)\n    local ts = module.required[\"core.integrations.treesitter\"]\n\n    -- declare query on load so that it's parsed once, on first use\n    local heading_query\n\n    local get_first_heading_title = function(bufnr)\n        local document_root = ts.get_document_root(bufnr)\n        if not document_root then\n            return\n        end\n        if not heading_query then\n            -- allow second level headings, just in case\n            local heading_query_string = [[\n                         [\n                             (heading1\n                                 title: (paragraph_segment) @next-segment\n                             )\n                             (heading2\n                                 title: (paragraph_segment) @next-segment\n                             )\n                         ]\n                     ]]\n            heading_query = utils.ts_parse_query(\"norg\", heading_query_string)\n        end\n        -- search up to 20 lines (a doc could potentially have metadata without metadata.title)\n        local _, heading = heading_query:iter_captures(document_root, bufnr)()\n        if not heading then\n            return nil\n        end\n        local start_line, _ = heading:start()\n        local lines = vim.api.nvim_buf_get_lines(bufnr, start_line, start_line + 1, false)\n        if #lines > 0 then\n            local title = lines[1]:gsub(\"^%s*%*+%s*\", \"\") -- strip out '*' prefix (handle '* title', ' **title', etc)\n            if title ~= \"\" then -- exclude an empty heading like `*` (although the query should have excluded)\n                return title\n            end\n        end\n    end\n\n    -- Return true if catagories_path is or is a subcategory of an entry in included_categories\n    local is_included_category = function(included_categories, category_path)\n        local found_match = false\n        for _, included in ipairs(included_categories) do\n            local included_path = vim.split(included, \".\", { plain = true })\n            for i, path in ipairs(included_path) do\n                if path == category_path[i] and i == #included_path then\n                    found_match = true\n                    break\n                elseif path ~= category_path[i] then\n                    break\n                end\n            end\n        end\n        return found_match\n    end\n\n    -- Insert a categorized record for the given file into the categories table\n    local insert_categorized = function(categories, category_path, norgname, metadata)\n        local leaf_categories = categories\n        for i, path in ipairs(category_path) do\n            local titled_path = lib.title(path)\n            if i == #category_path then\n                -- There are no more sub catergories so insert the record\n                table.insert(leaf_categories[titled_path], {\n                    title = tostring(metadata.title),\n                    norgname = norgname,\n                    description = metadata.description,\n                })\n                break\n            end\n            local sub_categories = vim.defaulttable()\n            if leaf_categories[titled_path] then\n                -- This category already been added so find it's sub_categories table\n                for _, item in ipairs(leaf_categories[titled_path]) do\n                    if item.sub_categories then\n                        leaf_categories = item.sub_categories\n                        goto continue\n                    end\n                end\n            end\n            -- This is a new sub category\n            table.insert(leaf_categories[titled_path], {\n                title = titled_path,\n                sub_categories = sub_categories,\n            })\n            leaf_categories = sub_categories\n            ::continue::\n        end\n    end\n\n    module.config.public.strategy = lib.match(module.config.public.strategy)({\n        default = function()\n            return function(files, ws_root, heading_level, include_categories)\n                local categories = vim.defaulttable()\n\n                if vim.tbl_isempty(include_categories) then\n                    include_categories = nil\n                end\n\n                utils.read_files(files, function(bufnr, filename)\n                    local metadata = ts.get_document_metadata(bufnr)\n\n                    if not metadata then\n                        metadata = {}\n                    end\n\n                    local norgname = filename:match(\"(.+)%.norg$\") -- strip extension for link destinations\n                    if not norgname then\n                        norgname = filename\n                    end\n                    norgname = string.sub(norgname, ws_root:len() + 1)\n\n                    -- normalise categories into a list. Could be vim.NIL, a number, a string or a list ...\n                    if not metadata.categories or metadata.categories == vim.NIL then\n                        metadata.categories = { \"Uncategorised\" }\n                    elseif not vim.islist(metadata.categories) then ---@diagnostic disable-line\n                        metadata.categories = { tostring(metadata.categories) }\n                    end\n\n                    if not metadata.title then\n                        metadata.title = get_first_heading_title(bufnr)\n                        if not metadata.title then\n                            metadata.title = vim.fs.basename(norgname)\n                        end\n                    end\n\n                    if metadata.description == vim.NIL then\n                        metadata.description = nil\n                    end\n\n                    for _, category in ipairs(metadata.categories) do\n                        local category_path = vim.split(category, \".\", { plain = true })\n\n                        if include_categories then\n                            if is_included_category(include_categories, category_path) then\n                                insert_categorized(categories, category_path, norgname, metadata)\n                            end\n                        else\n                            insert_categorized(categories, category_path, norgname, metadata)\n                        end\n                    end\n                end)\n\n                local result = {}\n                local starting_prefix = string.rep(\"*\", heading_level)\n\n                local function add_category(category, data, level)\n                    local result_temp = {}\n                    local sub_cats_temp = {}\n                    local new_prefix = starting_prefix .. string.rep(\"*\", level)\n                    table.insert(result_temp, new_prefix .. \" \" .. category)\n                    for _, datapoint in ipairs(data) do\n                        if datapoint.sub_categories then\n                            for sub_category, sub_data in vim.spairs(datapoint.sub_categories) do\n                                table.insert(sub_cats_temp, add_category(sub_category, sub_data, level + 1))\n                            end\n                        else\n                            table.insert(\n                                result_temp,\n                                table.concat({\n                                    string.rep(\" \", level + 1),\n                                    \" - {:$\",\n                                    datapoint.norgname,\n                                    \":}[\",\n                                    lib.title(datapoint.title),\n                                    \"]\",\n                                })\n                                    .. (\n                                        datapoint.description and (table.concat({ \" - \", datapoint.description })) or \"\"\n                                    )\n                            )\n                        end\n                    end\n                    for _, sub_cat in pairs(sub_cats_temp) do\n                        for _, row in pairs(sub_cat) do\n                            table.insert(result_temp, row)\n                        end\n                    end\n                    return result_temp\n                end\n                for category, data in vim.spairs(categories) do\n                    local temp = add_category(category, data, 0)\n                    for _, row in pairs(temp) do\n                        table.insert(result, row)\n                    end\n                end\n                return result\n            end\n        end,\n        headings = function()\n            return function() end\n        end,\n        by_path = function()\n            return function(files, ws_root, heading_level, include_categories)\n                local categories = vim.defaulttable()\n\n                if vim.tbl_isempty(include_categories) then\n                    include_categories = nil\n                end\n\n                utils.read_files(files, function(bufnr, filename)\n                    local metadata = ts.get_document_metadata(bufnr) or {}\n\n                    local path_tokens = lib.tokenize_path(filename)\n                    local category = path_tokens[#path_tokens - 1] or \"Uncategorised\"\n\n                    local norgname = filename:match(\"(.+)%.norg$\") or filename -- strip extension for link destinations\n                    norgname = string.sub(norgname, ws_root:len() + 1)\n\n                    if not metadata.title then\n                        metadata.title = get_first_heading_title(bufnr) or vim.fs.basename(norgname)\n                    end\n\n                    if metadata.description == vim.NIL then\n                        metadata.description = nil\n                    end\n\n                    if not include_categories or vim.tbl_contains(include_categories, category:lower()) then\n                        table.insert(categories[lib.title(category)], {\n                            title = tostring(metadata.title),\n                            norgname = norgname,\n                            description = metadata.description,\n                        })\n                    end\n                end)\n                local result = {}\n                local prefix = string.rep(\"*\", heading_level)\n\n                for category, data in vim.spairs(categories) do\n                    table.insert(result, prefix .. \" \" .. category)\n\n                    for _, datapoint in ipairs(data) do\n                        table.insert(\n                            result,\n                            table.concat({\n                                string.rep(\" \", heading_level),\n                                \" - {:$\",\n                                datapoint.norgname,\n                                \":}[\",\n                                lib.title(datapoint.title),\n                                \"]\",\n                            })\n                                .. (datapoint.description and (table.concat({ \" - \", datapoint.description })) or \"\")\n                        )\n                    end\n                end\n\n                return result\n            end\n        end,\n    }) or module.config.public.strategy\nend\n\nmodule.config.public = {\n    -- The strategy to use to generate a summary.\n    --\n    -- Possible options are:\n    -- - \"default\" - read the metadata to categorize and annotate files. Files\n    --   without metadata will use the top level heading as the title. If no headings are present, the filename will be used.\n    -- - \"by_path\" - Similar to \"default\" but uses the capitalized name of the folder containing a *.norg file as category.\n    -- - A custom function with the signature:\n    --   `fun(files: PathlibPath[], ws_root: PathlibPath, heading_level: number?, include_categories: string[]?): string[]?`.\n    --   Returning a list of lines that make up the summary\n    strategy = \"default\",\n}\n\n---@class core.summary\nmodule.public = {\n    ---@param buf integer? the buffer to insert the summary to\n    ---@param cursor_pos integer[]? a tuple of row, col of the cursor positon (see nvim_win_get_cursor())\n    ---@param include_categories string[]? table of strings (ignores case) for categories that you wish to include in the summary.\n    -- if excluded then all categories are written into the summary.\n    generate_workspace_summary = function(buf, cursor_pos, include_categories)\n        local ts = module.required[\"core.integrations.treesitter\"]\n\n        local buffer = buf or 0\n        local cursor_position = cursor_pos or vim.api.nvim_win_get_cursor(0)\n\n        local node_at_cursor = ts.get_first_node_on_line(buffer, cursor_position[1] - 1)\n\n        if not node_at_cursor or not node_at_cursor:type():match(\"^heading%d$\") then\n            utils.notify(\n                \"No heading under cursor! Please move your cursor under the heading you'd like to generate the summary under.\"\n            )\n            return\n        end\n        -- heading level of 'node_at_cursor' (summary headings should be one level deeper)\n        local level = tonumber(string.sub(node_at_cursor:type(), -1))\n\n        local dirman = modules.get_module(\"core.dirman\")\n\n        if not dirman then\n            utils.notify(\"`core.dirman` is not loaded! It is required to generate summaries\")\n            return\n        end\n\n        local ws_root = dirman.get_current_workspace()[2]\n        local generated = module.config.public.strategy(\n            dirman.get_norg_files(dirman.get_current_workspace()[1]) or {},\n            ws_root,\n            level + 1,\n            vim.tbl_map(string.lower, include_categories or {})\n        )\n\n        if not generated or vim.tbl_isempty(generated) then\n            utils.notify(\n                \"No summary to generate! Either change the `strategy` option or ensure you have some indexable files in your workspace.\"\n            )\n            return\n        end\n\n        vim.api.nvim_buf_set_lines(buffer, cursor_position[1], cursor_position[1], true, generated)\n    end,\n}\n\nmodule.events.subscribed = {\n    [\"core.neorgcmd\"] = {\n        [\"summary.summarize\"] = true,\n    },\n}\n\nmodule.on_event = function(event)\n    if event.type == \"core.neorgcmd.events.summary.summarize\" then\n        -- Remove `data` key, and take only the numerical keys from event.content\n        -- these numerical keys are the category args passed to the command\n        local include_categories = { unpack(event.content) }\n        module.public.generate_workspace_summary(event.buffer, event.cursor_position, include_categories)\n    end\nend\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/syntax/module.lua",
    "content": "--[[\n    file: Syntax\n    title: Where Treesitter can't Reach\n    description: When a language not supported by Treesitter is found a fallback is made to use vim regex highlighting.\n    summary: Handles interaction for syntax files for code blocks.\n    internal: true\n    ---\nThe `core.syntax` module highlights any `@code` region where there is no treesitter parser present\nto highlight the region.\n\nThis module very closely resembles the [`concealer`](@core.concealer), but some parts have been\nadapted to fit the correct use case.\n\n##### Author's note:\n\nThis module will appear as spaghetti code at first glance. This is intentional.\nIf one needs to edit this module, it is best to talk to me at `katawful` on GitHub.\nAny edit is assumed to break this module.\n--]]\n\nlocal neorg = require(\"neorg.core\")\nlocal modules, utils = neorg.modules, neorg.utils\n\nlocal module = modules.create(\"core.syntax\")\n---@type core.integrations.treesitter\nlocal ts\n\nlocal function schedule(func)\n    vim.schedule(function()\n        if\n            module.private.disable_deferred_updates\n            or (\n                (module.private.debounce_counters[vim.api.nvim_win_get_cursor(0)[1] + 1] or 0)\n                >= module.config.public.performance.max_debounce\n            )\n        then\n            return\n        end\n\n        func()\n    end)\nend\n\nmodule.setup = function()\n    return {\n        success = true,\n        requires = {\n            \"core.autocommands\",\n            \"core.integrations.treesitter\",\n        },\n    }\nend\n\nmodule.private = {\n\n    largest_change_start = -1,\n    largest_change_end = -1,\n\n    last_change = {\n        active = false,\n        line = 0,\n    },\n\n    -- we need to track the buffers in use\n    last_buffer = \"\",\n\n    disable_deferred_updates = false,\n    debounce_counters = {},\n\n    code_block_table = {\n        --[[\n           table is setup like so\n            {\n               buf_name_1 = {loaded_regex = {regex_name = {type = \"type\", range = {start_row1 = end_row1}}}}\n               buf_name_2 = {loaded_regex = {regex_name = {type = \"type\", range = {start_row1 = end_row1}}}}\n            }\n        --]]\n    },\n\n    available_languages = {},\n}\n\n---@class core.syntax\nmodule.public = {\n\n    -- fills module.private.loaded_code_blocks with the list of active code blocks in the buffer\n    -- stores globally apparently\n    check_code_block_type = function(buf, reload, from, to)\n        -- parse the current buffer, and clear out the buffer's loaded code blocks if needed\n        local current_buf = vim.api.nvim_buf_get_name(buf)\n\n        -- load nil table with empty values\n        if module.private.code_block_table[current_buf] == nil then\n            module.private.code_block_table[current_buf] = { loaded_regex = {} }\n        end\n\n        -- recreate table for buffer on buffer change\n        -- reason for existence:\n        --[[\n            user deletes a bunch of code blocks from file, and said code blocks\n            were the only regex blocks of that language. on a full buffer refresh\n            like reentering the buffer, this will get cleared to recreate what languages\n            are loaded. then another function will handle unloading syntax files on next load\n        --]]\n        for key in pairs(module.private.code_block_table) do\n            if current_buf == key and reload == true then\n                for k, _ in pairs(module.private.code_block_table[current_buf].loaded_regex) do\n                    module.public.remove_syntax(\n                        string.format(\"textGroup%s\", string.upper(k)),\n                        string.format(\"textSnip%s\", string.upper(k))\n                    )\n                    module.private.code_block_table[current_buf].loaded_regex[k] = nil\n                end\n            end\n        end\n\n        -- If the tree is valid then attempt to perform the query\n        local root = ts.get_document_root(buf)\n\n        if root then\n            -- get the language node used by the code block\n            local code_lang = utils.ts_parse_query(\n                \"norg\",\n                [[(\n                    (ranged_verbatim_tag (tag_name) @_tagname (tag_parameters) @language)\n                    (#any-of? @_tagname \"code\" \"embed\")\n                )]]\n            )\n\n            -- check for each code block capture in the root with a language parameter\n            -- to build a table of all the languages for a given buffer\n            local compare_table = {} -- a table to compare to what was loaded\n            for id, node in code_lang:iter_captures(root, buf, from or 0, to or -1) do\n                if id == 2 then -- id 2 here refers to the \"language\" tag\n                    -- find the end node of a block so we can grab the row\n                    local end_node = node:next_named_sibling():next_sibling()\n                    -- get the start and ends of the current capture\n                    local start_row = node:range() + 1\n                    local end_row\n\n                    -- don't try to parse a nil value\n                    if end_node == nil then\n                        end_row = start_row + 1\n                    else\n                        end_row = end_node:range() + 1\n                    end\n\n                    local regex_lang = vim.treesitter.get_node_text(node, buf)\n\n                    -- make sure that the language is actually valid\n                    local type_func = function()\n                        return module.private.available_languages[regex_lang].type\n                    end\n                    local ok, type = pcall(type_func)\n\n                    if not ok then\n                        type = \"null\" -- null type will never get parsed like treesitter languages\n                    end\n\n                    -- add language to table\n                    -- if type is empty it means this language has never been found\n                    if module.private.code_block_table[current_buf].loaded_regex[regex_lang] == nil then\n                        module.private.code_block_table[current_buf].loaded_regex[regex_lang] = {\n                            type = type,\n                            range = {},\n                            cluster = \"\",\n                        }\n                    end\n                    -- else just do what we need to do\n                    module.private.code_block_table[current_buf].loaded_regex[regex_lang].range[start_row] = end_row\n                    table.insert(compare_table, regex_lang)\n                end\n            end\n\n            -- compare loaded languages to see if the file actually has the code blocks\n            if from == nil then\n                for lang in pairs(module.private.code_block_table[current_buf].loaded_regex) do\n                    local found_lang = false\n                    for _, matched in pairs(compare_table) do\n                        if matched == lang then\n                            found_lang = true\n                            break\n                        end\n                    end\n                    -- if no lang was matched, means we didn't find a language in our parse\n                    -- remove the syntax include and region\n                    if found_lang == false then\n                        -- delete loaded lang from the table\n                        module.private.code_block_table[current_buf].loaded_regex[lang] = nil\n                        module.public.remove_syntax(\n                            string.format(\"textGroup%s\", string.upper(lang)),\n                            string.format(\"textSnip%s\", string.upper(lang))\n                        )\n                    end\n                end\n            end\n        end\n    end,\n\n    -- load syntax files for regex code blocks\n    trigger_highlight_regex_code_block = function(buf, remove, ignore_buf, from, to)\n        -- scheduling this function seems to break parsing properly\n        -- schedule(function()\n        local current_buf = vim.api.nvim_buf_get_name(buf)\n        -- only parse from the loaded_code_blocks module, not from the file directly\n        if module.private.code_block_table[current_buf] == nil then\n            return\n        end\n        local lang_table = module.private.code_block_table[current_buf].loaded_regex\n        for lang_name, curr_table in pairs(lang_table) do\n            if curr_table.type == \"syntax\" then\n                -- NOTE: the regex fallback code was originally mostly adapted from Vimwiki\n                -- In its current form it has been intensely expanded upon\n                local group = string.format(\"textGroup%s\", string.upper(lang_name))\n                local snip = string.format(\"textSnip%s\", string.upper(lang_name))\n                local start_marker = string.format(\"@code %s\", lang_name)\n                local end_marker = \"@end\"\n                local has_syntax = string.format(\"syntax list @%s\", group)\n\n                -- sync groups when needed\n                if ignore_buf == false and vim.api.nvim_buf_get_name(buf) == module.private.last_buffer then\n                    module.public.sync_regex_code_blocks(buf, lang_name, from, to)\n                end\n\n                -- try removing syntax before doing anything\n                -- fixes hi link groups from not loading on certain updates\n                if remove == true then\n                    module.public.remove_syntax(group, snip)\n                end\n\n                --- @type boolean, string|{ output: string }\n                local ok, result = pcall(vim.api.nvim_exec2, has_syntax, { output = true })\n\n                result = result.output or result\n\n                local count = select(2, result:gsub(\"\\n\", \"\\n\")) -- get length of result from syn list\n                local empty_result = 0\n                -- look to see if the textGroup is actually empty\n                -- clusters don't delete when they're clear\n                for line in result:gmatch(\"([^\\n]*)\\n?\") do\n                    empty_result = string.match(line, \"textGroup%w+%s+cluster=NONE\")\n                    if empty_result == nil then\n                        empty_result = 0\n                    else\n                        empty_result = #empty_result\n                        break\n                    end\n                end\n\n                -- see if the syntax files even exist before we try to call them\n                -- if syn list was an error, or if it was an empty result\n                if\n                    ok == false\n                    or (\n                        ok == true and ((string.sub(result, 1, 1) == (\"N\" or \"V\") and count == 0) or (empty_result > 0))\n                    )\n                then\n                    -- absorb all syntax stuff\n                    -- potentially needs to be expanded upon as bad values come in\n                    local is_keyword = vim.bo[buf].iskeyword\n                    local current_syntax = \"\"\n                    local foldmethod = vim.o.foldmethod\n                    local foldexpr = vim.o.foldexpr\n                    local foldtext = vim.o.foldtext\n                    local foldnestmax = vim.o.foldnestmax\n                    local foldcolumn = vim.o.foldcolumn\n                    local foldenable = vim.o.foldenable\n                    local foldminlines = vim.o.foldminlines\n                    if vim.b.current_syntax ~= \"\" or vim.b.current_syntax ~= nil then\n                        current_syntax = lang_name\n                        vim.b.current_syntax = nil ---@diagnostic disable-line\n                    end\n\n                    -- include the cluster that will put inside the region\n                    -- source using the available languages\n                    for syntax, table in pairs(module.private.available_languages) do\n                        if table.type == \"syntax\" then\n                            if lang_name == syntax then\n                                if empty_result == 0 then\n                                    -- get the file name for the syntax file\n                                    --- @type string|string[]\n                                    local file =\n                                        vim.api.nvim_get_runtime_file(string.format(\"syntax/%s.vim\", syntax), false)\n                                    if file == nil then\n                                        file = vim.api.nvim_get_runtime_file(\n                                            string.format(\"after/syntax/%s.vim\", syntax),\n                                            false\n                                        )\n                                    end\n\n                                    file = file[1]\n\n                                    local command = string.format(\"syntax include @%s %s\", group, file)\n                                    vim.cmd(command)\n\n                                    -- make sure that group has things when needed\n                                    local regex = group .. \"%s+cluster=(.+)\"\n                                    --- @type boolean, string|{ output: string }\n                                    local _, found_cluster = pcall(\n                                        vim.api.nvim_exec2,\n                                        string.format(\"syntax list @%s\", group),\n                                        { output = true }\n                                    )\n\n                                    found_cluster = found_cluster.output or found_cluster\n\n                                    local actual_cluster\n                                    for match in found_cluster:gmatch(regex) do\n                                        actual_cluster = match\n                                    end\n                                    if actual_cluster ~= nil then\n                                        module.private.code_block_table[current_buf].loaded_regex[lang_name].cluster =\n                                            actual_cluster\n                                    end\n                                elseif\n                                    module.private.code_block_table[current_buf].loaded_regex[lang_name].cluster ~= nil\n                                then\n                                    local command = string.format(\n                                        \"silent! syntax cluster %s add=%s\",\n                                        group,\n                                        module.private.code_block_table[current_buf].loaded_regex[lang_name].cluster\n                                    )\n                                    vim.cmd(command)\n                                end\n                            end\n                        end\n                    end\n\n                    -- reset some values after including\n                    vim.bo[buf].iskeyword = is_keyword\n                    vim.b.current_syntax = current_syntax or \"\" ---@diagnostic disable-line\n\n                    has_syntax = string.format(\"syntax list %s\", snip)\n                    --- @type boolean, string|{ output: string }\n                    _, result = pcall(vim.api.nvim_exec2, has_syntax, { output = true })\n                    result = result.output or result\n                    count = select(2, result:gsub(\"\\n\", \"\\n\")) -- get length of result from syn list\n\n                    --[[\n                        if we see \"-\" it means there potentially is already a region for this lang\n                        we must have only 1 line, more lines means there is a region already\n                        see :h syn-list for the format\n                    --]]\n                    if count == 0 or (string.sub(result, 1, 1) == \"-\" and count == 0) then\n                        -- set highlight groups\n                        local regex_fallback_hl = string.format(\n                            [[\n                                syntax region %s\n                                \\ matchgroup=Snip\n                                \\ start=\"%s\" end=\"%s\"\n                                \\ contains=@%s\n                                \\ keepend\n                            ]],\n                            snip,\n                            start_marker,\n                            end_marker,\n                            group\n                        )\n                        vim.cmd(string.format(\"%s\", regex_fallback_hl))\n                        -- sync everything\n                        module.public.sync_regex_code_blocks(buf, lang_name, from, to)\n                    end\n\n                    vim.o.foldmethod = foldmethod\n                    vim.o.foldexpr = foldexpr\n                    vim.o.foldtext = foldtext\n                    vim.o.foldnestmax = foldnestmax\n                    vim.o.foldcolumn = foldcolumn\n                    vim.o.foldenable = foldenable\n                    vim.o.foldminlines = foldminlines\n                end\n\n                vim.b.current_syntax = \"\" ---@diagnostic disable-line\n                module.private.last_buffer = vim.api.nvim_buf_get_name(buf)\n            end\n        end\n        -- end)\n    end,\n\n    -- remove loaded syntax include and snip region\n    remove_syntax = function(group, snip)\n        -- these clears are silent. errors do not matter\n        -- errors are assumed to come from the functions that call this\n        local group_remove = string.format(\"silent! syntax clear @%s\", group)\n        vim.cmd(group_remove)\n\n        local snip_remove = string.format(\"silent! syntax clear %s\", snip)\n        vim.cmd(snip_remove)\n    end,\n\n    -- sync regex code blocks\n    sync_regex_code_blocks = function(buf, regex, from, to)\n        local current_buf = vim.api.nvim_buf_get_name(buf)\n        -- only parse from the loaded_code_blocks module, not from the file directly\n        if module.private.code_block_table[current_buf] == nil then\n            return\n        end\n        local lang_table = module.private.code_block_table[current_buf].loaded_regex\n        for lang_name, curr_table in pairs(lang_table) do\n            -- if we got passed a regex, then we need to only parse the right one\n            if regex ~= nil then\n                if regex ~= lang_name then\n                    goto continue\n                end\n            end\n            if curr_table.type == \"syntax\" then\n                -- sync from code block\n\n                -- for incremental syncing\n                if from ~= nil then\n                    local found_lang = false\n                    for start_row, end_row in pairs(curr_table.range) do\n                        -- see if the text changes we made included a regex code block\n                        if start_row <= from and end_row >= to then\n                            found_lang = true\n                        end\n                    end\n\n                    -- didn't find match from this range of the current language, skip parsing\n                    if found_lang == false then\n                        goto continue\n                    end\n                end\n\n                local snip = string.format(\"textSnip%s\", string.upper(lang_name))\n                local start_marker = string.format(\"@code %s\", lang_name)\n                -- local end_marker = \"@end\"\n                local regex_fallback_hl = string.format(\n                    [[\n                        syntax sync match %s\n                        \\ grouphere %s\n                        \\ \"%s\"\n                    ]],\n                    snip,\n                    snip,\n                    start_marker\n                )\n                vim.cmd(string.format(\"silent! %s\", regex_fallback_hl))\n\n                -- NOTE: this is kept as a just in case\n                -- sync back from end block\n                -- regex_fallback_hl = string.format(\n                --     [[\n                --         syntax sync match %s\n                --         \\ groupthere %s\n                --         \\ \"%s\"\n                --     ]],\n                --     snip,\n                --     snip,\n                --     end_marker\n                -- )\n                -- TODO check groupthere, a slower process\n                -- vim.cmd(string.format(\"silent! %s\", regex_fallback_hl))\n                -- vim.cmd(\"syntax sync maxlines=100\")\n            end\n            ::continue::\n        end\n    end,\n}\n\nmodule.config.public = {\n    -- Performance options for highlighting.\n    --\n    -- These options exhibit the same behaviour as the [`concealer`](@core.concealer)'s.\n    performance = {\n        -- How many lines each \"chunk\" of a file should take up.\n        --\n        -- When the size of the buffer is greater than this value,\n        -- the buffer is then broken up into equal chunks and operations\n        -- are done individually on those chunks.\n        increment = 1250,\n\n        -- How long the syntax module should wait before starting to conceal\n        -- the buffer.\n        timeout = 0,\n\n        -- How long the syntax module should wait before starting to conceal\n        -- a new chunk.\n        interval = 500,\n\n        -- The maximum amount of recalculations that take place at a single time.\n        -- More operations than this count will be dropped.\n        --\n        -- Especially useful when e.g. holding down `x` in a buffer, forcing\n        -- hundreds of recalculations at a time.\n        max_debounce = 5,\n    },\n}\n\nmodule.load = function()\n    -- Enabled the required autocommands\n    -- This is generally any potential redraw event\n    module.required[\"core.autocommands\"].enable_autocommand(\"BufEnter\")\n    module.required[\"core.autocommands\"].enable_autocommand(\"ColorScheme\")\n    module.required[\"core.autocommands\"].enable_autocommand(\"TextChanged\")\n    -- module.required[\"core.autocommands\"].enable_autocommand(\"TextChangedI\")\n\n    -- module.required[\"core.autocommands\"].enable_autocommand(\"InsertEnter\")\n    module.required[\"core.autocommands\"].enable_autocommand(\"InsertLeave\")\n    module.required[\"core.autocommands\"].enable_autocommand(\"VimLeavePre\")\n\n    -- Load available regex languages\n    -- get the available regex files for the current session\n    module.private.available_languages = utils.get_language_list(false)\n\n    ts = module.required[\"core.integrations.treesitter\"] --[[@as core.integrations.treesitter]]\nend\n\nmodule.on_event = function(event)\n    module.private.debounce_counters[event.cursor_position[1] + 1] = module.private.debounce_counters[event.cursor_position[1] + 1]\n        or 0\n\n    local function should_debounce()\n        return module.private.debounce_counters[event.cursor_position[1] + 1]\n            >= module.config.public.performance.max_debounce\n    end\n\n    if event.type == \"core.autocommands.events.bufenter\" and event.content.norg then\n        local buf = event.buffer\n\n        local line_count = vim.api.nvim_buf_line_count(buf)\n\n        if line_count < module.config.public.performance.increment then\n            module.public.check_code_block_type(buf, false)\n            module.public.trigger_highlight_regex_code_block(buf, false, false)\n        else\n            -- This bit of code gets triggered if the line count of the file is bigger than one increment level\n            -- provided by the user.\n            -- In this case, the syntax trigger enters a block mode and splits up the file into chunks. It then goes through each\n            -- chunk at a set interval and applies the syntax that way to reduce load and improve performance.\n\n            -- This points to the current block the user's cursor is in\n            local block_current = math.floor(event.cursor_position[1] / module.config.public.performance.increment)\n\n            local function trigger_syntax_for_block(block)\n                local line_begin = block == 0 and 0 or block * module.config.public.performance.increment - 1\n                local line_end = math.min(\n                    block * module.config.public.performance.increment + module.config.public.performance.increment - 1,\n                    line_count\n                )\n\n                module.public.check_code_block_type(buf, false, line_begin, line_end)\n                module.public.trigger_highlight_regex_code_block(buf, false, false, line_begin, line_end)\n            end\n\n            trigger_syntax_for_block(block_current)\n\n            local block_bottom, block_top = block_current - 1, block_current + 1\n\n            local timer = vim.loop.new_timer()\n\n            timer:start(\n                module.config.public.performance.timeout,\n                module.config.public.performance.interval,\n                vim.schedule_wrap(function()\n                    local block_bottom_valid = block_bottom == 0\n                        or (block_bottom * module.config.public.performance.increment - 1 >= 0)\n                    local block_top_valid = block_top * module.config.public.performance.increment - 1 < line_count\n\n                    if not vim.api.nvim_buf_is_loaded(buf) or (not block_bottom_valid and not block_top_valid) then\n                        timer:stop()\n                        return\n                    end\n\n                    if block_bottom_valid then\n                        trigger_syntax_for_block(block_bottom)\n                        block_bottom = block_bottom - 1\n                    end\n\n                    if block_top_valid then\n                        trigger_syntax_for_block(block_top)\n                        block_top = block_top + 1\n                    end\n                end)\n            )\n        end\n\n        vim.api.nvim_buf_attach(buf, false, {\n            on_lines = function(_, cur_buf, _, start, _end)\n                if buf ~= cur_buf then\n                    return true\n                end\n\n                if should_debounce() then\n                    return\n                end\n\n                module.private.last_change.active = true\n\n                local mode = vim.api.nvim_get_mode().mode\n\n                if mode ~= \"i\" then\n                    module.private.debounce_counters[event.cursor_position[1] + 1] = module.private.debounce_counters[event.cursor_position[1] + 1]\n                        + 1\n\n                    schedule(function()\n                        local new_line_count = vim.api.nvim_buf_line_count(buf)\n\n                        -- Sometimes occurs with one-line undos\n                        if start == _end then\n                            _end = _end + 1\n                        end\n\n                        if new_line_count > line_count then\n                            _end = _end + (new_line_count - line_count - 1)\n                        end\n\n                        line_count = new_line_count\n\n                        vim.schedule(function()\n                            module.private.debounce_counters[event.cursor_position[1] + 1] = module.private.debounce_counters[event.cursor_position[1] + 1]\n                                - 1\n                        end)\n                    end)\n                else\n                    if module.private.largest_change_start == -1 then\n                        module.private.largest_change_start = start\n                    end\n\n                    if module.private.largest_change_end == -1 then\n                        module.private.largest_change_end = _end\n                    end\n\n                    module.private.largest_change_start = start < module.private.largest_change_start and start\n                        or module.private.largest_change_start\n                    module.private.largest_change_end = _end > module.private.largest_change_end and _end\n                        or module.private.largest_change_end\n                end\n            end,\n        })\n    elseif event.type == \"core.autocommands.events.insertleave\" then\n        if should_debounce() then\n            return\n        end\n\n        schedule(function()\n            if not module.private.last_change.active or module.private.largest_change_end == -1 then\n                module.public.check_code_block_type(\n                    event.buffer,\n                    false\n                    -- module.private.last_change.line,\n                    -- module.private.last_change.line + 1\n                )\n                module.public.trigger_highlight_regex_code_block(\n                    event.buffer,\n                    false,\n                    true,\n                    module.private.last_change.line,\n                    module.private.last_change.line + 1\n                )\n            else\n                module.public.check_code_block_type(\n                    event.buffer,\n                    false,\n                    module.private.last_change.line,\n                    module.private.last_change.line + 1\n                )\n                module.public.trigger_highlight_regex_code_block(\n                    event.buffer,\n                    false,\n                    true,\n                    module.private.largest_change_start,\n                    module.private.largest_change_end\n                )\n            end\n\n            module.private.largest_change_start, module.private.largest_change_end = -1, -1\n        end)\n    elseif event.type == \"core.autocommands.events.vimleavepre\" then\n        module.private.disable_deferred_updates = true\n        -- this autocmd is used to fix hi link syntax languages\n        -- TEMP(vhyrro): Temporarily removed for testing - executes code twice when it should not.\n        -- elseif event.type == \"core.autocommands.events.textchanged\" then\n        -- module.private.trigger_highlight_regex_code_block(event.buffer, false, true)\n        -- elseif event.type == \"core.autocommands.events.textchangedi\" then\n        --     module.private.trigger_highlight_regex_code_block(event.buffer, false, true)\n    elseif event.type == \"core.autocommands.events.colorscheme\" then\n        module.public.trigger_highlight_regex_code_block(event.buffer, true, false)\n    end\nend\n\nmodule.events.subscribed = {\n    [\"core.autocommands\"] = {\n        bufenter = true,\n        colorscheme = true,\n        insertleave = true,\n        vimleavepre = true,\n    },\n}\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/tangle/module.lua",
    "content": "--[[\n    file: Tangling\n    title: From Code Blocks to Files\n    description: The `core.tangle` module exports code blocks within a `.norg` file straight to a file of your choice.\n    summary: An Advanced Code Block Exporter.\n    ---\nThe goal of this module is to allow users to spit out the contents of code blocks into\nmany different files. This is the primary component required for a literate configuration in Neorg,\nwhere the configuration is annotated and described in a `.norg` document, and the actual code itself\nis thrown out into a file that can then be normally consumed by e.g. an application.\n\nThe `tangle` module currently provides a single command:\n- `:Neorg tangle current-file` - performs all possible tangling operations on the current file\n\n### Usage Tutorial\n\nBy default, *zero* code blocks are tangled. You must provide where you'd like to tangle each code\nblock manually (global configuration will be discussed later). To do so, add a `#tangle\n<output-file>` tag above the code block you'd wish to export, where <output-file> is relative to the\ncurrent file. For example:\n\n```norg\n#tangle init.lua\n@code lua\nprint(\"Hello World!\")\n@end\n```\nThe above snippet will *only* tangle that single code block to the desired output file: `init.lua`.\n\n> [!WARNING]\n> Due to a bug in the norg treesitter parser, `#tangle ./init.lua` or `#tangle folder/init.lua` will not work\n> As a result, we recommend specifying files destinations in metadata\n\n#### Global Tangling for Single Files\nApart from tangling a single or a set of code blocks, you can declare a global output file in the document's metadata:\n```norg\n@document.meta\ntangle: ./init.lua\n@end\n```\n\nThis will tangle all `lua` code blocks to `init.lua`, *unless* the code block has an explicit `#tangle` tag associated with it, in which case\nthe `#tangle` tag takes precedence.\n\n#### Global Tangling for Multiple Files\nApart from a single filepath, you can provide many in an array:\n```norg\n@document.meta\ntangle: [\n    ./init.lua\n    ./output.hs\n]\n@end\n```\n\nThe above snippet tells the Neorg tangling engine to tangle all `lua` code blocks to `./init.lua` and all `haskell` code blocks to `./output.hs`.\nAs always if any of the code blocks have a `#tangle` tag then that takes precedence.\n\nIf you want to be more verbose, or you're tangling to a file without an extension (perhaps you're\nwriting a shell script that has a shebang) you can also do this:\n```norg\n@document.meta\ntangle: {\n    lua: ./init.lua\n    python: ./output\n}\n@end\n```\n\n#### Ignoring Code Blocks\nSometimes when tangling you may want to omit some code blocks. For this you may use the `#tangle.none` tag:\n```norg\n#tangle.none\n@code lua\nprint(\"I won't be tangled!\")\n@end\n```\n\n#### Global Tangling with Extra Options\nBut wait, it doesn't stop there! You can supply a string to `tangle`, an array to `tangle`, but also an object!\nIt looks like this:\n```norg\n@document.meta\ntangle: {\n    languages: {\n        lua: ./output.lua\n        haskell: my-haskell-file\n    }\n    delimiter: heading\n    scope: all\n}\n@end\n```\n\nThe `language` option determines which filetype should go into which file.\nIt's a simple language-filepath mapping, but it's especially useful when the output file's language type cannot be inferred from the name or shebang.\nIt is also possible to use the name `_` as a catchall to direct output for all files not otherwise listed.\n\nThe `delimiter` option determines how to delimit code blocks that export to the same file.\nThe following variations are allowed:\n\n* `heading` -- Try to determine the filetype of the code block and insert any headings from the original document as a comment in the tangled output.\n  If filetype detection fails, `newline` will be used instead.\n* `file-content` -- Try to determine the filetype of the codeblock and insert the Neorg file content as a delimiter.\n  If filetype detection fails, `none` will be used instead.\n* `newline` -- Use an extra newline between tangled blocks.\n* `none` -- Do not add any delimiter. This implies that the code blocks are inserted into the tangle target as-is.\n\nThe `scope` option is discussed below.\n\n#### Tangling Scopes\nWhat you've seen so far is the tangler operating in `all` mode. This means it captures all code blocks of a certain type unless that code block is tagged\nwith `#tangle.none`. There are two other types: `tagged` and `main`.\n\n##### The `tagged` Scope\nWhen in this mode, the tangler will only tangle code blocks that have been `tagged` with a `#tangle` tag.\nNote that you don't have to always provide a filetype, and that:\n```norg\n#tangle\n@code lua\n@end\n```\nWill use the global output file for that language as defined in the metadata. I.e., if I do:\n```norg\n@document.meta\ntangle: {\n    languages: {\n        lua: ./output.lua\n    }\n    scope: tagged\n}\n@end\n\n@code lua\nprint(\"Hello\")\n@end\n\n#tangle\n@code lua\nprint(\"Sup\")\n@end\n\n#tangle other-file.lua\n@code lua\nprint(\"Ayo\")\n@end\n```\nThe first code block will not be touched, the second code block will be tangled to `./output.lua` and the third code block will be tangled to `other-file.lua`. You\ncan probably see that this system can get expressive pretty quick.\n\n##### The `main` scope\nThis mode is the opposite of the `tagged` one in that it will only tangle code blocks to files that are defined in the document metadata. I.e. in this case:\n```norg\n@document.meta\ntangle: {\n    languages: {\n        lua: ./output.lua\n    }\n    scope: main\n}\n@end\n\n@code lua\nprint(\"Hello\")\n@end\n\n#tangle\n@code lua\nprint(\"Sup\")\n@end\n\n#tangle other-file.lua\n@code lua\nprint(\"Ayo\")\n@end\n```\nThe first code block will be tangled to `./output.lua`, the second code block will also be tangled to `./output.lua` and the third code block will be ignored.\n--]]\n\nlocal neorg = require(\"neorg.core\")\nlocal lib, modules, utils, log = neorg.lib, neorg.modules, neorg.utils, neorg.log\n\nlocal module = modules.create(\"core.tangle\")\nlocal Path = require(\"pathlib\")\n---@type core.dirman.utils\nlocal dirman_utils\n\nmodule.setup = function()\n    return {\n        requires = {\n            \"core.integrations.treesitter\",\n            \"core.dirman.utils\",\n            \"core.neorgcmd\",\n        },\n    }\nend\n\nmodule.load = function()\n    dirman_utils = module.required[\"core.dirman.utils\"]\n    modules.await(\"core.neorgcmd\", function(neorgcmd)\n        neorgcmd.add_commands_from_table({\n            tangle = {\n                args = 1,\n                condition = \"norg\",\n\n                subcommands = {\n                    [\"current-file\"] = {\n                        args = 0,\n                        name = \"core.tangle.current-file\",\n                    },\n                    -- directory = {\n                    --     max_args = 1,\n                    --     name = \"core.tangle.directory\",\n                    -- }\n                },\n            },\n        })\n    end)\n\n    if module.config.public.tangle_on_write then\n        local augroup = vim.api.nvim_create_augroup(\"norg_auto_tangle\", { clear = true })\n        vim.api.nvim_create_autocmd(\"BufWritePost\", {\n            desc = \"Tangle the current file on write\",\n            pattern = \"*.norg\",\n            group = augroup,\n            command = \"Neorg tangle current-file\",\n        })\n    end\nend\n\nlocal function get_comment_string(language)\n    local cur_buf = vim.api.nvim_get_current_buf()\n    local tmp_buf = vim.api.nvim_create_buf(false, true)\n    vim.api.nvim_set_current_buf(tmp_buf)\n    vim.bo.filetype = language\n    local commentstring = vim.bo.commentstring\n    vim.api.nvim_set_current_buf(cur_buf)\n    vim.api.nvim_buf_delete(tmp_buf, { force = true })\n    return commentstring\nend\n\n---@class core.tangle\nmodule.public = {\n    tangle = function(buffer)\n        ---@type core.integrations.treesitter\n        local treesitter = module.required[\"core.integrations.treesitter\"]\n        local parsed_document_metadata = treesitter.get_document_metadata(buffer) or {}\n        local tangle_settings = parsed_document_metadata.tangle or {}\n        local options = {\n            languages = tangle_settings.languages or tangle_settings,\n            scope = tangle_settings.scope or \"all\", -- \"all\" | \"tagged\" | \"main\"\n            delimiter = tangle_settings.delimiter or \"newline\", -- \"newline\" | \"heading\" | \"file-content\" | \"none\"\n        }\n\n        ---@diagnostic disable-next-line\n        if vim.islist(options.languages) then\n            options.filenames_only = options.languages\n            options.languages = {}\n        elseif type(options.languages) == \"string\" then\n            options.languages = { _ = options.languages }\n        end\n\n        local document_root = treesitter.get_document_root(buffer)\n        if not document_root then\n            return\n        end\n        local filename_to_languages = {}\n        local tangles = {\n            -- filename = { block_content }\n        }\n\n        local query_str = lib.match(options.scope)({\n            _ = [[\n                (ranged_verbatim_tag\n                    name: (tag_name) @_name\n                    (#eq? @_name \"code\")\n                    (tag_parameters\n                       .\n                       (tag_param) @_language)) @tag\n            ]],\n            tagged = [[\n                (ranged_verbatim_tag\n                    [(strong_carryover_set\n                        (strong_carryover\n                          name: (tag_name) @_strong_carryover_tag_name\n                          (#eq? @_strong_carryover_tag_name \"tangle\")))\n                     (weak_carryover_set\n                        (weak_carryover\n                          name: (tag_name) @_weak_carryover_tag_name\n                          (#eq? @_weak_carryover_tag_name \"tangle\")))]\n                  name: (tag_name) @_name\n                  (#eq? @_name \"code\")\n                  (tag_parameters\n                    .\n                    (tag_param) @_language)) @tag\n            ]],\n        })\n\n        local query = utils.ts_parse_query(\"norg\", query_str)\n        local previous_headings = {}\n        local commentstrings = {}\n        local file_content_line_start = {}\n        local buf_name = vim.api.nvim_buf_get_name(buffer)\n\n        for id, node in query:iter_captures(document_root, buffer, 0, -1) do\n            local capture = query.captures[id]\n\n            if capture == \"tag\" then\n                local ok, parsed_tag = pcall(treesitter.get_tag_info, node, true)\n                if not ok then\n                    if module.config.public.indent_errors == \"print\" then\n                        print(parsed_tag)\n                    else\n                        log.error(parsed_tag)\n                    end\n                    goto skip_tag\n                end\n\n                if parsed_tag then\n                    local declared_filetype = parsed_tag.parameters[1]\n                    local block_content = parsed_tag.content\n\n                    if parsed_tag.parameters[1] == \"norg\" then\n                        for i, line in ipairs(block_content) do\n                            -- remove escape char\n                            local new_line, _ = line:gsub(\"\\\\(.?)\", \"%1\")\n                            block_content[i] = new_line or \"\"\n                        end\n                    end\n\n                    local file_to_tangle_to\n                    for _, attribute in ipairs(parsed_tag.attributes) do\n                        if attribute.name == \"tangle.none\" then\n                            goto skip_tag\n                        elseif attribute.name == \"tangle\" and attribute.parameters[1] then\n                            if options.scope == \"main\" then\n                                goto skip_tag\n                            end\n                            file_to_tangle_to = table.concat(attribute.parameters)\n                        end\n                    end\n\n                    -- determine tangle file target\n                    if not file_to_tangle_to then\n                        if declared_filetype and options.languages[declared_filetype] then\n                            file_to_tangle_to = options.languages[declared_filetype]\n                        else\n                            if options.filenames_only then\n                                for _, filename in ipairs(options.filenames_only) do\n                                    if\n                                        declared_filetype\n                                        == vim.filetype.match({ filename = filename, contents = block_content }) ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n                                    then\n                                        file_to_tangle_to = filename\n                                        break\n                                    end\n                                end\n                            end\n                            if not file_to_tangle_to then\n                                file_to_tangle_to = options.languages[\"_\"]\n                            end\n                            if declared_filetype then\n                                options.languages[declared_filetype] = file_to_tangle_to\n                            end\n                        end\n                    end\n                    if not file_to_tangle_to then\n                        goto skip_tag\n                    end\n\n                    local path_lib_path = Path.new(file_to_tangle_to)\n                    if path_lib_path:is_relative() then\n                        local buf_path = Path.new(buf_name)\n                        file_to_tangle_to =\n                            tostring(dirman_utils.expand_pathlib(file_to_tangle_to, true, buf_path):resolve())\n                    end\n\n                    local delimiter_content\n                    if options.delimiter == \"heading\" or options.delimiter == \"file-content\" then\n                        local language\n                        if filename_to_languages[file_to_tangle_to] then\n                            language = filename_to_languages[file_to_tangle_to]\n                        else\n                            language = vim.filetype.match({ filename = file_to_tangle_to, contents = block_content }) ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n                            if not language and declared_filetype then\n                                language = vim.filetype.match({\n                                    filename = \"___.\" .. declared_filetype,\n                                    contents = block_content,\n                                })\n                            end\n                            filename_to_languages[file_to_tangle_to] = language\n                        end\n\n                        -- Get commentstring from vim scratch buffer\n                        if language and not commentstrings[language] then\n                            commentstrings[language] = get_comment_string(language)\n                        end\n\n                        -- TODO(vhyrro): Maybe issue warnings to the user when the target\n                        -- commentstring is not found by Neovim?\n                        -- if not language or commentstrings[language] == \"\" then\n                        --     No action\n                        -- end\n                        if options.delimiter == \"heading\" then\n                            -- get current heading\n                            local heading_string\n                            local heading = treesitter.find_parent(node, \"heading%d+\")\n                            if heading and heading:named_child(1) then\n                                local srow, scol, erow, ecol = heading:named_child(1):range()\n                                heading_string = vim.api.nvim_buf_get_text(0, srow, scol, erow, ecol, {})[1]\n                            end\n\n                            -- don't reuse the same header more than once\n                            if heading_string and language and previous_headings[language] ~= heading then\n                                previous_headings[language] = heading\n                                if tangles[file_to_tangle_to] then\n                                    delimiter_content = { \"\", commentstrings[language]:format(heading_string), \"\" }\n                                else\n                                    delimiter_content = { commentstrings[language]:format(heading_string), \"\" }\n                                end\n                            elseif tangles[file_to_tangle_to] then\n                                delimiter_content = { \"\" }\n                            end\n                        elseif options.delimiter == \"file-content\" then\n                            if not file_content_line_start[file_to_tangle_to] then\n                                file_content_line_start[file_to_tangle_to] = 0\n                            end\n                            local start = file_content_line_start[file_to_tangle_to]\n                            local srow, _, erow, _ = node:range()\n                            delimiter_content = vim.api.nvim_buf_get_lines(buffer, start, srow, true)\n                            file_content_line_start[file_to_tangle_to] = erow + 1\n                            for idx, line in ipairs(delimiter_content) do\n                                if line ~= \"\" then\n                                    delimiter_content[idx] = commentstrings[language]:format(line)\n                                end\n                            end\n                        end\n                    elseif options.delimiter == \"newline\" then\n                        if tangles[file_to_tangle_to] then\n                            delimiter_content = { \"\" }\n                        end\n                    end\n\n                    if not tangles[file_to_tangle_to] then\n                        tangles[file_to_tangle_to] = {}\n                    end\n\n                    if delimiter_content then\n                        vim.list_extend(tangles[file_to_tangle_to], delimiter_content)\n                    end\n                    vim.list_extend(tangles[file_to_tangle_to], block_content)\n                end\n            end\n            ::skip_tag::\n        end\n\n        if options.delimiter == \"file-content\" then\n            for filename, start in pairs(file_content_line_start) do\n                local language = filename_to_languages[filename]\n                local delimiter_content = vim.api.nvim_buf_get_lines(buffer, start, -1, true)\n                for idx, line in ipairs(delimiter_content) do\n                    if line ~= \"\" then\n                        delimiter_content[idx] = commentstrings[language]:format(line)\n                    end\n                end\n                vim.list_extend(tangles[filename], delimiter_content)\n            end\n        end\n\n        return tangles\n    end,\n}\n\nmodule.config.public = {\n    -- Notify when there is nothing to tangle (INFO) or when the content is empty (WARN).\n    report_on_empty = true,\n\n    -- Tangle all code blocks in the current norg file on file write.\n    tangle_on_write = false,\n\n    -- When text in a code block is less indented than the block itself, Neorg will not tangle that\n    -- block to a file. Instead it can either print or vim.notify error. By default, vim.notify is\n    -- loud and is more likely to create a press enter message.\n    -- - \"notify\" - Throw a normal looking error\n    -- - \"print\" - print the error\n    indent_errors = \"notify\",\n}\n\nmodule.on_event = function(event)\n    if event.type == \"core.neorgcmd.events.core.tangle.current-file\" then\n        local tangles = module.public.tangle(event.buffer)\n\n        if not tangles or vim.tbl_isempty(tangles) then\n            if module.config.public.report_on_empty then\n                utils.notify(\"Nothing to tangle!\", vim.log.levels.INFO)\n            end\n            return\n        end\n\n        local file_count = vim.tbl_count(tangles)\n        local tangled_count = 0\n\n        for file_str, content in pairs(tangles) do\n            local file = dirman_utils.expand_pathlib(file_str, true)\n            if not file then\n                goto continue\n            end\n            if not file:parent():exists() then\n                file:parent():mkdir(Path.permission(\"rwxr-xr-x\"), true)\n            end\n            file_str = file:tostring()\n            vim.uv.fs_open(file_str, \"w\", 438, function(werr, fd)\n                assert(\n                    not werr and fd,\n                    lib.lazy_string_concat(\"Failed to open file '\", file_str, \"' for tangling: \", werr)\n                )\n\n                local write_content = table.concat(content, \"\\n\")\n                if module.config.public.report_on_empty and write_content:len() == 0 then\n                    vim.schedule(function()\n                        utils.notify(string.format(\"Tangled content for %s is empty.\", file_str), vim.log.levels.WARN)\n                    end)\n                end\n\n                vim.uv.fs_write(fd, write_content, 0, function(werr2)\n                    assert(\n                        not werr2,\n                        lib.lazy_string_concat(\"Failed to write to '\", file_str, \"' for tangling: \", werr2)\n                    )\n                    tangled_count = tangled_count + 1\n                    file_count = file_count - 1\n                    if file_count == 0 then\n                        vim.schedule(\n                            lib.wrap(\n                                utils.notify,\n                                string.format(\n                                    \"Successfully tangled %d file%s!\",\n                                    tangled_count,\n                                    tangled_count == 1 and \"\" or \"s\"\n                                )\n                            )\n                        )\n                    end\n                end)\n                vim.uv.fs_close(fd, function(err)\n                    assert(not err, lib.lazy_string_concat(\"Failed to close file '\", file_str, \"' for tangling: \", err))\n                end)\n            end)\n            ::continue::\n        end\n    end\nend\n\nmodule.events.subscribed = {\n    [\"core.neorgcmd\"] = {\n        [\"core.tangle.current-file\"] = true,\n        [\"core.tangle.directory\"] = true,\n    },\n}\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/tempus/module.lua",
    "content": "--[[\n    file: Tempus\n    title: Hassle-Free Dates\n    summary: Parses and handles dates in Neorg.\n    internal: true\n    ---\n`core.tempus` is an internal module specifically designed\nto handle complex dates. It exposes two functions: `parse_date(string) -> date|string`\nand `to_lua_date(date) -> osdate`.\n\n## Keybinds\n\nThis module exposes the following keybinds (see [`core.keybinds`](@core.keybinds) for instructions on\nmapping them):\n\n- `neorg.tempus.insert-date` - Insert date at cursor position (called from normal mode)\n- `neorg.tempus.insert-date.insert-mode` - Insert date at cursor position (called from insert mode)\n--]]\n\nlocal neorg = require(\"neorg.core\")\nlocal lib, modules, utils = neorg.lib, neorg.modules, neorg.utils\n\nlocal module = modules.create(\"core.tempus\")\n\n-- NOTE: Maybe encapsulate whole date parser in a single PEG grammar?\nlocal _, time_regex = pcall(vim.re.compile, [[{%d%d?} \":\" {%d%d} (\".\" {%d%d?})?]])\n\nlocal timezone_list = {\n    \"ACDT\",\n    \"ACST\",\n    \"ACT\",\n    \"ACWST\",\n    \"ADT\",\n    \"AEDT\",\n    \"AEST\",\n    \"AET\",\n    \"AFT\",\n    \"AKDT\",\n    \"AKST\",\n    \"ALMT\",\n    \"AMST\",\n    \"AMT\",\n    \"ANAT\",\n    \"AQTT\",\n    \"ART\",\n    \"AST\",\n    \"AWST\",\n    \"AZOST\",\n    \"AZOT\",\n    \"AZT\",\n    \"BNT\",\n    \"BIOT\",\n    \"BIT\",\n    \"BOT\",\n    \"BRST\",\n    \"BRT\",\n    \"BST\",\n    \"BTT\",\n    \"CAT\",\n    \"CCT\",\n    \"CDT\",\n    \"CEST\",\n    \"CET\",\n    \"CHADT\",\n    \"CHAST\",\n    \"CHOT\",\n    \"CHOST\",\n    \"CHST\",\n    \"CHUT\",\n    \"CIST\",\n    \"CKT\",\n    \"CLST\",\n    \"CLT\",\n    \"COST\",\n    \"COT\",\n    \"CST\",\n    \"CT\",\n    \"CVT\",\n    \"CWST\",\n    \"CXT\",\n    \"DAVT\",\n    \"DDUT\",\n    \"DFT\",\n    \"EASST\",\n    \"EAST\",\n    \"EAT\",\n    \"ECT\",\n    \"EDT\",\n    \"EEST\",\n    \"EET\",\n    \"EGST\",\n    \"EGT\",\n    \"EST\",\n    \"ET\",\n    \"FET\",\n    \"FJT\",\n    \"FKST\",\n    \"FKT\",\n    \"FNT\",\n    \"GALT\",\n    \"GAMT\",\n    \"GET\",\n    \"GFT\",\n    \"GILT\",\n    \"GIT\",\n    \"GMT\",\n    \"GST\",\n    \"GYT\",\n    \"HDT\",\n    \"HAEC\",\n    \"HST\",\n    \"HKT\",\n    \"HMT\",\n    \"HOVST\",\n    \"HOVT\",\n    \"ICT\",\n    \"IDLW\",\n    \"IDT\",\n    \"IOT\",\n    \"IRDT\",\n    \"IRKT\",\n    \"IRST\",\n    \"IST\",\n    \"JST\",\n    \"KALT\",\n    \"KGT\",\n    \"KOST\",\n    \"KRAT\",\n    \"KST\",\n    \"LHST\",\n    \"LINT\",\n    \"MAGT\",\n    \"MART\",\n    \"MAWT\",\n    \"MDT\",\n    \"MET\",\n    \"MEST\",\n    \"MHT\",\n    \"MIST\",\n    \"MIT\",\n    \"MMT\",\n    \"MSK\",\n    \"MST\",\n    \"MUT\",\n    \"MVT\",\n    \"MYT\",\n    \"NCT\",\n    \"NDT\",\n    \"NFT\",\n    \"NOVT\",\n    \"NPT\",\n    \"NST\",\n    \"NT\",\n    \"NUT\",\n    \"NZDT\",\n    \"NZST\",\n    \"OMST\",\n    \"ORAT\",\n    \"PDT\",\n    \"PET\",\n    \"PETT\",\n    \"PGT\",\n    \"PHOT\",\n    \"PHT\",\n    \"PHST\",\n    \"PKT\",\n    \"PMDT\",\n    \"PMST\",\n    \"PONT\",\n    \"PST\",\n    \"PWT\",\n    \"PYST\",\n    \"PYT\",\n    \"RET\",\n    \"ROTT\",\n    \"SAKT\",\n    \"SAMT\",\n    \"SAST\",\n    \"SBT\",\n    \"SCT\",\n    \"SDT\",\n    \"SGT\",\n    \"SLST\",\n    \"SRET\",\n    \"SRT\",\n    \"SST\",\n    \"SYOT\",\n    \"TAHT\",\n    \"THA\",\n    \"TFT\",\n    \"TJT\",\n    \"TKT\",\n    \"TLT\",\n    \"TMT\",\n    \"TRT\",\n    \"TOT\",\n    \"TVT\",\n    \"ULAST\",\n    \"ULAT\",\n    \"UTC\",\n    \"UYST\",\n    \"UYT\",\n    \"UZT\",\n    \"VET\",\n    \"VLAT\",\n    \"VOLT\",\n    \"VOST\",\n    \"VUT\",\n    \"WAKT\",\n    \"WAST\",\n    \"WAT\",\n    \"WEST\",\n    \"WET\",\n    \"WIB\",\n    \"WIT\",\n    \"WITA\",\n    \"WGST\",\n    \"WGT\",\n    \"WST\",\n    \"YAKT\",\n    \"YEKT\",\n}\n\n---@alias Date {weekday: {name: string, number: number}?, day: number?, month: {name: string, number: number}?, year: number?, timezone: string?, time: {hour: number, minute: number, second: number?}?}\n\n---@class core.tempus\nmodule.public = {\n    --- Converts a parsed date with `parse_date` to a lua date.\n    ---@param parsed_date Date #The date to convert\n    ---@return osdate #A Lua date\n    to_lua_date = function(parsed_date)\n        local now = os.date(\"*t\") --[[@as osdate]]\n        local parsed = os.time(vim.tbl_deep_extend(\"force\", now, {\n            day = parsed_date.day,\n            month = parsed_date.month and parsed_date.month.number or nil,\n            year = parsed_date.year,\n            hour = parsed_date.time and parsed_date.time.hour,\n            min = parsed_date.time and parsed_date.time.minute,\n            sec = parsed_date.time and parsed_date.time.second,\n        }) --[[@as osdateparam]])\n        return os.date(\"*t\", parsed) --[[@as osdate]]\n    end,\n\n    --- Converts a lua `osdate` to a Neorg date.\n    ---@param osdate osdate #The date to convert\n    ---@param include_time boolean? #Whether to include the time (hh::mm.ss) in the output.\n    ---@return Date #The converted date\n    to_date = function(osdate, include_time)\n        -- TODO: Extract into a function to get weekdays (have to hot recalculate every time because the user may change locale\n        local weekdays = {}\n        for i = 1, 7 do\n            table.insert(weekdays, os.date(\"%A\", os.time({ year = 2000, month = 5, day = i })):lower()) ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n        end\n\n        local months = {}\n        for i = 1, 12 do\n            table.insert(months, os.date(\"%B\", os.time({ year = 2000, month = i, day = 1 })):lower()) ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n        end\n\n        -- os.date(\"*t\") returns wday with Sunday as 1, needs to be\n        -- converted to Monday as 1\n        local converted_weekday = lib.number_wrap(osdate.wday - 1, 1, 7)\n\n        return module.private.tostringable_date({\n            weekday = osdate.wday and {\n                number = converted_weekday,\n                name = lib.title(weekdays[converted_weekday]),\n            } or nil,\n            day = osdate.day,\n            month = osdate.month and {\n                number = osdate.month,\n                name = lib.title(months[osdate.month]),\n            } or nil,\n            year = osdate.year,\n            time = osdate.hour and setmetatable({\n                hour = osdate.hour,\n                minute = osdate.min or 0,\n                second = osdate.sec or 0,\n            }, {\n                __tostring = function()\n                    if not include_time then\n                        return \"\"\n                    end\n\n                    return tostring(osdate.hour)\n                        .. \":\"\n                        .. tostring(string.format(\"%02d\", osdate.min))\n                        .. (osdate.sec ~= 0 and (\".\" .. tostring(osdate.sec)) or \"\")\n                end,\n            }) or nil,\n        })\n    end,\n\n    --- Parses a date and returns a table representing the date\n    ---@param input string #The input which should follow the date specification found in the Norg spec.\n    ---@return Date|string #The data extracted from the input or an error message\n    parse_date = function(input)\n        local weekdays = {}\n        for i = 1, 7 do\n            table.insert(weekdays, os.date(\"%A\", os.time({ year = 2000, month = 5, day = i })):lower()) ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n        end\n\n        local months = {}\n        for i = 1, 12 do\n            table.insert(months, os.date(\"%B\", os.time({ year = 2000, month = i, day = 1 })):lower()) ---@diagnostic disable-line -- TODO: type error workaround <pysan3>\n        end\n\n        local output = {}\n\n        for word in vim.gsplit(input, \"%s+\") do\n            if word:len() == 0 then\n                goto continue\n            end\n\n            if word:match(\"^-?%d%d%d%d+$\") then\n                output.year = tonumber(word)\n            elseif word:match(\"^%d+%w*$\") then\n                output.day = tonumber(word:match(\"%d+\"))\n            elseif vim.list_contains(timezone_list, word:upper()) then\n                output.timezone = word:upper()\n            else\n                do\n                    local hour, minute, second = vim.re.match(word, time_regex)\n\n                    if hour and minute then\n                        output.time = setmetatable({\n                            hour = tonumber(hour),\n                            minute = tonumber(minute),\n                            second = second and tonumber(second) or nil,\n                        }, {\n                            __tostring = function()\n                                return word\n                            end,\n                        })\n\n                        goto continue\n                    end\n                end\n\n                do\n                    local valid_months = {}\n\n                    -- Check for month abbreviation\n                    for i, month in ipairs(months) do\n                        if vim.startswith(month, word:lower()) then\n                            valid_months[month] = i\n                        end\n                    end\n\n                    local count = vim.tbl_count(valid_months)\n                    if count > 1 then\n                        return \"Ambiguous month name! Possible interpretations: \"\n                            .. table.concat(vim.tbl_keys(valid_months), \",\")\n                    elseif count == 1 then\n                        local valid_month_name, valid_month_number = unpack(valid_months[1])\n\n                        output.month = {\n                            name = lib.title(valid_month_name),\n                            number = valid_month_number,\n                        }\n\n                        goto continue\n                    end\n                end\n\n                do\n                    word = word:match(\"^([^,]+),?$\")\n\n                    local valid_weekdays = {}\n\n                    -- Check for weekday abbreviation\n                    for i, weekday in ipairs(weekdays) do\n                        if vim.startswith(weekday, word:lower()) then\n                            valid_weekdays[weekday] = i\n                        end\n                    end\n\n                    local count = vim.tbl_count(valid_weekdays)\n                    if count > 1 then\n                        return \"Ambiguous weekday name! Possible interpretations: \"\n                            .. table.concat(vim.tbl_keys(valid_weekdays), \",\")\n                    elseif count == 1 then\n                        local valid_weekday_name, valid_weekday_number = unpack(valid_weekdays[1])\n\n                        output.weekday = {\n                            name = lib.title(valid_weekday_name),\n                            number = valid_weekday_number,\n                        }\n\n                        goto continue\n                    end\n                end\n\n                return \"Unidentified string: `\"\n                    .. word\n                    .. \"` - make sure your locale and language are set correctly if you are using a language other than English!\"\n            end\n\n            ::continue::\n        end\n\n        return module.private.tostringable_date(output)\n    end,\n\n    insert_date = function(insert_mode)\n        local function callback(input)\n            if input == \"\" or not input then\n                return\n            end\n\n            local output\n\n            if type(input) == \"table\" then\n                output = tostring(module.public.to_date(input))\n            else\n                output = module.public.parse_date(input)\n\n                if type(output) == \"string\" then\n                    utils.notify(output, vim.log.levels.ERROR)\n\n                    vim.ui.input({\n                        prompt = \"Date: \",\n                        default = input,\n                    }, callback)\n\n                    return\n                end\n\n                output = tostring(output)\n            end\n\n            vim.api.nvim_put({ \"{@ \" .. output .. \"}\" }, \"c\", false, true)\n\n            if insert_mode then\n                vim.cmd.startinsert()\n            end\n        end\n\n        if modules.is_module_loaded(\"core.ui.calendar\") then\n            vim.cmd.stopinsert()\n            modules.get_module(\"core.ui.calendar\").select_date({ callback = vim.schedule_wrap(callback) })\n        else\n            vim.ui.input({\n                prompt = \"Date: \",\n            }, callback)\n        end\n    end,\n}\n\nmodule.private = {\n    tostringable_date = function(date_table)\n        return setmetatable(date_table, {\n            __tostring = function()\n                local function d(str)\n                    return str and (tostring(str) .. \" \") or \"\"\n                end\n\n                return vim.trim(\n                    d(date_table.weekday and date_table.weekday.name)\n                        .. d(date_table.day)\n                        .. d(date_table.month and date_table.month.name)\n                        .. d(date_table.year and string.format(\"%04d\", date_table.year))\n                        .. d(date_table.time and tostring(date_table.time))\n                        .. d(date_table.timezone)\n                )\n            end,\n        })\n    end,\n}\n\nmodule.load = function()\n    vim.keymap.set(\"\", \"<Plug>(neorg.tempus.insert-date)\", lib.wrap(module.public.insert_date, false))\n    vim.keymap.set(\"i\", \"<Plug>(neorg.tempus.insert-date.insert-mode)\", lib.wrap(module.public.insert_date, true))\nend\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/text-objects/module.lua",
    "content": "--[[\n    file: Norg-Text-Objects\n    title: Navigation, Selection, and Swapping\n    summary: A Neorg module for moving and selecting elements of the document.\n    ---\n\n- Easily move items up and down in the document\n- Provides text objects for headings, tags, and lists\n\n## Usage\n\nUsers can create keybinds for some or all of the different events this module exposes. Those are:\n\n- `core.text-objects.item_up` - Moves the current \"item\" up\n- `core.text-objects.item_down` - same but down\n- `core.text-objects.textobject.heading.outer`\n- `core.text-objects.textobject.heading.inner`\n- `core.text-objects.textobject.tag.inner`\n- `core.text-objects.textobject.tag.outer`\n- `core.text-objects.textobject.list.outer` - around the entire list\n\n_Movable \"items\" include headings, and list items (ordered/unordered/todo)_\n\n### Example\n\nExample keybinds that would go in your Neorg configuration:\n\n```lua\nvim.keymap.set(\"n\", \"<up>\", \"<Plug>(neorg.text-objects.item-up)\", {})\nvim.keymap.set(\"n\", \"<down>\", \"<Plug>(neorg.text-objects.item-down)\", {})\nvim.keymap.set({ \"o\", \"x\" }, \"iH\", \"<Plug>(neorg.text-objects.textobject.heading.inner)\", {})\nvim.keymap.set({ \"o\", \"x\" }, \"aH\", \"<Plug>(neorg.text-objects.textobject.heading.outer)\", {})\n```\n\n--]]\n\nlocal neorg = require(\"neorg.core\")\nlocal log, modules, lib = neorg.log, neorg.modules, neorg.lib\nlocal ts\n\nlocal module = modules.create(\"core.text-objects\")\n\nmodule.setup = function()\n    return {\n        success = true,\n        requires = { \"core.integrations.treesitter\" },\n    }\nend\n\nmodule.load = function()\n    ts = module.required[\"core.integrations.treesitter\"]\n    vim.keymap.set(\"\", \"<Plug>(neorg.text-objects.item-up)\", module.public.move_up)\n    vim.keymap.set(\"\", \"<Plug>(neorg.text-objects.item-down)\", module.public.move_down)\n    vim.keymap.set(\n        \"\",\n        \"<Plug>(neorg.text-objects.textobject.heading.outer)\",\n        lib.wrap(module.public.highlight_node, \"heading.outer\")\n    )\n    vim.keymap.set(\n        \"\",\n        \"<Plug>(neorg.text-objects.textobject.heading.inner)\",\n        lib.wrap(module.public.highlight_node, \"heading.inner\")\n    )\n    vim.keymap.set(\n        \"\",\n        \"<Plug>(neorg.text-objects.textobject.tag.inner)\",\n        lib.wrap(module.public.highlight_node, \"tag.inner\")\n    )\n    vim.keymap.set(\n        \"\",\n        \"<Plug>(neorg.text-objects.textobject.tag.outer)\",\n        lib.wrap(module.public.highlight_node, \"tag.outer\")\n    )\n    vim.keymap.set(\n        \"\",\n        \"<Plug>(neorg.text-objects.textobject.list.outer)\",\n        lib.wrap(module.public.highlight_node, \"lits.outer\")\n    )\nend\n\nmodule.config.public = {\n    moveables = {\n        headings = {\n            \"heading%d\",\n            \"heading%d\",\n        },\n        todo_items = {\n            \"todo_item%d\",\n            {\n                \"todo_item%d\",\n                \"unordered_list%d\",\n            },\n        },\n        unordered_list_elements = {\n            \"unordered_list%d\",\n            {\n                \"todo_item%d\",\n                \"unordered_list%d\",\n            },\n        },\n    },\n}\n\nmodule.private = {\n    get_element_from_cursor = function(node_pattern)\n        local node_at_cursor = vim.treesitter.get_node()\n\n        if\n            not node_at_cursor\n            or not node_at_cursor:parent()\n            or not node_at_cursor:parent():type():match(node_pattern)\n        then\n            log.trace(string.format(\"Could not find element of pattern '%s' under the cursor\", node_pattern))\n            return\n        end\n\n        return node_at_cursor:parent()\n    end,\n\n    move_item_down = function(pattern, expected_sibling_name, buffer)\n        local element = module.private.get_element_from_cursor(pattern)\n\n        if not element then\n            return\n        end\n\n        local next_element = element:next_named_sibling()\n\n        if type(expected_sibling_name) == \"string\" then\n            if next_element and next_element:type():match(expected_sibling_name) then\n                ts.swap_nodes(element, next_element, buffer, true)\n            end\n        else\n            for _, expected in ipairs(expected_sibling_name) do\n                if next_element and next_element:type():match(expected) then\n                    ts.swap_nodes(element, next_element, buffer, true)\n                    return\n                end\n            end\n        end\n    end,\n\n    move_item_up = function(pattern, expected_sibling_name, buffer)\n        local element = module.private.get_element_from_cursor(pattern)\n\n        if not element then\n            return\n        end\n\n        local prev_element = element:prev_named_sibling()\n\n        if type(expected_sibling_name) == \"string\" then\n            if prev_element and prev_element:type():match(expected_sibling_name) then\n                ts.swap_nodes(element, prev_element, buffer, true)\n            end\n        else\n            for _, expected in ipairs(expected_sibling_name) do\n                if prev_element and prev_element:type():match(expected) then\n                    ts.swap_nodes(element, prev_element, buffer, true)\n                    return\n                end\n            end\n        end\n    end,\n}\n\n---@class core.text-objects\nmodule.public = {\n    move_up = function()\n        local config = module.config.public.moveables\n        local buffer = vim.api.nvim_get_current_buf()\n\n        for _, data in pairs(config) do\n            module.private.move_item_up(data[1], data[2], buffer)\n        end\n    end,\n\n    move_down = function()\n        local config = module.config.public.moveables\n        local buffer = vim.api.nvim_get_current_buf()\n\n        for _, data in pairs(config) do\n            module.private.move_item_down(data[1], data[2], buffer)\n        end\n    end,\n\n    highlight_node = function(name)\n        local textobj_lookup = module.config.private.textobjects[name]\n\n        if textobj_lookup then\n            return textobj_lookup(vim.treesitter.get_node())\n        end\n    end,\n}\n\nlocal function find(node, expected_type)\n    while not node:type():match(expected_type) do\n        if not node:parent() or node:type() == \"document\" then\n            return\n        end\n\n        node = node:parent()\n    end\n\n    return node\nend\n\nlocal function find_content(node, expected_type, content_field)\n    local heading = find(node, expected_type)\n\n    if not heading then\n        return\n    end\n\n    local content = heading:field(content_field or \"content\")\n\n    return #content > 0 and content\nend\n\nlocal function highlight_node(node)\n    if not node then\n        return\n    end\n\n    local range = module.required[\"core.integrations.treesitter\"].get_node_range(node)\n    if range.column_end == 0 then\n        range.row_end = range.row_end - 1\n        range.column_end = vim.api.nvim_buf_get_lines(0, range.row_end, range.row_end + 1, true)[1]:len()\n    end\n    if range.column_start == vim.api.nvim_buf_get_lines(0, range.row_start, range.row_start + 1, true)[1]:len() then\n        range.row_start = range.row_start + 1\n        range.column_start = 0\n    end\n\n    -- This method of selection is from ts_utils, it avoids a bug with the nvim_buf_set_mark\n    -- approach\n    local selection_mode = \"v\"\n    local mode = vim.api.nvim_get_mode()\n    if mode.mode ~= selection_mode then\n        vim.cmd.normal({ selection_mode, bang = true })\n    end\n\n    vim.api.nvim_win_set_cursor(0, { range.row_start + 1, range.column_start })\n    vim.cmd.normal({ bang = true, args = { \"o\" } })\n    vim.api.nvim_win_set_cursor(0, { range.row_end + 1, range.column_end })\nend\n\nmodule.config.private = {\n    textobjects = {\n        [\"heading.outer\"] = function(node)\n            return highlight_node(find(node, \"^heading%d+$\"))\n        end,\n        [\"heading.inner\"] = function(node)\n            return highlight_node(find_content(node, \"^heading%d+$\"))\n        end,\n        [\"tag.outer\"] = function(node)\n            return highlight_node(find(node, \"ranged_tag$\"))\n        end,\n        [\"tag.inner\"] = function(node)\n            -- TODO: Fix Treesitter, this is currently buggy\n            return highlight_node(find_content(node, \"ranged_tag$\"))\n        end,\n        [\"list.outer\"] = function(node)\n            return highlight_node(find(node, \"generic_list\"))\n        end,\n    },\n}\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/todo-introspector/module.lua",
    "content": "--[[\n    file: Todo-Introspector\n    title: See Your Progress at a Glance\n    description: The introspector module displays progress for nested tasks.\n    summary: Module for displaying progress of completed subtasks in the virtual line.\n    ---\n\nWhen an item with a TODO status has children with their own TODOs this module enables virtual text in the top level item and displays the\nprogress of the subtasks. By default it displays in the format of `[completed/total] (progress%)`.\n--]]\nlocal neorg = require(\"neorg\")\nlocal modules = neorg.modules\n\nlocal module = modules.create(\"core.todo-introspector\")\n\nmodule.private = {\n    namespace = vim.api.nvim_create_namespace(\"neorg/todo-introspector\"),\n\n    --- List of active buffers\n    buffers = {},\n}\n\n---@class core.todo-introspector\nmodule.config.public = {\n\n    -- Highlight group to display introspector in.\n    --\n    -- Defaults to \"Normal\".\n    highlight_group = \"Normal\",\n\n    -- Which status types to count towards the totol.\n    --\n    -- Defaults to the following: `done`, `pending`, `undone`, `urgent`.\n    counted_statuses = {\n        \"done\",\n        \"pending\",\n        \"undone\",\n        \"urgent\",\n    },\n\n    -- Which status should count towards the completed count (should be a subset of counted_statuses).\n    --\n    -- Defaults to the following: `done`.\n    completed_statuses = {\n        \"done\",\n    },\n\n    -- Callback to format introspector. Takes in two parameters:\n    -- * `completed`: number of completed tasks\n    -- * `total`: number of total counted tasks\n    --\n    -- Should return a string with the format you want to display the introspector in.\n    --\n    -- Defaults to \"[completed/total] (progress%)\"\n    format = function(completed, total)\n        -- stylua: ignore start\n        return string.format(\n            \"[%d/%d] (%d%%)\",\n            completed,\n            total,\n            (total ~= 0 and math.floor((completed / total) * 100) or 0)\n        )\n        -- stylua: ignore end\n    end,\n}\n\nmodule.setup = function()\n    return {\n        requires = { \"core.integrations.treesitter\" },\n    }\nend\n\nmodule.load = function()\n    vim.api.nvim_create_autocmd(\"Filetype\", {\n        pattern = \"norg\",\n        desc = \"Attaches the TODO introspector to any Norg buffer.\",\n        callback = function(ev)\n            local buf = ev.buf\n\n            if module.private.buffers[buf] then\n                return\n            end\n\n            module.private.buffers[buf] = true\n            module.public.attach_introspector(buf)\n        end,\n    })\nend\n\n--- Attaches the introspector to a given Norg buffer.\n--- Errors if the target buffer is not a Norg buffer.\n---@param buffer number #The buffer ID to attach to.\nfunction module.public.attach_introspector(buffer)\n    if not vim.api.nvim_buf_is_valid(buffer) or vim.bo[buffer].filetype ~= \"norg\" then\n        error(string.format(\"Could not attach to buffer %d, buffer is not a norg file!\", buffer))\n    end\n\n    module.required[\"core.integrations.treesitter\"].execute_query(\n        [[\n    (_\n      state: (detached_modifier_extension)) @item\n    ]],\n        function(query, id, node)\n            if query.captures[id] == \"item\" then\n                module.public.perform_introspection(buffer, node)\n            end\n        end,\n        buffer\n    )\n\n    vim.api.nvim_buf_attach(buffer, false, {\n        on_lines = vim.schedule_wrap(function(_, buf, _, first)\n            if not vim.api.nvim_buf_is_valid(buf) then\n                return\n            end\n            -- If we delete the last line of a file `first` will point to a nonexistent line\n            -- For this reason we fall back to the line count (accounting for 0-based indexing)\n            -- whenever a change to the document is made.\n            first = math.min(first, vim.api.nvim_buf_line_count(buf) - 1)\n\n            ---@type TSNode?\n            local node = module.required[\"core.integrations.treesitter\"].get_first_node_on_line(buf, first)\n\n            if not node then\n                return\n            end\n\n            vim.api.nvim_buf_clear_namespace(buffer, module.private.namespace, first + 1, first + 1)\n\n            local function introspect(start_node)\n                local parent = start_node\n\n                while parent do\n                    local child = parent:named_child(1)\n\n                    if child and child:type() == \"detached_modifier_extension\" then\n                        module.public.perform_introspection(buffer, parent)\n                        -- NOTE: do not break here as we want the introspection to propagate all the way up the syntax tree\n                    end\n\n                    parent = parent:parent()\n                end\n            end\n\n            introspect(node)\n\n            local node_above = module.required[\"core.integrations.treesitter\"].get_first_node_on_line(buf, first - 1)\n\n            if node_above then\n                local todo_status = node_above:named_child(1)\n\n                if todo_status and todo_status:type() == \"detached_modifier_extension\" then\n                    introspect(node_above)\n                end\n            end\n        end),\n\n        on_detach = function()\n            vim.api.nvim_buf_clear_namespace(buffer, module.private.namespace, 0, -1)\n            module.private.buffers[buffer] = nil\n        end,\n    })\nend\n\n--- Aggregates TODO item counts from children.\n---@param node TSNode\n---@return number completed Total number of completed tasks\n---@return number total Total number of counted tasks\nfunction module.public.calculate_items(node)\n    local counts = {}\n    for _, status in ipairs(module.config.public.counted_statuses) do\n        counts[status] = 0\n    end\n\n    local total = 0\n\n    -- Go through all the children of the current todo item node and count the amount of \"done\" children\n    for child in node:iter_children() do\n        if child:named_child(1) and child:named_child(1):type() == \"detached_modifier_extension\" then\n            for status in child:named_child(1):iter_children() do\n                if status:type():match(\"^todo_item_\") then\n                    local type = status:type():match(\"^todo_item_(.+)$\")\n\n                    if not counts[type] then\n                        break\n                    end\n\n                    counts[type] = counts[type] + 1\n                    total = total + 1\n                end\n            end\n        end\n    end\n\n    local completed = 0\n    for _, status in ipairs(module.config.public.completed_statuses) do\n        if counts[status] then\n            completed = completed + counts[status]\n        end\n    end\n\n    return completed, total\nend\n\n--- Displays the amount of done items in the form of an extmark.\n---@param buffer number\n---@param node TSNode\nfunction module.public.perform_introspection(buffer, node)\n    local completed, total = module.public.calculate_items(node)\n\n    local line, col = node:start()\n\n    vim.api.nvim_buf_clear_namespace(buffer, module.private.namespace, line, line + 1)\n\n    if total == 0 then\n        return\n    end\n\n    vim.api.nvim_buf_set_extmark(buffer, module.private.namespace, line, col, {\n        virt_text = {\n            {\n                module.config.public.format(completed, total),\n                module.config.public.highlight_group,\n            },\n        },\n        invalidate = true,\n        hl_mode = \"combine\",\n    })\nend\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/ui/calendar/module.lua",
    "content": "--[[\n    file: Calendar\n    title: Frictionless Dates\n    description: The calendar module provides a range of functionality for different date-related tasks.\n    summary: Opens an interactive calendar for date-related tasks.\n---\nThe calendar is most often invoked with the intent of selecting a date, but may\nalso be launched in standalone mode, select date range mode and others.\n\nTo view keybinds and help, press `?` in the calendar view.\n--]]\n\nlocal neorg = require(\"neorg.core\")\nlocal modules = neorg.modules\n\nlocal module = modules.create(\"core.ui.calendar\")\n\nmodule.setup = function()\n    return {\n        requires = {\n            \"core.ui\",\n            \"core.ui.calendar.views.monthly\",\n        },\n    }\nend\n\nmodule.private = {\n\n    modes = {},\n    views = {},\n\n    get_mode = function(name, callback)\n        if module.private.modes[name] ~= nil then\n            local cur_mode = module.private.modes[name](callback)\n            cur_mode.name = name\n            return cur_mode\n        end\n\n        print(\"Error: mode not set or not available\")\n    end,\n\n    get_view = function(name)\n        if module.private.views[name] ~= nil then\n            return module.private.views[name]\n        end\n\n        print(\"Error: view not set or not available\")\n    end,\n\n    extract_ui_info = function(buffer, window)\n        local width = vim.api.nvim_win_get_width(window)\n        local height = vim.api.nvim_win_get_height(window)\n\n        local half_width = math.floor(width / 2)\n        local half_height = math.floor(height / 2)\n\n        return {\n            window = window,\n            buffer = buffer,\n            width = width,\n            height = height,\n            half_width = half_width,\n            half_height = half_height,\n        }\n    end,\n\n    open_window = function(options)\n        local MIN_HEIGHT = 14\n\n        local buffer, window = module.required[\"core.ui\"].create_split(\n            \"calendar-\" .. tostring(os.clock()):gsub(\"%.\", \"-\"),\n            {},\n            options.height or MIN_HEIGHT + (options.padding or 0)\n        )\n\n        vim.api.nvim_create_autocmd({ \"WinClosed\", \"BufDelete\" }, {\n            buffer = buffer,\n\n            callback = function()\n                pcall(vim.api.nvim_win_close, window, true)\n                pcall(vim.api.nvim_buf_delete, buffer, { force = true })\n            end,\n        })\n\n        return buffer, window\n    end,\n}\n\n---@class core.ui.calendar\nmodule.public = {\n    add_mode = function(name, factory)\n        module.private.modes[name] = factory\n    end,\n\n    add_view = function(name, details)\n        module.private.views[name] = details\n    end,\n\n    create_calendar = function(buffer, window, options)\n        local callback_and_close = function(result)\n            if options.callback ~= nil then\n                options.callback(result)\n            end\n\n            pcall(vim.api.nvim_win_close, window, true)\n            pcall(vim.api.nvim_buf_delete, buffer, { force = true })\n        end\n\n        local mode = module.private.get_mode(options.mode, callback_and_close)\n        if mode == nil then\n            return\n        end\n\n        local ui_info = module.private.extract_ui_info(buffer, window)\n\n        local view = module.private.get_view(options.view or \"monthly\")\n\n        view.setup(ui_info, mode, options.date or os.date(\"*t\"), options)\n    end,\n\n    open = function(options)\n        local buffer, window = module.private.open_window(options)\n\n        options.mode = \"standalone\"\n\n        return module.public.create_calendar(buffer, window, options)\n    end,\n\n    select_date = function(options)\n        local buffer, window = module.private.open_window(options)\n\n        options.mode = \"select_date\"\n\n        return module.public.create_calendar(buffer, window, options)\n    end,\n\n    select_date_range = function(options)\n        local buffer, window = module.private.open_window(options)\n\n        options.mode = \"select_range\"\n\n        return module.public.create_calendar(buffer, window, options)\n    end,\n}\n\nmodule.load = function()\n    -- Add default calendar modes\n    module.public.add_mode(\"standalone\", function(_)\n        return {}\n    end)\n\n    module.public.add_mode(\"select_date\", function(callback)\n        return {\n            on_select = function(_, date)\n                if callback then\n                    callback(date)\n                end\n                return false\n            end,\n        }\n    end)\n\n    module.public.add_mode(\"select_range\", function(callback)\n        return {\n            range_start = nil,\n            range_end = nil,\n\n            on_select = function(self, date)\n                if not self.range_start then\n                    self.range_start = date\n                    return true\n                else\n                    if os.time(date) <= os.time(self.range_start) then\n                        print(\"Error: you should choose a date that is after the starting day.\")\n                        return false\n                    end\n\n                    self.range_end = date\n                    callback({ self.range_start, self.range_end })\n                    return false\n                end\n            end,\n\n            get_day_highlight = function(self, date, default_highlight)\n                if self.range_start ~= nil then\n                    if os.time(date) < os.time(self.range_start) then\n                        return \"@comment\"\n                    end\n                end\n                return default_highlight\n            end,\n        }\n    end)\nend\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/ui/calendar/views/monthly/module.lua",
    "content": "local neorg = require(\"neorg.core\")\nlocal lib, log, modules, utils = neorg.lib, neorg.log, neorg.modules, neorg.utils\n\nlocal module = modules.create(\"core.ui.calendar.views.monthly\")\n\nlocal function reformat_time(date)\n    return os.date(\"*t\", os.time(date))\nend\n\nmodule.setup = function()\n    return {\n        requires = {\n            \"core.ui.calendar\",\n            \"core.tempus\",\n        },\n    }\nend\n\nmodule.private = {\n    namespaces = {\n        logical = vim.api.nvim_create_namespace(\"neorg/calendar/logical\"),\n        decorational = vim.api.nvim_create_namespace(\"neorg/calendar/decorational\"),\n    },\n\n    set_extmark = function(ui_info, namespace, row, col, length, virt_text, alignment, extra)\n        if alignment then\n            local text_length = 0\n\n            for _, tuple in ipairs(virt_text) do\n                text_length = text_length + tuple[1]:len()\n            end\n\n            if alignment == \"center\" then\n                col = col + (ui_info.half_width - math.floor(text_length / 2))\n            elseif alignment == \"right\" then\n                col = col + (ui_info.width - text_length)\n            end\n        end\n\n        local default_extra = {\n            virt_text = virt_text,\n            virt_text_pos = \"overlay\",\n        }\n\n        if length then\n            default_extra.end_col = col + length\n        end\n\n        return vim.api.nvim_buf_set_extmark(\n            ui_info.buffer,\n            namespace,\n            row,\n            col,\n            vim.tbl_deep_extend(\"force\", default_extra, extra or {})\n        )\n    end,\n\n    set_decorational_extmark = function(ui_info, row, col, length, virt_text, alignment, extra)\n        return module.private.set_extmark(\n            ui_info,\n            module.private.namespaces.decorational,\n            row,\n            col,\n            length,\n            virt_text,\n            alignment,\n            extra\n        )\n    end,\n\n    set_logical_extmark = function(ui_info, row, col, virt_text, alignment, extra)\n        return module.private.set_extmark(\n            ui_info,\n            module.private.namespaces.logical,\n            row,\n            col,\n            nil,\n            virt_text,\n            alignment,\n            extra\n        )\n    end,\n\n    new_view_instance = function()\n        return {\n            current_mode = {},\n\n            extmarks = {\n                decorational = {\n                    calendar_text = nil,\n                    help_and_custom_input = nil,\n                    current_view = nil,\n                    month_headings = {},\n                    weekday_displays = {},\n                },\n                logical = {\n                    year = nil,\n                    months = {\n                        -- [3] = { [31] = <id> }\n                    },\n                },\n            },\n\n            -- TODO: implemant distance like in render_weekday_banner\n            render_month_banner = function(self, ui_info, date, weekday_banner_extmark_id)\n                local month_name = os.date(\n                    \"%B\",\n                    os.time({\n                        year = date.year,\n                        month = date.month,\n                        day = date.day,\n                    })\n                )\n                ---@cast month_name string\n                local month_length = vim.api.nvim_strwidth(month_name)\n\n                local weekday_banner_id = vim.api.nvim_buf_get_extmark_by_id(\n                    ui_info.buffer,\n                    module.private.namespaces.decorational,\n                    weekday_banner_extmark_id,\n                    {\n                        details = true,\n                    }\n                )\n\n                self.extmarks.decorational.month_headings[weekday_banner_extmark_id] = module.private.set_decorational_extmark(\n                    ui_info,\n                    4,\n                    weekday_banner_id[2]\n                        + math.ceil((weekday_banner_id[3].end_col - weekday_banner_id[2]) / 2)\n                        - math.floor(month_length / 2),\n                    month_length,\n                    { { month_name, \"@text.underline\" } },\n                    nil,\n                    {\n                        id = self.extmarks.decorational.month_headings[weekday_banner_extmark_id],\n                    }\n                )\n            end,\n\n            render_weekday_banner = function(self, ui_info, offset, distance)\n                offset = offset or 0\n                distance = distance or 4\n\n                -- Render the days of the week\n                -- To effectively do this, we grab all the weekdays from a constant time.\n                -- This makes the weekdays retrieved locale dependent (which is what we want).\n                local weekdays = {}\n                local weekdays_string_length = 0\n                for i = 1, 7 do\n                    local weekday = os.date(\"%a\", os.time({ year = 2000, month = 5, day = i }))\n                    ---@cast weekday string\n                    local truncated = utils.truncate_by_cell(weekday, 2)\n                    local truncated_length = vim.api.nvim_strwidth(truncated)\n                    weekdays[#weekdays + 1] = { truncated, \"@text.title\" }\n                    weekdays[#weekdays + 1] = { (\" \"):rep(4 - truncated_length) }\n                    weekdays_string_length = truncated_length -- remember last day's length\n                end\n                weekdays[#weekdays] = nil -- delete last padding\n                weekdays_string_length = weekdays_string_length + 4 * 6\n\n                -- This serves as the index of this week banner extmark inside the extmark table\n                local absolute_offset = offset + (offset < 0 and (-offset * 100) or 0)\n\n                local extmark_position = 0\n\n                -- Calculate offset position only for the previous and following months\n                if offset ~= 0 then\n                    extmark_position = (weekdays_string_length * math.abs(offset)) + (distance * math.abs(offset))\n                end\n\n                -- For previous months, revert the offset\n                if offset < 0 then\n                    extmark_position = -extmark_position\n                end\n\n                local weekday_banner_id = module.private.set_decorational_extmark(\n                    ui_info,\n                    6,\n                    extmark_position,\n                    weekdays_string_length,\n                    weekdays,\n                    \"center\",\n                    {\n                        id = self.extmarks.decorational.weekday_displays[absolute_offset],\n                    }\n                )\n\n                self.extmarks.decorational.weekday_displays[absolute_offset] = weekday_banner_id\n\n                return weekday_banner_id\n            end,\n\n            render_month = function(self, ui_info, target_date, weekday_banner_extmark_id)\n                --> Month rendering routine\n                -- We render the first month at the very center of the screen. Each\n                -- month takes up a static amount of characters.\n\n                -- Render the top text of the month (June, August etc.)\n                -- Render the numbers for weekdays\n                local days_of_month = {\n                    -- [day of month] = <day of week>,\n                }\n\n                local current_date = os.date(\"*t\")\n\n                local month, year = target_date.month, target_date.year\n\n                local days_in_current_month = module.private.get_month_length(month, year)\n\n                for i = 1, days_in_current_month do\n                    days_of_month[i] = tonumber(os.date(\n                        \"%u\",\n                        os.time({\n                            year = year,\n                            month = month,\n                            day = i,\n                        })\n                    ))\n                end\n\n                local beginning_of_weekday_extmark = vim.api.nvim_buf_get_extmark_by_id(\n                    ui_info.buffer,\n                    module.private.namespaces.decorational,\n                    weekday_banner_extmark_id,\n                    {}\n                )\n\n                local render_column = days_of_month[1] - 1\n                local render_row = 1\n\n                self.extmarks.logical.months[month] = self.extmarks.logical.months[month] or {}\n\n                for day_of_month, day_of_week in ipairs(days_of_month) do\n                    local is_current_day = current_date.year == year\n                        and current_date.month == month\n                        and day_of_month == current_date.day\n\n                    local start_row = beginning_of_weekday_extmark[1] + render_row\n                    local start_col = beginning_of_weekday_extmark[2] + (4 * render_column)\n\n                    if is_current_day then\n                        -- TODO: Make this configurable. The user might want the cursor to start\n                        -- on a specific date in a specific month.\n                        -- Just look up the extmark and place the cursor there.\n                        vim.api.nvim_win_set_cursor(ui_info.window, { start_row + 1, start_col })\n                    end\n\n                    local day_highlight = is_current_day and \"@text.todo\" or nil\n\n                    if self.current_mode.get_day_highlight then\n                        day_highlight = self.current_mode:get_day_highlight({\n                            year = year,\n                            month = month,\n                            day = day_of_month,\n                        }, day_highlight)\n                    end\n\n                    self.extmarks.logical.months[month][day_of_month] = vim.api.nvim_buf_set_extmark(\n                        ui_info.buffer,\n                        module.private.namespaces.logical,\n                        start_row,\n                        start_col,\n                        {\n                            virt_text = {\n                                {\n                                    (day_of_month < 10 and \"0\" or \"\") .. tostring(day_of_month),\n                                    day_highlight,\n                                },\n                            },\n                            virt_text_pos = \"overlay\",\n                            id = self.extmarks.logical.months[month][day_of_month],\n                        }\n                    )\n\n                    if day_of_week == 7 then\n                        render_column = 0\n                        render_row = render_row + 1\n                    else\n                        render_column = render_column + 1\n                    end\n                end\n            end,\n\n            render_month_array = function(self, ui_info, date, options)\n                -- Render the first weekday banner in the middle\n                local weekday_banner = self:render_weekday_banner(ui_info, 0, options.distance)\n                self:render_month_banner(ui_info, date, weekday_banner)\n                self:render_month(ui_info, date, weekday_banner)\n\n                local months_to_render = module.private.rendered_months_in_width(ui_info.width, options.distance)\n                months_to_render = math.floor(months_to_render / 2)\n\n                for i = 1, months_to_render do\n                    weekday_banner = self:render_weekday_banner(ui_info, i, options.distance)\n\n                    local positive_target_date = reformat_time({\n                        year = date.year,\n                        month = date.month + i,\n                        day = 1,\n                    })\n\n                    self:render_month_banner(ui_info, positive_target_date, weekday_banner)\n                    self:render_month(ui_info, positive_target_date, weekday_banner)\n\n                    weekday_banner = self:render_weekday_banner(ui_info, i * -1, options.distance)\n\n                    local negative_target_date = reformat_time({\n                        year = date.year,\n                        month = date.month - i,\n                        day = 1,\n                    })\n\n                    self:render_month_banner(ui_info, negative_target_date, weekday_banner)\n                    self:render_month(ui_info, negative_target_date, weekday_banner)\n                end\n            end,\n\n            render_year_tag = function(self, ui_info, year)\n                -- Display the current year (i.e. `< 2022 >`)\n                local extra = nil\n\n                if self.extmarks.logical.year ~= nil then\n                    extra = {\n                        id = self.extmarks.logical.year,\n                    }\n                end\n\n                local extmark = module.private.set_logical_extmark(\n                    ui_info,\n                    2,\n                    0,\n                    { { \"< \", \"Whitespace\" }, { tostring(year), \"@number\" }, { \" >\", \"Whitespace\" } },\n                    \"center\",\n                    extra\n                )\n\n                if self.extmarks.logical.year == nil then\n                    self.extmarks.logical.year = extmark\n                end\n            end,\n\n            render_decorative_text = function(self, ui_info, view)\n                --> Decorational section\n                -- CALENDAR text:\n                self.extmarks.decorational = vim.tbl_deep_extend(\"force\", self.extmarks.decorational, {\n                    calendar_text = module.private.set_decorational_extmark(ui_info, 0, 0, 0, {\n                        { \"CALENDAR\", \"@text.strong\" },\n                    }, \"center\"),\n\n                    -- Help text at the bottom left of the screen\n                    help_and_custom_input = module.private.set_decorational_extmark(\n                        ui_info,\n                        ui_info.height - 1,\n                        0,\n                        0,\n                        {\n                            { \"?\", \"@character\" },\n                            { \" - \" },\n                            { \"help\", \"@text.strong\" },\n                            { \"    \" },\n                            { \"i\", \"@character\" },\n                            { \" - \" },\n                            { \"custom input\", \"@text.strong\" },\n                        }\n                    ),\n\n                    -- The current view (bottom right of the screen)\n                    current_view = module.private.set_decorational_extmark(\n                        ui_info,\n                        ui_info.height - 1,\n                        0,\n                        0,\n                        { { \"[\", \"Whitespace\" }, { view, \"@label\" }, { \"]\", \"Whitespace\" } },\n                        \"right\"\n                    ),\n                })\n            end,\n\n            select_current_day = function(self, ui_info, date)\n                local extmark_id = self.extmarks.logical.months[date.month][date.day]\n\n                local position = vim.api.nvim_buf_get_extmark_by_id(\n                    ui_info.buffer,\n                    module.private.namespaces.logical,\n                    extmark_id,\n                    {}\n                )\n\n                vim.api.nvim_win_set_cursor(ui_info.window, { position[1] + 1, position[2] })\n            end,\n\n            render_view = function(self, ui_info, date, previous_date, options)\n                local is_first_render = (previous_date == nil)\n\n                if is_first_render then\n                    vim.api.nvim_buf_clear_namespace(ui_info.buffer, module.private.namespaces.decorational, 0, -1)\n                    vim.api.nvim_buf_clear_namespace(ui_info.buffer, module.private.namespaces.logical, 0, -1)\n\n                    vim.api.nvim_set_option_value(\"modifiable\", true, { buf = ui_info.buffer })\n\n                    module.private.fill_buffer(ui_info)\n                    self:render_decorative_text(ui_info, module.public.view_name:upper())\n                    self:render_year_tag(ui_info, date.year)\n                    self:render_month_array(ui_info, date, options)\n                    self:select_current_day(ui_info, date)\n\n                    vim.api.nvim_set_option_value(\"modifiable\", false, { buf = ui_info.buffer })\n                    vim.api.nvim_set_option_value(\"winfixbuf\", true, { win = ui_info.window })\n\n                    return\n                end\n\n                local year_changed = (date.year ~= previous_date.year)\n                local month_changed = (date.month ~= previous_date.month)\n                local day_changed = (date.day ~= previous_date.day)\n\n                if year_changed then\n                    self:render_year_tag(ui_info, date.year)\n                end\n\n                if year_changed or month_changed then\n                    self:render_month_array(ui_info, date, options)\n                    self:clear_extmarks(ui_info, date, options)\n                end\n\n                if year_changed or month_changed or day_changed then\n                    self:select_current_day(ui_info, date)\n                end\n            end,\n\n            clear_extmarks = function(self, ui_info, current_date, options)\n                local cur_month = current_date.month\n\n                local rendered_months_offset =\n                    math.floor(module.private.rendered_months_in_width(ui_info.width, options.distance) / 2)\n\n                -- Mimics ternary operator to be concise\n                local month_min = cur_month - rendered_months_offset\n                month_min = month_min <= 0 and (12 + month_min) or month_min\n\n                local month_max = cur_month + rendered_months_offset\n                month_max = month_max > 12 and (month_max - 12) or month_max\n\n                local clear_extmarks_for_month = function(month)\n                    for _, extmark_id in ipairs(self.extmarks.logical.months[month]) do\n                        vim.api.nvim_buf_del_extmark(ui_info.buffer, module.private.namespaces.logical, extmark_id)\n                    end\n\n                    self.extmarks.logical.months[month] = nil\n                end\n\n                for month, _ in pairs(self.extmarks.logical.months) do\n                    -- Check if the month is outside the current view range\n                    -- considering the month wrapping after 12\n                    if month_min < month_max then\n                        if month_min > month or month > month_max then\n                            clear_extmarks_for_month(month)\n                        end\n                    elseif month_min > month_max then\n                        if month_max < month and month < month_min then\n                            clear_extmarks_for_month(month)\n                        end\n                    elseif month_min == month_max then\n                        if month ~= cur_month then\n                            clear_extmarks_for_month(month)\n                        end\n                    end\n                end\n            end,\n        }\n    end,\n\n    fill_buffer = function(ui_info)\n        -- There are many steps to render a calendar.\n        -- The first step is to fill the entire buffer with spaces. This lets\n        -- us place extmarks at any position in the document. Won't be used for\n        -- the meaty stuff, but will come in handy for rendering decorational\n        -- elements.\n        local fill = {}\n        local filler = string.rep(\" \", ui_info.width)\n\n        for i = 1, ui_info.height do\n            fill[i] = filler\n        end\n\n        vim.api.nvim_buf_set_lines(ui_info.buffer, 0, -1, true, fill)\n    end,\n\n    --- get the number of days in the month, months are wrapped (ie, month 13 <==> month 1)\n    get_month_length = function(month, year)\n        return ({\n            31,\n            (module.private.is_leap_year(year)) and 29 or 28,\n            31,\n            30,\n            31,\n            30,\n            31,\n            31,\n            30,\n            31,\n            30,\n            31,\n        })[lib.number_wrap(month, 1, 12)]\n    end,\n\n    is_leap_year = function(year)\n        if year % 4 ~= 0 then\n            return false\n        end\n\n        -- Years disible by 100 are leap years only if also divisible by 400\n        if year % 100 == 0 and year % 400 ~= 0 then\n            return false\n        end\n\n        return true\n    end,\n\n    rendered_months_in_width = function(width, distance)\n        local rendered_month_width = 26\n        local months = math.floor(width / (rendered_month_width + distance))\n\n        -- Do not show more than one year\n        if months > 12 then\n            months = 12\n        end\n\n        if months % 2 == 0 then\n            return months - 1\n        end\n        return months\n    end,\n\n    display_help = function(lines)\n        local width, height = 44, 32\n        local buffer = vim.api.nvim_create_buf(false, true)\n        local window = vim.api.nvim_open_win(buffer, true, {\n            style = \"minimal\",\n            border = \"rounded\",\n            title = \" Calendar \",\n            title_pos = \"center\",\n            row = (vim.o.lines / 2) - height / 2,\n            col = (vim.o.columns / 2) - width / 2,\n            width = width,\n            height = height,\n            relative = \"editor\",\n            noautocmd = true,\n        })\n        vim.api.nvim_set_option_value(\"winfixbuf\", true, { win = window })\n\n        local function quit()\n            vim.api.nvim_win_close(window, true)\n            pcall(vim.api.nvim_buf_delete, buffer, { force = true })\n        end\n\n        vim.keymap.set(\"n\", \"q\", quit, { buffer = buffer })\n\n        vim.api.nvim_create_autocmd({ \"BufLeave\", \"WinLeave\" }, {\n            buffer = buffer,\n            callback = quit,\n        })\n\n        local namespace = vim.api.nvim_create_namespace(\"neorg/calendar-help\")\n        vim.api.nvim_set_option_value(\"modifiable\", false, { buf = buffer })\n\n        vim.api.nvim_buf_set_extmark(buffer, namespace, 0, 0, {\n            virt_lines = lines,\n        })\n    end,\n}\n\n---@class core.ui.calendar.views.monthly\nmodule.public = {\n\n    view_name = \"monthly\",\n\n    setup = function(ui_info, mode, date, options)\n        options.distance = options.distance or 4\n\n        local view = module.private.new_view_instance()\n\n        view.current_mode = mode\n\n        view:render_view(ui_info, date, nil, options)\n\n        do\n            vim.keymap.set(\"n\", \"q\", function()\n                vim.api.nvim_buf_delete(ui_info.buffer, { force = true })\n            end, { buffer = ui_info.buffer })\n\n            -- TODO: Make cursor wrapping behaviour configurable\n            vim.keymap.set(\"n\", \"l\", function()\n                local new_date = reformat_time({\n                    year = date.year,\n                    month = date.month,\n                    day = date.day + 1 * vim.v.count1,\n                })\n                view:render_view(ui_info, new_date, date, options)\n                date = new_date\n            end, { buffer = ui_info.buffer })\n\n            vim.keymap.set(\"n\", \"h\", function()\n                local new_date = reformat_time({\n                    year = date.year,\n                    month = date.month,\n                    day = date.day - 1 * vim.v.count1,\n                })\n                view:render_view(ui_info, new_date, date, options)\n                date = new_date\n            end, { buffer = ui_info.buffer })\n\n            vim.keymap.set(\"n\", \"j\", function()\n                local new_date = reformat_time({\n                    year = date.year,\n                    month = date.month,\n                    day = date.day + 7 * vim.v.count1,\n                })\n                view:render_view(ui_info, new_date, date, options)\n                date = new_date\n            end, { buffer = ui_info.buffer })\n\n            vim.keymap.set(\"n\", \"k\", function()\n                local new_date = reformat_time({\n                    year = date.year,\n                    month = date.month,\n                    day = date.day - 7 * vim.v.count1,\n                })\n                view:render_view(ui_info, new_date, date, options)\n                date = new_date\n            end, { buffer = ui_info.buffer })\n\n            vim.keymap.set(\"n\", \"<cr>\", function()\n                local should_redraw = false\n\n                if view.current_mode.on_select ~= nil then\n                    should_redraw = view.current_mode:on_select(date)\n                end\n\n                if should_redraw then\n                    view:render_view(ui_info, date, nil, options)\n                end\n            end, { buffer = ui_info.buffer })\n\n            vim.keymap.set(\"n\", \"L\", function()\n                local new_date = reformat_time({\n                    year = date.year,\n                    month = date.month + vim.v.count1,\n                    day = date.day,\n                })\n                view:render_view(ui_info, new_date, date, options)\n                date = new_date\n            end, { buffer = ui_info.buffer })\n\n            vim.keymap.set(\"n\", \"H\", function()\n                local new_date = reformat_time({\n                    year = date.year,\n                    month = date.month - vim.v.count1,\n                    day = date.day,\n                })\n                view:render_view(ui_info, new_date, date, options)\n                date = new_date\n            end, { buffer = ui_info.buffer })\n\n            vim.keymap.set(\"n\", \"m\", function()\n                local new_date = reformat_time({\n                    year = date.year,\n                    month = date.month + vim.v.count1,\n                    day = 1,\n                })\n                view:render_view(ui_info, new_date, date, options)\n                date = new_date\n            end, { buffer = ui_info.buffer })\n\n            vim.keymap.set(\"n\", \"M\", function()\n                if date.day > 1 then\n                    date.month = date.month + 1\n                end\n                local new_date = reformat_time({\n                    year = date.year,\n                    month = date.month - vim.v.count1,\n                    day = 1,\n                })\n                view:render_view(ui_info, new_date, date, options)\n                date = new_date\n            end, { buffer = ui_info.buffer })\n\n            vim.keymap.set(\"n\", \"y\", function()\n                local new_date = reformat_time({\n                    year = date.year + vim.v.count1,\n                    month = date.month,\n                    day = date.day,\n                })\n                view:render_view(ui_info, new_date, date, options)\n                date = new_date\n            end, { buffer = ui_info.buffer })\n\n            vim.keymap.set(\"n\", \"Y\", function()\n                local new_date = reformat_time({\n                    year = date.year - vim.v.count1,\n                    month = date.month,\n                    day = date.day,\n                })\n                view:render_view(ui_info, new_date, date, options)\n                date = new_date\n            end, { buffer = ui_info.buffer })\n\n            vim.keymap.set(\"n\", \"$\", function()\n                local new_day = date.day - (lib.number_wrap(date.wday - 1, 1, 7) - 1) + 6\n                local new_date = reformat_time({\n                    year = date.year,\n                    month = date.month,\n                    day = new_day,\n                })\n                view:render_view(ui_info, new_date, date, options)\n                date = new_date\n            end, { buffer = ui_info.buffer })\n\n            local start_of_week = function()\n                local new_day = date.day - (lib.number_wrap(date.wday - 1, 1, 7) - 1)\n                local new_date = reformat_time({\n                    year = date.year,\n                    month = date.month,\n                    day = new_day,\n                })\n                view:render_view(ui_info, new_date, date, options)\n                date = new_date\n            end\n\n            vim.keymap.set(\"n\", \"0\", start_of_week, { buffer = ui_info.buffer })\n            vim.keymap.set(\"n\", \"_\", start_of_week, { buffer = ui_info.buffer })\n\n            vim.keymap.set(\"n\", \"t\", function()\n                local new_date = os.date(\"*t\")\n\n                view:render_view(ui_info, new_date, date, options)\n                date = new_date\n            end, { buffer = ui_info.buffer })\n\n            vim.keymap.set(\"n\", \"e\", function()\n                local end_of_current_month = module.private.get_month_length(date.month, date.year)\n                if end_of_current_month > date.day then\n                    date.month = date.month - 1\n                end\n                local new_date = reformat_time({\n                    year = date.year,\n                    month = date.month + vim.v.count1,\n                    day = module.private.get_month_length(date.month + vim.v.count1, date.year),\n                })\n                view:render_view(ui_info, new_date, date, options)\n                date = new_date\n            end, { buffer = ui_info.buffer })\n\n            vim.keymap.set(\"n\", \"E\", function()\n                local new_date = reformat_time({\n                    year = date.year,\n                    month = date.month - vim.v.count1,\n                    day = module.private.get_month_length(date.month - vim.v.count1, date.year),\n                })\n                view:render_view(ui_info, new_date, date, options)\n                date = new_date\n            end, { buffer = ui_info.buffer })\n\n            vim.keymap.set(\"n\", \"w\", function()\n                local new_date = reformat_time({\n                    year = date.year,\n                    month = date.month,\n                    day = date.day + 7 * vim.v.count1,\n                })\n                new_date.day = new_date.day - (lib.number_wrap(new_date.wday - 1, 1, 7) - 1)\n                view:render_view(ui_info, new_date, date, options)\n                date = new_date\n            end, { buffer = ui_info.buffer })\n\n            vim.keymap.set(\"n\", \"W\", function()\n                local new_date = reformat_time({\n                    year = date.year,\n                    month = date.month,\n                    day = date.day - 7 * vim.v.count1,\n                })\n                new_date.day = new_date.day - (lib.number_wrap(new_date.wday - 1, 1, 7) - 1)\n                view:render_view(ui_info, new_date, date, options)\n                date = new_date\n            end, { buffer = ui_info.buffer })\n\n            local months = {}\n            for i = 1, 12 do\n                table.insert(\n                    months,\n                    (os.date(\"%B\", os.time({ year = 2000, month = i, day = 1 })) --[[@as string]]):lower()\n                )\n            end\n\n            -- store the last `;` repeatable search\n            local last_semi_jump = nil\n            -- flag to set when we're using `;` so it doesn't cycle\n            local skip_next = false\n\n            vim.keymap.set(\"n\", \";\", function()\n                if last_semi_jump then\n                    vim.api.nvim_feedkeys(last_semi_jump, \"m\", false)\n                end\n            end, { buffer = ui_info.buffer })\n\n            vim.keymap.set(\"n\", \",\", function()\n                if last_semi_jump then\n                    local action = string.sub(last_semi_jump, 1, 1)\n                    local subject = string.sub(last_semi_jump, 2)\n                    local new_keys\n                    if string.upper(action) == action then\n                        new_keys = action:lower() .. subject\n                    else\n                        new_keys = action:upper() .. subject\n                    end\n                    vim.api.nvim_feedkeys(new_keys, \"m\", false)\n\n                    skip_next = true\n                end\n            end, { buffer = ui_info.buffer })\n\n            vim.keymap.set(\"n\", \"f\", function()\n                local char = vim.fn.getcharstr()\n\n                for i = date.month + 1, date.month + 12 do\n                    local m = lib.number_wrap(i, 1, 12)\n                    if months[m]:match(\"^\" .. char) then\n                        if not skip_next then\n                            last_semi_jump = \"f\" .. char\n                        else\n                            skip_next = false\n                        end\n\n                        local new_date = reformat_time({\n                            year = date.year,\n                            month = m,\n                            day = date.day,\n                        })\n                        view:render_view(ui_info, new_date, date, options)\n                        date = new_date\n                        break\n                    end\n                end\n            end, { buffer = ui_info.buffer })\n\n            vim.keymap.set(\"n\", \"F\", function()\n                local char = vim.fn.getcharstr()\n\n                for i = date.month + 11, date.month, -1 do\n                    local m = lib.number_wrap(i, 1, 12)\n                    if months[m]:match(\"^\" .. char) then\n                        if not skip_next then\n                            last_semi_jump = \"F\" .. char\n                        else\n                            skip_next = false\n                        end\n                        local new_date = reformat_time({\n                            year = date.year,\n                            month = m,\n                            day = date.day,\n                        })\n                        view:render_view(ui_info, new_date, date, options)\n                        date = new_date\n                        break\n                    end\n                end\n            end, { buffer = ui_info.buffer })\n\n            vim.keymap.set(\"n\", \"g\", function()\n                local day = math.min(vim.v.count1, module.private.get_month_length(date.month, date.year))\n\n                local new_date = reformat_time({\n                    year = date.year,\n                    month = date.month,\n                    day = day,\n                })\n                view:render_view(ui_info, new_date, date, options)\n                date = new_date\n            end, { buffer = ui_info.buffer, nowait = true })\n\n            vim.keymap.set(\"n\", \"G\", function()\n                local new_date = reformat_time({\n                    year = date.year,\n                    month = date.month,\n                    day = module.private.get_month_length(date.month, date.year),\n                })\n                view:render_view(ui_info, new_date, date, options)\n                date = new_date\n            end, { buffer = ui_info.buffer })\n\n            vim.keymap.set(\"n\", \"d\", function()\n                local n = vim.v.count1\n                local weekday = math.min(n, 7)\n                local new_date = reformat_time({\n                    year = date.year,\n                    month = date.month,\n                    day = date.day + (weekday - lib.number_wrap(date.wday - 1, 1, 7)),\n                })\n                view:render_view(ui_info, new_date, date, options)\n                date = new_date\n            end, { buffer = ui_info.buffer, nowait = true })\n\n            vim.keymap.set(\n                \"n\",\n                \"?\",\n                lib.wrap(module.private.display_help, {\n                    {\n                        { \"q\", \"@namespace\" },\n                        { \" - \" },\n                        { \"close this window\", \"@text.strong\" },\n                    },\n                    {},\n                    {\n                        { \"<CR>\", \"@namespace\" },\n                        { \" - \" },\n                        { \"select date\", \"@text.strong\" },\n                    },\n                    {},\n                    {\n                        { \"--- Basic Movement ---\", \"@text.title\" },\n                    },\n                    {},\n                    {\n                        { \"l/h\", \"@namespace\" },\n                        { \" - \" },\n                        { \"next/previous day\", \"@text.strong\" },\n                    },\n                    {\n                        { \"j/k\", \"@namespace\" },\n                        { \" - \" },\n                        { \"next/previous week\", \"@text.strong\" },\n                    },\n                    {\n                        { \"w/W\", \"@namespace\" },\n                        { \" - \" },\n                        { \"start of next/this or previous week\", \"@text.strong\" },\n                    },\n                    {\n                        { \"t\", \"@namespace\" },\n                        { \" - \" },\n                        { \"today\", \"@text.strong\" },\n                    },\n                    {\n                        { \"d\", \"@namespace\" },\n                        { \"n\" },\n                        { \" - \" },\n                        { \"weekday \", \"@text.strong\" },\n                        { \"n\" },\n                        { \" (1 = monday)\", \"@text.strong\" },\n                    },\n                    {},\n                    {\n                        { \"--- Moving Between Months ---\", \"@text.title\" },\n                    },\n                    {},\n                    {\n                        { \"L/H\", \"@namespace\" },\n                        { \" - \" },\n                        { \"next/previous month (same day)\", \"@text.strong\" },\n                    },\n                    {\n                        { \"m/M\", \"@namespace\" },\n                        { \" - \" },\n                        { \"1st of next/this or previous month\", \"@text.strong\" },\n                    },\n                    {\n                        { \"f\", \"@namespace\" },\n                        { \"x\" },\n                        { \"/F\", \"@namespace\" },\n                        { \"x\" },\n                        { \" - \" },\n                        { \"next/previous month starting with \", \"@text.strong\" },\n                        { \"x\" },\n                    },\n                    {},\n                    {\n                        { \"--- Moving Between Years ---\", \"@text.title\" },\n                    },\n                    {},\n                    {\n                        { \"y/Y\", \"@namespace\" },\n                        { \" - \" },\n                        { \"next/previous year (same day)\", \"@text.strong\" },\n                    },\n                    {\n                        { \"gy\", \"@namespace\" },\n                        { \" - \" },\n                        { \"start of the current year\", \"@text.strong\" },\n                    },\n                    {\n                        { \"c/C\", \"@namespace\" },\n                        { \" - \" },\n                        { \"next/this or previous century\", \"@text.strong\" },\n                    },\n                    {\n                        { \"g/G\", \"@namespace\" },\n                        { \" - \" },\n                        { \"start/end of month\", \"@text.strong\" },\n                    },\n                    {\n                        { \"      \" },\n                        { \"<n>g takes you to <n> day of the month\", \"@text.strong\" },\n                    },\n                    {},\n                    {\n                        { \"--- Additional Info ---\", \"@text.title\" },\n                    },\n                    {},\n                    {\n                        { \"All movements accept counts\" },\n                    },\n                    {\n                        { \"f/F and g/G work with `;` and `,`\" },\n                    },\n                }),\n                { buffer = ui_info.buffer }\n            )\n\n            vim.keymap.set(\"n\", \"i\", function()\n                local buffer = vim.api.nvim_create_buf(false, true)\n                vim.api.nvim_open_win(buffer, true, {\n                    style = \"minimal\",\n                    border = \"single\",\n                    title = \"Date (`?` for help)\",\n                    row = vim.api.nvim_win_get_height(0),\n                    col = 0,\n                    width = vim.o.columns,\n                    height = 1,\n                    relative = \"win\",\n                    win = vim.api.nvim_get_current_win(),\n                    noautocmd = true,\n                })\n\n                vim.cmd.startinsert()\n\n                local function quit()\n                    vim.cmd.stopinsert()\n                    vim.api.nvim_buf_delete(buffer, { force = true })\n                end\n\n                vim.keymap.set(\"n\", \"<Esc>\", quit, { buffer = buffer })\n                vim.keymap.set(\"i\", \"<C-c>\", quit, { buffer = buffer })\n                vim.keymap.set(\n                    \"n\",\n                    \"?\",\n                    lib.wrap(module.private.display_help, {\n                        {\n                            { \"q\", \"@namespace\" },\n                            { \" - \" },\n                            { \"close this window\", \"@text.strong\" },\n                        },\n                        {\n                            { \"<CR>\", \"@namespace\" },\n                            { \" - \" },\n                            { \"confirm date\", \"@text.strong\" },\n                        },\n                        {},\n                        {\n                            { \"--- Quitting ---\", \"@text.title\" },\n                        },\n                        {},\n                        {\n                            { \"<C-c> (insert mode)\", \"@namespace\" },\n                            { \" - \" },\n                            { \"quit\", \"@text.strong\" },\n                        },\n                        {\n                            { \"<Esc>\", \"@namespace\" },\n                            { \" - \" },\n                            { \"quit\", \"@text.strong\" },\n                        },\n                        {},\n                        {\n                            { \"--- Date Syntax ---\", \"@text.title\" },\n                        },\n                        {},\n                        {\n                            { \"Order \" },\n                            { \"does not matter\", \"@text.strong\" },\n                            { \" with dates.\" },\n                        },\n                        {},\n                        {\n                            { \"Some things depend on locale.\" },\n                        },\n                        {},\n                        {\n                            { \"Months and weekdays may be written\" },\n                        },\n                        {\n                            { \"with a shorthand.\" },\n                        },\n                        {},\n                        {\n                            { \"Years must contain 4 digits at\" },\n                        },\n                        {\n                            { \"all times. Prefix with zeroes\" },\n                        },\n                        {\n                            { \"where necessary.\" },\n                        },\n                        {},\n                        {\n                            { \"Hour syntax: `00:00.00` (hour, min, sec)\" },\n                        },\n                        {},\n                        {\n                            { \"--- Examples ---\", \"@text.title\" },\n                        },\n                        {},\n                        {\n                            { \"Tuesday May 5th 2023 19:00.23\", \"@neorg.markup.verbatim\" },\n                        },\n                        {\n                            { \"10 Feb CEST 0600\", \"@neorg.markup.verbatim\" },\n                            { \" (\", \"@comment\" },\n                            { \"0600\", \"@text.emphasis\" },\n                            { \" is the year)\", \"@comment\" },\n                        },\n                        {\n                            { \"9:00.4 2nd March Wed\", \"@neorg.markup.verbatim\" },\n                        },\n                    }),\n                    { buffer = buffer }\n                )\n\n                vim.keymap.set({ \"n\", \"i\" }, \"<CR>\", function()\n                    local line = vim.api.nvim_buf_get_lines(buffer, 0, -1, true)[1]\n\n                    local parsed_date = module.required[\"core.tempus\"].parse_date(line)\n\n                    if type(parsed_date) == \"string\" then\n                        log.error(\"[ERROR]:\", parsed_date)\n                        return\n                    end\n\n                    quit()\n\n                    local lua_date = module.required[\"core.tempus\"].to_lua_date(parsed_date)\n\n                    local should_redraw = false\n\n                    if view.current_mode.on_select ~= nil then\n                        should_redraw = view.current_mode:on_select(lua_date)\n                    end\n\n                    if should_redraw then\n                        view:render_view(ui_info, lua_date, nil, options)\n                    end\n                end, { buffer = buffer })\n            end, { buffer = ui_info.buffer })\n        end\n    end,\n}\n\nmodule.load = function()\n    module.required[\"core.ui.calendar\"].add_view(module.public.view_name, module.public)\nend\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/ui/module.lua",
    "content": "--[[\n    File: Core-UI\n    Title: Module for managing and displaying UIs to the user.\n    Summary: A set of public functions to help developers create and manage UI (selection popups, prompts...) in their modules.\n    Internal: true\n    ---\n--]]\n\nlocal neorg = require(\"neorg.core\")\nlocal log, modules = neorg.log, neorg.modules\n\nlocal module = modules.create(\"core.ui\", {\n    \"selection_popup\",\n    \"text_popup\",\n})\n\nmodule.setup = function()\n    for _, imported in pairs(module.imported) do\n        module.public = vim.tbl_extend(\"force\", module.public, imported.public)\n    end\n\n    return {}\nend\n\nmodule.private = {\n    namespace = vim.api.nvim_create_namespace(\"core.ui\"),\n}\n\n---@class core.ui: core.ui.selection_popup, core.ui.text_popup\nmodule.public = {\n    --- Returns a table in the form of { width, height } containing the width and height of the current window\n    ---@param half boolean #If true returns a position that could be considered the center of the window\n    get_window_size = function(half)\n        return half\n                and {\n                    math.floor(vim.fn.winwidth(0) / 2),\n                    math.floor(vim.fn.winheight(0) / 2),\n                }\n            or { vim.fn.winwidth(0), vim.fn.winheight(0) }\n    end,\n\n    --- Returns a modified version of floating window options.\n    ---@param modifiers table #This option set has two values - center_x and center_y.\n    --                           If they either of them is set to true then the window gets centered on that axis.\n    ---@param config table #A table containing regular Neovim options for a floating window\n    apply_custom_options = function(modifiers, config)\n        -- Default modifier options\n        local user_options = {\n            center_x = false,\n            center_y = false,\n        }\n\n        -- Override the default options with the user provided options\n        user_options = vim.tbl_extend(\"force\", user_options, modifiers or {})\n\n        -- Assign some default values to certain config options in case they're not specified\n        config = vim.tbl_deep_extend(\"keep\", config, {\n            relative = \"win\",\n            row = 0,\n            col = 0,\n            width = 100,\n            height = 100,\n        })\n\n        -- Get the current window's dimensions except halved\n        local halved_window_size = module.public.get_window_size(true)\n\n        -- If we want to center along the x axis then return a configuration that does so\n        if user_options.center_x then\n            config.row = config.row + halved_window_size[2] - math.floor(config.height / 2)\n        end\n\n        -- If we want to center along the y axis then return a configuration that does so\n        if user_options.center_y then\n            config.col = config.col + halved_window_size[1] - math.floor(config.width / 2)\n        end\n\n        return config\n    end,\n\n    --- Applies a set of options to a buffer\n    ---@param buf number the buffer number to apply the options to\n    ---@param option_list table a table of option = value pairs\n    apply_buffer_options = function(buf, option_list)\n        for option_name, value in pairs(option_list or {}) do\n            vim.api.nvim_set_option_value(option_name, value, { buf = buf })\n        end\n    end,\n\n    ---Creates a new horizontal split at the bottom of the screen\n    ---@param  name string the name of the buffer contained within the split (will have neorg:// prepended to it)\n    ---@param  config table? a table of <option> = <value> keypairs signifying buffer-local options for the buffer contained within the split\n    ---@param  height number? the height of the new split\n    ---@return number?, number? #Both the buffer ID and window ID\n    create_split = function(name, config, height)\n        vim.validate({\n            name = { name, \"string\" },\n            config = { config, \"table\", true },\n            height = { height, \"number\", true },\n        })\n\n        local bufname = \"neorg://\" .. name\n\n        if vim.fn.bufexists(bufname) == 1 then ---@diagnostic disable-line\n            log.error(\"Buffer '\" .. name .. \"' already exists\")\n            return\n        end\n\n        vim.cmd(\"below new\")\n\n        if height then\n            vim.api.nvim_win_set_height(0, height)\n        end\n\n        local buf = vim.api.nvim_win_get_buf(0)\n\n        local default_options = {\n            swapfile = false,\n            bufhidden = \"hide\",\n            buftype = \"nofile\",\n            buflisted = false,\n            filetype = \"norg\",\n        }\n\n        vim.api.nvim_buf_set_name(buf, bufname)\n        vim.api.nvim_win_set_buf(0, buf)\n\n        vim.api.nvim_set_option_value(\"list\", false, { win = 0 })\n        vim.api.nvim_set_option_value(\"colorcolumn\", \"\", { win = 0 })\n        vim.api.nvim_set_option_value(\"number\", false, { win = 0 })\n        vim.api.nvim_set_option_value(\"relativenumber\", false, { win = 0 })\n        vim.api.nvim_set_option_value(\"signcolumn\", \"no\", { win = 0 })\n\n        -- Merge the user provided options with the default options and apply them to the new buffer\n        module.public.apply_buffer_options(buf, vim.tbl_extend(\"keep\", config or {}, default_options))\n\n        local window = vim.api.nvim_get_current_win()\n\n        -- Make sure to clean up the window if the user leaves the popup at any time\n        vim.api.nvim_create_autocmd({ \"BufDelete\", \"WinClosed\" }, {\n            buffer = buf,\n            once = true,\n            callback = function()\n                pcall(vim.api.nvim_win_close, window, true)\n                pcall(vim.api.nvim_buf_delete, buf, { force = true })\n            end,\n        })\n\n        return buf, window\n    end,\n\n    --- Creates a new vertical split\n    ---@param name string the name of the buffer\n    ---@param enter boolean enter the window or not\n    ---@param buf_config table a table of <option> = <value> keypairs signifying buffer-local options for the buffer contained within the split\n    ---@param win_config table table of <option>=<value> keypairs for `nvim_open_win`, must provide `win`\n    ---@return number?, number? #The buffer and window numbers of the vertical split\n    create_vsplit = function(name, enter, buf_config, win_config)\n        vim.validate({\n            name = { name, \"string\" },\n            enter = { enter, \"boolean\", true },\n            config = { buf_config, \"table\" },\n            win_config = { win_config, \"table\" },\n        })\n\n        local buf = vim.api.nvim_create_buf(false, true)\n\n        local default_options = {\n            swapfile = false,\n            bufhidden = \"hide\",\n            buftype = \"nofile\",\n            buflisted = false,\n            filetype = \"norg\",\n        }\n\n        vim.api.nvim_buf_set_name(buf, \"neorg://\" .. name)\n\n        local win_options = {\n            vertical = true,\n        }\n        win_options = vim.tbl_deep_extend(\"keep\", win_options, win_config)\n        local window = vim.api.nvim_open_win(buf, enter, win_options)\n\n        vim.api.nvim_set_option_value(\"number\", false, { win = window })\n        vim.api.nvim_set_option_value(\"relativenumber\", false, { win = window })\n\n        -- Merge the user provided options with the default options and apply them to the new buffer\n        module.public.apply_buffer_options(buf, vim.tbl_extend(\"keep\", buf_config or {}, default_options))\n\n        -- Make sure to clean up the window if the user leaves the popup at any time\n        vim.api.nvim_create_autocmd({ \"BufDelete\", \"WinClosed\" }, {\n            buffer = buf,\n            once = true,\n            callback = function()\n                pcall(vim.api.nvim_win_close, window, true)\n                pcall(vim.api.nvim_buf_delete, buf, { force = true })\n            end,\n        })\n\n        return buf, window\n    end,\n\n    --- Creates a new display in which you can place organized data\n    ---@param name string #The name of the display\n    ---@param split_type string #\"vsplitl\"|\"vsplitr\"|\"split\"|\"nosplit\" - if suffixed with \"l\" vertical split will be spawned on the left, else on the right. \"split\" is a horizontal split.\n    ---@param content table #A table of content for the display\n    create_display = function(name, split_type, content)\n        if not vim.tbl_contains({ \"nosplit\", \"vsplitl\", \"vsplitr\", \"split\" }, split_type) then\n            log.error(\n                \"Unable to create display. Expected one of 'vsplitl', 'vsplitr', 'split' or 'nosplit', got\",\n                split_type,\n                \"instead.\"\n            )\n            return\n        end\n\n        local namespace = vim.api.nvim_create_namespace(\"neorg://display/\" .. name)\n\n        local buf = (function()\n            name = \"display/\" .. name\n\n            if split_type == \"vsplitl\" then\n                return module.public.create_vsplit(name, true, {}, { split = \"left\" })\n            elseif split_type == \"vsplitr\" then\n                return module.public.create_vsplit(name, true, {}, { split = \"right\" })\n            elseif split_type == \"split\" then\n                return module.public.create_split(name, {})\n            else\n                local buf = vim.api.nvim_create_buf(true, true)\n                vim.api.nvim_buf_set_name(buf, name)\n                return buf\n            end\n        end)()\n\n        vim.api.nvim_win_set_buf(0, buf)\n\n        local length = vim.fn.len(vim.tbl_filter(function(elem)\n            return vim.tbl_isempty(elem) or (elem[3] == nil and true or elem[3])\n        end, content))\n\n        vim.api.nvim_buf_set_lines(buf, 0, length, false, vim.split((\"\\n\"):rep(length), \"\\n\", { plain = true }))\n\n        local line_number = 1\n        local buffer = {}\n\n        for i, text_info in ipairs(content) do\n            if not vim.tbl_isempty(text_info) then\n                local newline = text_info[3] == nil and true or text_info[3]\n\n                table.insert(buffer, { text_info[1], text_info[2] or \"Normal\" })\n\n                if i == #content or newline then\n                    vim.api.nvim_buf_set_extmark(0, namespace, line_number - 1, 0, {\n                        virt_text_pos = \"overlay\",\n                        virt_text = buffer,\n                    })\n                    buffer = {}\n                    line_number = line_number + 1\n                end\n            else\n                line_number = line_number + 1\n            end\n        end\n\n        vim.keymap.set(\"n\", \"<Esc>\", vim.cmd.bdelete, { buffer = buf, silent = true })\n        vim.keymap.set(\"n\", \"q\", vim.cmb.bdelete, { buffer = buf, silent = true })\n\n        vim.api.nvim_set_option_value(\"modifiable\", false, { buf = buf })\n\n        ---@diagnostic disable-next-line: undefined-field\n        local cached_virtualedit = vim.opt.virtualedit:get()\n        vim.opt.virtualedit = \"all\"\n\n        vim.api.nvim_create_autocmd({ \"BufLeave\", \"BufDelete\" }, {\n            buffer = buf,\n            callback = function()\n                vim.opt.virtualedit = cached_virtualedit\n                pcall(vim.api.nvim_win_close, 0, true)\n                pcall(vim.api.nvim_buf_delete, buf, { force = true })\n            end,\n        })\n\n        vim.cmd(([[\n            autocmd BufLeave,BufDelete <buffer=%s> set virtualedit=%s | silent! bd! %s\n        ]]):format(buf, cached_virtualedit[1] or \"\", buf))\n\n        return { buffer = buf, namespace = namespace }\n    end,\n\n    --- Creates a new Neorg buffer in a split or in the main window\n    ---@param name string the name of the buffer *without* the .norg extension\n    ---@param split_type string \"vsplitl\"|\"vsplitr\"|\"split\"|\"nosplit\" - if suffixed with \"l\" vertical split will be spawned on the left, else on the right. \"split\" is a horizontal split.\n    ---@param config table|nil a table of { option = value } pairs that set buffer-local options for the created Neorg buffer\n    ---@param opts table|nil\n    ---   - opts.keybinds (boolean)             if false, will not use the default keybinds\n    ---   - opts.del_on_autocommands (table)    delete buffer on specified autocommands\n    ---@return number? buffer or nil when an error is logged\n    create_norg_buffer = function(name, split_type, config, opts)\n        vim.validate({\n            name = { name, \"string\" },\n            split_type = { split_type, \"string\" },\n            config = { config, \"table\", true },\n            opts = { opts, \"table\", true },\n        })\n\n        config = vim.tbl_deep_extend(\"keep\", config or {}, {\n            ft = \"norg\",\n        })\n\n        opts = vim.tbl_deep_extend(\n            \"force\",\n            { keybinds = true, del_on_autocommands = { \"BufLeave\", \"BufDelete\", \"BufUnload\" } },\n            opts or {}\n        )\n\n        if not vim.tbl_contains({ \"nosplit\", \"vsplitl\", \"vsplitr\", \"split\" }, split_type) then\n            log.error(\n                \"Unable to create display. Expected one of 'vsplitl', 'vsplitr', 'split' or 'nosplit', got\",\n                split_type,\n                \"instead.\"\n            )\n            return\n        end\n\n        local buf = (function()\n            name = \"norg/\" .. name .. \".norg\"\n\n            if split_type == \"vsplitl\" then\n                return module.public.create_vsplit(name, true, {}, { split = \"left\" })\n            elseif split_type == \"vsplitr\" then\n                return module.public.create_vsplit(name, true, {}, { split = \"right\" })\n            elseif split_type == \"split\" then\n                return module.public.create_split(name, {})\n            else\n                local buf = vim.api.nvim_create_buf(true, true)\n                vim.api.nvim_buf_set_name(buf, name)\n                return buf\n            end\n        end)()\n\n        vim.api.nvim_win_set_buf(0, buf)\n\n        if opts.keybinds == true then\n            vim.keymap.set(\"n\", \"<Esc>\", vim.cmd.bdelete, { buffer = buf, silent = true })\n            vim.keymap.set(\"n\", \"q\", vim.cmd.bdelete, { buffer = buf, silent = true })\n        end\n\n        module.public.apply_buffer_options(buf, config or {})\n\n        if opts.del_on_autocommands and #opts.del_on_autocommands ~= 0 then\n            vim.cmd(\n                \"autocmd \"\n                    .. table.concat(opts.del_on_autocommands, \",\")\n                    .. (\" <buffer=%s> silent! bd! %s\"):format(buf, buf)\n            )\n        end\n\n        return buf\n    end,\n}\n\nmodule.examples = {\n    [\"Create a selection popup\"] = function()\n        -- Creates the buffer\n        local buffer = module.public.create_split(\"selection/Test selection\")\n        if not buffer then\n            return\n        end\n\n        -- Binds a selection to that buffer\n        local selection = module.public\n            .begin_selection(buffer)\n            :apply({\n                -- A title will simply be text with a custom highlight\n                title = function(self, text)\n                    return self:text(text, \"@text.title\")\n                end,\n            })\n            :listener({ \"<Esc>\" }, function(self)\n                self:destroy()\n            end)\n            :listener({ \"<BS>\" }, function(self)\n                self:pop_page()\n            end)\n\n        selection\n            :options({\n                text = {\n                    highlight = \"@text.underline\",\n                },\n            })\n            :title(\"Hello World!\")\n            :blank()\n            :text(\"Flags:\")\n            :flag(\"<CR>\", \"finish\")\n            :flag(\"t\", \"test flag\", function()\n                log.warn(\"The test flag has been pressed!\")\n            end)\n            :blank()\n            :text(\"Other flags:\")\n            :rflag(\"a\", \"press me!\", function()\n                selection:setstate(\"test\", \"hello from the other side\")\n\n                -- Create more elements for the selection\n                selection\n                    :title(\"Another Title!\")\n                    :blank()\n                    :text(\"Other Flags:\")\n                    :flag(\"a\", \"i do nothing :)\")\n                    :rflag(\"b\", \"yet another nested flag\", function()\n                        selection\n                            :title(\"Final Title\")\n                            :blank()\n                            :text(\"Btw, did you know that you can\")\n                            :text(\"Press <BS> to go back a page? Try it!\")\n                            :blank()\n                            :text(\"Also, psst, pressing `g` will give you a small surprise\")\n                            :blank()\n                            :flag(\"a\", \"does nothing too\")\n                            :listener({ \"g\" }, function()\n                                log.warn(\"You are awesome :)\")\n                            end)\n                    end)\n            end)\n            :stateof( -- To view this press `a` and then <BS> to go back\n                \"test\",\n                \"This is a custom message: %s.\" --[[ you can supply a third argument which\n                will forcefully render the message even if the state isn't present. The state will be replaced with a \" \" ]]\n            )\n    end,\n}\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/ui/selection_popup/module.lua",
    "content": "--[[\n    A UI module to allow the user to press different keys to select different actions\n--]]\n\nlocal neorg = require(\"neorg.core\")\nlocal modules = neorg.modules\nlocal lib = neorg.lib\n\nlocal module = modules.create(\"core.ui.selection_popup\")\n\n---@class core.ui.selection_popup\nmodule.public = {\n    --- Constructs a new selection\n    ---@param buffer number #The number of the buffer the selection should attach to\n    ---@param keybind_buffer number? #An alternate buffer from which the keybinds for the selection popup are entered.\n    ---@return table #A selection object\n    begin_selection = function(buffer, keybind_buffer)\n        -- Data that is gathered up over the lifetime of the selection popup\n        local data = {}\n\n        -- Get the name of the buffer we are about to attach to\n        local name = vim.api.nvim_buf_get_name(buffer)\n\n        -- Create a namespace from the buffer name\n        local namespace = vim.api.nvim_create_namespace(name)\n\n        --- Simply renders things using extmarks\n        local renderer = {\n            position = 0,\n\n            --- Renders something in the buffer\n            --- @vararg table #A vararg of { text, highlight } tables\n            render = function(self, ...)\n                vim.api.nvim_set_option_value(\"modifiable\", true, { buf = buffer })\n\n                -- Don't render if we're on the first line\n                -- because buffers always open with one line available\n                -- anyway\n                if self.position > 0 then\n                    vim.api.nvim_buf_set_lines(buffer, -1, -1, false, { \"\" })\n                end\n\n                if not vim.tbl_isempty({ ... }) then\n                    vim.api.nvim_buf_set_extmark(buffer, namespace, self.position, 0, {\n                        virt_text_pos = \"overlay\",\n                        virt_text = { ... },\n                    })\n                end\n\n                -- Track which line we're on\n                self.position = self.position + 1\n\n                vim.api.nvim_set_option_value(\"modifiable\", false, { buf = buffer })\n            end,\n\n            --- Resets the renderer by clearing the buffer and resetting\n            --- the render head\n            reset = function(self)\n                self.position = 0\n\n                vim.api.nvim_set_option_value(\"modifiable\", true, { buf = buffer })\n\n                vim.api.nvim_buf_clear_namespace(buffer, namespace, 0, -1)\n                vim.api.nvim_buf_set_lines(buffer, 0, -1, true, {})\n\n                vim.api.nvim_set_option_value(\"modifiable\", false, { buf = buffer })\n            end,\n        }\n\n        ---@class core.ui.selection\n        local selection = {\n            page = 1,\n            pages = { {} },\n            opts = {},\n            localkeys = {},\n            states = {},\n\n            --- Retrieves the options for a certain type\n            ---@param type string #The type of element to extract the options for\n            ---@return table #The options for said type or {}\n            options_for = function(self, type)\n                return self.opts[type] or {}\n            end,\n\n            --- Applies some new functions for the selection\n            ---@param tbl_of_functions table #A table of custom elements\n            ---@return core.ui.selection\n            apply = function(self, tbl_of_functions)\n                self = vim.tbl_deep_extend(\"force\", self, tbl_of_functions)\n                return self\n            end,\n\n            --- Adds a new element to the current page\n            ---@param element function #A pointer to the function that created the item\n            --- @vararg any #The arguments that were used to construct the element\n            add = function(self, element, ...)\n                table.insert(self.pages[self.page], { self[element], { ... } })\n            end,\n\n            --- Attaches a key listener to the current buffer\n            ---@param keys table #An array of keys to bind\n            ---@param func function #A callback to invoke whenever the key has been pressed\n            ---@param mode string #Optional, default \"n\": the mode to create the listener for\n            ---@return core.ui.selection\n            listener = function(self, keys, func, mode)\n                -- Go through all keys that the user has bound a listener to and bind them!\n                for _, key in ipairs(keys) do\n                    vim.keymap.set(mode or \"n\", key, lib.wrap(func, self), {\n                        buffer = keybind_buffer or buffer,\n                        silent = true,\n                        nowait = true,\n                    })\n                end\n\n                return self\n            end,\n\n            --- Attaches a key listener to the current page\n            ---@param keys table #An array of keys to bind\n            ---@param func function #A callback to invoke whenever the key has been pressed\n            ---@param mode string #Optional, default \"n\": the mode to create the listener for\n            ---@return core.ui.selection\n            locallistener = function(self, keys, func, mode)\n                -- Extend the page-local keys too\n                self.localkeys = vim.list_extend(self.localkeys, keys)\n\n                -- Go through all keys that the user has bound a listener to and bind them!\n                for _, key in pairs(keys) do\n                    vim.keymap.set(mode or \"n\", key, lib.wrap(func, self), {\n                        buffer = keybind_buffer or buffer,\n                        silent = true,\n                        nowait = true,\n                    })\n                end\n\n                return self\n            end,\n\n            --- Sets some options for the selection to take into account\n            ---@param opts table #A table of options\n            ---@return core.ui.selection\n            options = function(self, opts)\n                self.opts = vim.tbl_deep_extend(\"force\", self.opts, opts)\n                return self\n            end,\n\n            --- Returns the data the selection holds\n            data = function(_)\n                return data\n            end,\n\n            --- Add a pair of key, value in data\n            ---@param key string #The name for the key\n            ---@param value any #Its content\n            set_data = function(_, key, value)\n                data[key] = value\n            end,\n\n            --- Detaches the selection popup from the current buffer\n            --- Does *not* close the buffer\n            detach = function(self)\n                if not vim.api.nvim_buf_is_valid(buffer) then\n                    return\n                end\n\n                renderer:reset()\n\n                self.page = 1\n                self.pages = {}\n\n                return data\n            end,\n\n            --- Destroys the selection popup and the buffer it occupied\n            destroy = function(self)\n                if not vim.api.nvim_buf_is_valid(buffer) then\n                    return\n                end\n\n                renderer:reset()\n\n                self.page = 1\n                self.pages = {}\n\n                vim.api.nvim_buf_delete(buffer, { force = true })\n                return data\n            end,\n\n            --- Renders some text on the screen\n            ---@param text string #The text to display\n            ---@param highlight string #An optional highlight group to use (defaults to \"Normal\")\n            ---@return core.ui.selection\n            text = function(self, text, highlight)\n                local custom_highlight = self:options_for(\"text\").highlight\n\n                self:add(\"text\", text, highlight)\n\n                renderer:render({\n                    text,\n                    highlight or custom_highlight or \"Normal\",\n                })\n\n                return self\n            end,\n\n            --- Generates a title\n            ---@param text string #The text to display\n            ---@return core.ui.selection\n            title = function(self, text)\n                return self:text(text, \"@text.title\")\n            end,\n\n            --- Simply enters a blank line\n            ---@param count number #An optional number of blank lines to apply\n            ---@return core.ui.selection\n            blank = function(self, count)\n                count = count or 1\n                renderer:render()\n\n                self:add(\"blank\", count)\n\n                if count <= 1 then\n                    return self\n                else\n                    return self:blank(count - 1)\n                end\n            end,\n\n            --- Creates a pressable flag\n            ---@param flag string #The flag. These should be a single character\n            ---@param description string #The description for the flag\n            ---@param callback table|function #The callback to invoke or configuration options for the flag\n            ---@return core.ui.selection\n            flag = function(self, flag, description, callback)\n                -- Set up the configuration by properly merging everything\n                local configuration = vim.tbl_deep_extend(\n                    \"force\",\n                    {\n                        keys = {\n                            flag,\n                        },\n                        highlights = {\n                            -- TODO: Change highlight group names\n                            key = \"@neorg.selection_window.key\",\n                            description = \"@neorg.selection_window.keyname\",\n                            delimiter = \"@neorg.selection_window.arrow\",\n                        },\n                        delimiter = \" -> \",\n                        -- Whether to destroy the selection popup when this flag is pressed\n                        destroy = true,\n                    },\n                    self:options_for( -- First merge the global options\n                        \"flag\"\n                    ),\n                    type(callback) == \"table\" and callback or {} -- Then optionally merge the flag-specific options\n                )\n\n                self:add(\"flag\", flag, description, callback)\n\n                -- Attach a locallistener to this flag\n                self = self:locallistener(configuration.keys, function()\n                    -- Delete the selection before any action\n                    -- We assume pressing a flag does quit the popup\n                    if configuration.destroy then\n                        self:destroy()\n                    end\n\n                    -- Invoke the user-defined callback\n                    (function()\n                        if type(callback) == \"function\" then\n                            return callback\n                        else\n                            return callback and callback.callback or function() end\n                        end\n                    end)()()\n                end)\n\n                -- Actually render the flag\n                renderer:render({\n                    flag,\n                    configuration.highlights.key,\n                }, {\n                    configuration.delimiter,\n                    configuration.highlights.delimiter,\n                }, {\n                    description or \"no description\",\n                    configuration.highlights.description,\n                })\n\n                return self\n            end,\n\n            --- Constructs a recursive (nested) flag\n            ---@param flag string #The flag key, should be one character only\n            ---@param description string #The description of the flag\n            ---@param callback function|table #The callback to invoke after the flag is entered\n            ---@return core.ui.selection\n            rflag = function(self, flag, description, callback)\n                -- Set up the configuration by properly merging everything\n                local configuration = vim.tbl_deep_extend(\n                    \"force\",\n                    {\n                        keys = {\n                            flag,\n                        },\n                        highlights = {\n                            -- TODO: Change highlight group names\n                            key = \"@neorg.selection_window.key\",\n                            description = \"@neorg.selection_window.keyname\",\n                            delimiter = \"@neorg.selection_window.arrow\",\n                        },\n                        delimiter = \" -> \",\n                    },\n                    self:options_for( -- First merge the global options\n                        \"rflag\"\n                    ),\n                    type(callback) == \"table\" and callback or {} -- Then optionally merge the rflag-specific options\n                )\n\n                self:add(\"rflag\", flag, description, callback)\n\n                -- Attach a locallistener to this flag\n                self = self:locallistener(configuration.keys, function()\n                    -- Create a new page to allow the renderer to start fresh\n                    self:push_page();\n\n                    -- Invoke the user-defined callback\n                    (function()\n                        if type(callback) == \"function\" then\n                            return callback()\n                        elseif callback.callback then\n                            return callback.callback()\n                        end\n                    end)()\n                end)\n\n                -- Actually render the flag\n                renderer:render({\n                    flag,\n                    configuration.highlights.key,\n                }, {\n                    configuration.delimiter,\n                    configuration.highlights.delimiter,\n                }, {\n                    \"+\" .. (description or \"no description\"),\n                    configuration.highlights.description,\n                })\n\n                return self\n            end,\n\n            --- Pushes a new page onto the stack, clearing the buffer\n            --- and starting fresh\n            push_page = function(self)\n                -- Go through every locally bound key and unbind it\n                -- We don't want page-local keys to continue being bound\n                for _, key in ipairs(self.localkeys) do\n                    vim.api.nvim_buf_del_keymap(buffer, \"\", key)\n                end\n\n                self.localkeys = {}\n\n                self.page = self.page + 1\n                self.pages[self.page] = {}\n\n                renderer:reset()\n            end,\n\n            --- Pops the page stack, effectively restoring the previous\n            --- state\n            pop_page = function(self)\n                -- If we have no pages left then there's nothing to pop\n                if self.page - 1 < 1 then\n                    return\n                end\n\n                for _, key in ipairs(self.localkeys) do\n                    vim.api.nvim_buf_del_keymap(buffer, \"\", key)\n                end\n\n                self.localkeys = {}\n                -- Delete the current page from existence\n                self.pages[self.page] = {}\n\n                -- Decrement the page counter\n                self.page = self.page - 1\n\n                -- Create a local copy of the previous (now current) page\n                -- We do this because when we start rendering objects\n                -- they'll start getting added onto the current page\n                -- and will start looping to infinity.\n                local page_copy = vim.deepcopy(self.pages[self.page])\n                -- Clear the current page;\n                self.pages[self.page] = {}\n\n                -- Reset the renderer to make sure we're starting afresh\n                renderer:reset()\n\n                -- Loop through all items in the page and recreate\n                -- each element\n                for _, item in ipairs(page_copy) do\n                    item[1](self, unpack(item[2]))\n                end\n            end,\n\n            --- Creates a prompt inside the page\n            ---@param text string #The prompt text\n            ---@param callback table|function #The callback to invoke or configuration options for the prompt\n            ---@return core.ui.selection\n            prompt = function(self, text, callback)\n                -- Set up the configuration by properly merging everything\n                local configuration = vim.tbl_deep_extend(\n                    \"force\",\n                    {\n                        text = text or \"Input\",\n                        delimiter = \" -> \",\n                        -- Automatically destroys the popup when prompt is confirmed\n                        destroy = true,\n                        prompt_text = nil,\n                    },\n\n                    self:options_for( -- First merge the global options\n                        \"prompt\"\n                    ),\n                    type(callback) == \"table\" and callback or {} -- Then optionally merge the flag-specific options\n                )\n                self:add(\"prompt\", text, callback)\n                self = self:blank()\n\n                -- Create prompt text\n                vim.fn.prompt_setprompt(buffer, configuration.text .. configuration.delimiter)\n\n                -- Create prompt\n                vim.api.nvim_set_option_value(\"modifiable\", true, { buf = buffer })\n                local options = vim.api.nvim_get_option_value(\"buftype\", { buf = buffer })\n                vim.api.nvim_set_option_value(\"buftype\", \"prompt\", { buf = buffer })\n\n                -- Create a callback to be invoked on prompt confirmation\n                vim.fn.prompt_setcallback(buffer, function(content)\n                    if content:len() > 0 then\n                        -- Remakes the buftype option the same before prompt\n                        vim.api.nvim_set_option_value(\"buftype\", options, { buf = buffer })\n\n                        -- Delete the selection before any action\n                        -- We assume pressing a flag does quit the popup\n                        if configuration.pop then\n                            -- Reset buftype options to previous ones\n                            self:pop_page()\n                        elseif configuration.destroy then\n                            self:destroy()\n                        end\n\n                        -- Invoke the user-defined callback\n                        if type(callback) == \"function\" then\n                            callback(content)\n                        else\n                            callback.callback(content)\n                        end\n                    end\n                end)\n\n                -- Jump to insert mode\n                vim.api.nvim_feedkeys(\"A\", \"t\", false)\n\n                -- Add prompt text in the prompt\n                if configuration.prompt_text then\n                    vim.api.nvim_feedkeys(configuration.prompt_text, \"n\", false)\n                end\n\n                return self\n            end,\n\n            --- Concatenates a `callback` function that returns the selection popup to the existing selection popup\n            --- Example:\n            --- selection\n            ---   :text(\"test\")\n            ---   :concat(this_is_a_function)\n            ---@param callback function #The function to append\n            ---@return core.ui.selection\n            concat = function(self, callback)\n                self = callback(self)\n                return self\n            end,\n\n            ---@return core.ui.selection\n            setstate = function(self, key, value, rerender)\n                self.states[key] = {\n                    value = value,\n                }\n\n                -- Reset the renderer to make sure we're starting afresh\n                renderer:reset()\n\n                if rerender then\n                    renderer:reset()\n                    -- Loop through all items in the page and recreate\n                    -- each element\n                    for _, item in ipairs(self.pages[self.page]) do\n                        item[1](self, unpack(item[2]))\n                    end\n                end\n\n                return self\n            end,\n\n            -- TODO: Add support for a callback to be invoked on state change\n            --- Nicely display a data to be re-rendered on each modification\n            ---@param key string key to data\n            ---@param format string formatted string to display the content of key\n            ---@param force_render? boolean forcefully render the message even if the state isn't present\n            ---@return core.ui.selection\n            stateof = function(self, key, format, force_render)\n                format = format or \"%s\"\n                force_render = force_render or false\n\n                -- Set up the configuration by properly merging everything\n                local configuration = vim.tbl_deep_extend(\"force\", {\n                    highlight = \"Normal\",\n                }, self:options_for(\"stateof\"))\n\n                self:add(\"stateof\", key, format)\n\n                if force_render or (self.states[key] and self.states[key].value) then\n                    renderer:render({\n                        format:format(self.states[key] and self.states[key].value or \" \"),\n                        configuration.highlight,\n                    })\n                end\n\n                return self\n            end,\n        }\n\n        return selection\n    end,\n}\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/ui/text_popup/module.lua",
    "content": "--[[\n    File for creating text popups for the user.\n--]]\n\nlocal neorg = require(\"neorg.core\")\nlocal modules = neorg.modules\n\nlocal module = modules.create(\"core.ui.text_popup\")\n\n---@class core.ui.text_popup\nmodule.public = {\n    --- Opens a floating window at the specified position and asks for user input\n    ---@param name string #The name of the floating window\n    ---@param input_text string #The input text to prompt the user for input\n    ---@param callback fun(entered_text: string, data: table) #A function that gets invoked whenever the user provides some text.\n    ---@param modifiers table #Special table to modify certain attributes of the floating window (like centering on the x or y axis)\n    ---@param config table #A config like you would pass into nvim_open_win()\n    create_prompt = function(name, input_text, callback, modifiers, config)\n        -- Create the base cofiguration for the popup window\n        local window_config = {\n            relative = \"win\",\n            style = \"minimal\",\n            border = \"rounded\",\n        }\n\n        -- Apply any custom modifiers that the user has specified\n        window_config = assert(modules.get_module(\"core.ui\"), \"core.ui is not loaded!\").apply_custom_options(\n            modifiers,\n            vim.tbl_extend(\"force\", window_config, config or {})\n        )\n\n        local buf = vim.api.nvim_create_buf(false, true)\n\n        -- Set the buffer type to \"prompt\" to give it special behaviour (:h prompt-buffer)\n        vim.api.nvim_set_option_value(\"buftype\", \"prompt\", { buf = buf })\n        vim.api.nvim_buf_set_name(buf, name)\n\n        -- Create a callback to be invoked on prompt confirmation\n        vim.fn.prompt_setcallback(buf, function(content)\n            if content:len() > 0 then\n                callback(content, {\n                    close = function(opts)\n                        vim.api.nvim_buf_delete(buf, opts or { force = true })\n                    end,\n                })\n            end\n        end)\n\n        -- Construct some custom mappings for the popup\n        vim.keymap.set(\"n\", \"<Esc>\", vim.cmd.quit, { silent = true, buffer = buf })\n        vim.keymap.set(\"n\", \"<Tab>\", \"<CR>\", { silent = true, buffer = buf })\n        vim.keymap.set(\"i\", \"<Tab>\", \"<CR>\", { silent = true, buffer = buf })\n        vim.keymap.set(\"i\", \"<C-c>\", \"<Esc>:q<CR>\", { silent = true, buffer = buf })\n\n        -- If the use has specified some input text then show that input text in the buffer\n        if input_text then\n            vim.fn.prompt_setprompt(buf, input_text)\n        end\n\n        -- Automatically enter insert mode\n        vim.api.nvim_feedkeys(\"i\", \"t\", false)\n\n        -- Create the floating popup window with the prompt buffer\n        local winid = vim.api.nvim_open_win(buf, true, window_config)\n\n        -- Make sure to clean up the window if the user leaves the popup at any time\n        vim.api.nvim_create_autocmd({ \"WinLeave\", \"BufLeave\", \"BufDelete\" }, {\n            buffer = buf,\n            once = true,\n            callback = function()\n                pcall(vim.api.nvim_win_close, winid, true)\n                pcall(vim.api.nvim_buf_delete, buf, { force = true })\n            end,\n        })\n\n        -- HACK(vhyrro): Prevent the \"not enough room\" error when leaving the window.\n        -- See: https://github.com/neovim/neovim/issues/19464\n        vim.api.nvim_set_option_value(\"winbar\", \"\", { win = winid })\n    end,\n}\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/modules/core/upgrade/module.lua",
    "content": "--[[\n    file: Upgrade\n    title: Upgrade Tool for Neorg\n    summary: Converts old versions of the Norg syntax to newer ones.\n    ---\nWhen dealing with changes to the Norg syntax, it is very inconvenient to have to manually\nupdate all of your files, especially if you're not a regex wizard.\n\nTo alleviate this problem, the upgrade tool serves as a way to automate the process.\n\nThere are two main commands that are exposed for use:\n- `:Neorg upgrade current-file` - takes the current file and upgrades it to the new syntax.\n  Will ask for a backup.\n- `:Neorg upgrade current-directory` - upgrades all files in the current directory. Asks\n   for a backup and displays the current directory so no mistakes are made.\n\nWhen a backup is requested, Neorg backs up the file to `<filename>.old.norg`, then upgrades\nthe original file/directory in-place.\n--]]\n\nlocal neorg = require(\"neorg.core\")\nlocal log, modules = neorg.log, neorg.modules\n\nlocal module = modules.create(\"core.upgrade\")\n\nmodule.setup = function()\n    log.error(\n        \"The `core.upgrade` module has been deprecated and is no longer in use. Please remove it from your loaded list.\"\n    )\n\n    return {\n        success = false,\n    }\nend\n\nreturn module\n"
  },
  {
    "path": "lua/neorg/tests/init.lua",
    "content": "--[[\nThis file contains a variety of utility functions for writing Neorg tests.\n\nThe functions used here should be generalized and moved out into a separate rock on luarocks.org in due time.\n\nNeorg sets up `busted` using `neolua` (a wrapper around neovim) as its test interpreter. This allows access to all\nNeovim-related APIs natively. The entire process occurs in the flake.nix file.\n--]]\n\nlocal tests = {}\n\n--- Sets up Neorg with a given module.\n---@param module_name string The name of the module to load.\n---@param configuration table? The configuration for the module.\n---@return table #The main Neorg table with the setup() function called.\nfunction tests.neorg_with(module_name, configuration)\n    local neorg = require(\"neorg\")\n\n    neorg.setup({\n        load = {\n            [\"core.defaults\"] = {},\n            [module_name] = { config = configuration },\n        },\n    })\n\n    return neorg\nend\n\n--- Runs a callback in the context of a given file.\n---@param filename string The name of the file (used to determine filetype)\n---@param content string The content of the buffer.\n---@param callback fun(bufnr: number) The function to execute with the buffer number provided.\nfunction tests.in_file(filename, content, callback)\n    local buf = vim.api.nvim_create_buf(false, true)\n    vim.api.nvim_buf_set_name(buf, filename)\n    vim.api.nvim_buf_set_lines(buf, 0, -1, true, vim.split(content, \"\\n\"))\n\n    callback(buf)\n\n    vim.api.nvim_buf_delete(buf, { force = true })\nend\n\nreturn tests\n"
  },
  {
    "path": "neorg-scm-1.rockspec",
    "content": "local MODREV, SPECREV = \"scm\", \"-1\"\nrockspec_format = \"3.0\"\npackage = \"neorg\"\nversion = MODREV .. SPECREV\n\ndescription = {\n\tsummary = \"Modernity meets insane extensibility. The future of organizing your life in Neovim.\",\n\tlabels = { \"neovim\" },\n\thomepage = \"https://github.com/nvim-neorg/neorg\",\n\tlicense = \"GPL-3.0\",\n}\n\ndependencies = {\n\t\"lua == 5.1\",\n    \"nvim-nio ~> 1.7\",\n    \"lua-utils.nvim == 1.0.2\",\n    \"plenary.nvim == 0.1.4\",\n    \"nui.nvim == 0.3.0\",\n    \"pathlib.nvim ~> 2.2\",\n    -- \"norgopolis-client.lua >= 0.2.0\",\n    -- \"norgopolis-server.lua >= 1.3.1\",\n    -- \"tree-sitter-norg == 0.2.4\",\n    -- \"tree-sitter-norg-meta == 0.1.0\",\n}\n\nsource = {\n\turl = \"http://github.com/nvim-neorg/neorg/archive/v\" .. MODREV .. \".zip\",\n}\n\nif MODREV == \"scm\" then\n\tsource = {\n\t\turl = \"git://github.com/nvim-neorg/neorg\",\n\t}\nend\n\ntest_dependencies = {\n    \"nlua\",\n    -- Placed here as we plan on removing nvim-treesitter as a dependency soon, but it's still required for various tests.\n    \"nvim-treesitter-legacy-api == 0.9.2\",\n}\n\nbuild = {\n   type = \"builtin\",\n   copy_directories = {\n       \"queries\",\n       \"doc\",\n   }\n}\n"
  },
  {
    "path": "nix/checks/default.nix",
    "content": "{\n  self,\n  git-hooks,\n  lib,\n  ...\n}: {\n  perSystem = {\n    pkgs,\n    system,\n    ...\n  }: {\n    checks = {\n      type-check = git-hooks.lib.${system}.run {\n        src = \"${self}/lua\";\n\n        hooks = {\n          lua-ls = {\n            enable = true;\n            settings.configuration = pkgs.luarc-with-dependencies;\n          };\n        };\n      };\n\n      pre-commit-check = git-hooks.lib.${system}.run {\n        src = \"${self}\";\n\n        hooks = {\n          alejandra.enable = true;\n          luacheck.enable = true;\n          # stylua.enable = true;\n        };\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "nix/overlays/default.nix",
    "content": "{\n  self,\n  inputs,\n  gen-luarc,\n  neorocks,\n  ...\n}: {\n  perSystem = {\n    pkgs,\n    system,\n    ...\n  }: {\n    _module.args.pkgs = import inputs.nixpkgs {\n      inherit system;\n\n      overlays = [\n        gen-luarc.overlays.default\n        neorocks.overlays.default\n\n        (final: prev: {\n          lib = prev.lib // import ./lib.nix {inherit pkgs;};\n\n          # NOTE: I would have used callPackage for easy overriding, but\n          # this changes the type and *-to-json fails later. To be figured out.\n          luarc-with-dependencies = final.lib.callPackageNoOverridable ./luarc.nix {inherit self;};\n          installed-dependencies = final.lib.callPackageNoOverridable ./installed-dependencies.nix {inherit self;};\n        })\n      ];\n    };\n  };\n}\n"
  },
  {
    "path": "nix/overlays/installed-dependencies.nix",
    "content": "{\n  lib,\n  runCommand,\n  lua51Packages,\n  wget,\n  self,\n}: let\n  # Temporarily remove the parsers due to installation problems on Nix. Causes the integration test to fail for now.\n  dependencies = builtins.removeAttrs (builtins.fromJSON (builtins.readFile \"${self}/res/deps.json\")) [\"tree-sitter-norg\" \"tree-sitter-norg-meta\"];\nin\n  runCommand \"install-neorg-dependencies\" {\n    nativeBuildInputs = [lua51Packages.luarocks wget];\n    outputHashAlgo = \"sha256\";\n    outputHashMode = \"recursive\";\n    outputHash = \"sha256-tvMqTgTshVApj3muQyvwj+as/8b7tw1r0BlCTpMlGuU=\";\n  } ''\n    mkdir $PWD/home\n    export HOME=$PWD/home\n    mkdir -p $out/luarocks\n\n    ${lib.concatStrings (lib.mapAttrsToList (name: version:\n      ''\n        luarocks install --tree=\"$out/luarocks\" --force-lock --local ${name} ${version}\n        luarocks download ${name} ${version}\n      ''\n      + \"\\n\")\n    dependencies)}\n\n    mv *.src.rock -t $out/luarocks\n    luarocks-admin make-manifest --lua-version 5.1 $out/luarocks\n    rm $out/luarocks/index.html\n  ''\n"
  },
  {
    "path": "nix/overlays/lib.nix",
    "content": "{pkgs}: rec {\n  callPackageNoOverridableWith = originalAttrs: fun: additionalAttrs: let\n    f =\n      if pkgs.lib.isFunction fun\n      then fun\n      else import fun;\n    attrs = builtins.intersectAttrs (pkgs.lib.functionArgs f) originalAttrs;\n  in\n    f (attrs // additionalAttrs);\n\n  callPackageNoOverridable = callPackageNoOverridableWith pkgs;\n}\n"
  },
  {
    "path": "nix/overlays/luarc.nix",
    "content": "{\n  callPackage,\n  installed-dependencies,\n  lib,\n  lua51Packages,\n  mk-luarc,\n  self,\n}: let\n  luarc = mk-luarc {};\nin\n  lib.recursiveUpdate luarc\n  {\n    Lua.workspace.library = luarc.Lua.workspace.library ++ [\"${installed-dependencies}/luarocks/share/lua/5.1/\"];\n  }\n"
  },
  {
    "path": "nix/packages/default.nix",
    "content": "{pkgs, ...}: {\n  packages.integration-test = pkgs.callPackage ./integration-test.nix {};\n}\n"
  },
  {
    "path": "nix/packages/integration-test.nix",
    "content": "{\n  writeScript,\n  writeShellApplication,\n  neovim-unwrapped,\n  tree-sitter,\n  lua5_1,\n  wget,\n}: let\n  kickstart-config =\n    writeScript \"kickstart.lua\"\n    ''\n      -- Adapted from https://github.com/folke/lazy.nvim#-installation\n\n      -- Install lazy.nvim\n      local lazypath = vim.fn.stdpath(\"data\") .. \"/lazy/lazy.nvim\"\n      if not (vim.uv or vim.loop).fs_stat(lazypath) then\n      \tvim.fn.system({\n      \t\t\"git\",\n      \t\t\"clone\",\n      \t\t\"--filter=blob:none\",\n      \t\t\"https://github.com/folke/lazy.nvim.git\",\n      \t\t\"--branch=stable\", -- latest stable release\n      \t\tlazypath,\n      \t})\n      end\n      vim.opt.rtp:prepend(lazypath)\n\n      -- Set up both the traditional leader (for keymaps) as well as the local leader (for norg files)\n      vim.g.mapleader = \" \"\n      vim.g.maplocalleader = \",\"\n\n      require(\"lazy\").setup({\n      \t{\n      \t\t\"rebelot/kanagawa.nvim\", -- neorg needs a colorscheme with treesitter support\n      \t\tconfig = function()\n      \t\t\t\tvim.cmd.colorscheme(\"kanagawa\")\n      \t\tend,\n      \t},\n      \t{\n      \t\t\"nvim-treesitter/nvim-treesitter\",\n      \t\tbuild = \":TSUpdate\",\n      \t\topts = {\n      \t\t\tensure_installed = { \"c\", \"lua\", \"vim\", \"vimdoc\", \"query\" },\n      \t\t\thighlight = { enable = true },\n      \t\t},\n      \t\tconfig = function(_, opts)\n      \t\t\trequire(\"nvim-treesitter.configs\").setup(opts)\n      \t\tend,\n      \t},\n      \t{\n      \t\t\t\"vhyrro/luarocks.nvim\",\n      \t\t\tpriority = 1000,\n      \t\t\tconfig = true,\n      \t},\n      \t{\n      \t\t\"nvim-neorg/neorg\",\n      \t\tdependencies = { \"luarocks.nvim\" },\n      \t\tconfig = function()\n      \t\t\trequire(\"neorg\").setup {\n      \t\t\t\tload = {\n      \t\t\t\t\t[\"core.defaults\"] = {},\n      \t\t\t\t\t[\"core.concealer\"] = {},\n      \t\t\t\t\t[\"core.dirman\"] = {\n      \t\t\t\t\t\tconfig = {\n      \t\t\t\t\t\t\tworkspaces = {\n      \t\t\t\t\t\t\t\tnotes = \"~/notes\",\n      \t\t\t\t\t\t\t},\n      \t\t\t\t\t\t\tdefault_workspace = \"notes\",\n      \t\t\t\t\t\t},\n      \t\t\t\t\t},\n      \t\t\t\t},\n      \t\t\t}\n\n      \t\t\tvim.cmd.e(\"success\")\n\n      \t\t\tvim.wo.foldlevel = 99\n      \t\t\tvim.wo.conceallevel = 2\n      \t\tend,\n      \t}\n      })\n    '';\nin\n  writeShellApplication {\n    name = \"neorg-integration-test\";\n\n    runtimeInputs = [\n      neovim-unwrapped\n      tree-sitter\n      lua5_1\n      wget\n      kickstart-config\n    ];\n\n    text = ''\n      export NVIM_APPNAME=\"nvim-neorg\"\n\n      echo \"* Hello World!\" > example.norg\n\n      nvim --headless -u ${kickstart-config} example.norg -c wq\n\n      rm example.norg\n\n      if [ ! -f success ]; then\n      \techo \"Integration test failed!\"\n      \texit 1\n      fi\n\n      rm success\n    '';\n  }\n"
  },
  {
    "path": "nix/shells/default.nix",
    "content": "{pkgs, ...}: {\n  devShells.default = pkgs.mkShell {\n    name = \"neorg devShell\";\n\n    shellHook = ''\n      ln -fs ${pkgs.luarc-to-json pkgs.luarc-with-dependencies} .luarc.json\n    '';\n\n    packages = with pkgs; [\n      lua-language-server\n      stylua\n      lua51Packages.luacheck\n      nil\n      lua5_1\n    ];\n  };\n}\n"
  },
  {
    "path": "queries/norg/folds.scm",
    "content": "(ranged_verbatim_tag) @fold\n\n[\n    (heading1)\n    (heading2)\n    (heading3)\n    (heading4)\n    (heading5)\n    (heading6)\n] @fold\n\n[\n    (unordered_list1)\n    (unordered_list2)\n    (unordered_list3)\n    (unordered_list4)\n    (unordered_list5)\n    (unordered_list6)\n] @fold\n\n[\n    (ordered_list1)\n    (ordered_list2)\n    (ordered_list3)\n    (ordered_list4)\n    (ordered_list5)\n    (ordered_list6)\n] @fold\n"
  },
  {
    "path": "queries/norg/highlights.scm",
    "content": "(ranged_verbatim_tag\n  (\"_prefix\") @neorg.tags.ranged_verbatim.begin\n  name: (tag_name\n          [(word) @neorg.tags.ranged_verbatim.name.word\n           (\"_delimiter\") @neorg.tags.ranged_verbatim.name.delimiter]) @neorg.tags.ranged_verbatim.name\n  (tag_parameters\n    (tag_param) @neorg.tags.ranged_verbatim.parameters.word)? @neorg.tags.ranged_verbatim.parameters)\n\n(ranged_verbatim_tag_end\n    (\"_prefix\") @neorg.tags.ranged_verbatim.end\n    (\"_name\") @neorg.tags.ranged_verbatim.name.word)\n\n(ranged_verbatim_tag\n  (\"_prefix\")\n  name: (tag_name) @neorg.tags.ranged_verbatim.name\n  (#eq? @neorg.tags.ranged_verbatim.name \"comment\")\n  content: (ranged_verbatim_tag_content)? @neorg.tags.comment.content)\n\n(paragraph\n  (strong_carryover_set\n    (strong_carryover\n      name: (tag_name) @_name\n      (#eq? @_name \"comment\")))\n  (paragraph_segment) @neorg.tags.comment.content)\n\n(strong_carryover\n  (\"_prefix\" @neorg.tags.carryover.begin)\n  name: (tag_name\n          [(word) @neorg.tags.carryover.name.word\n           (\"_delimiter\") @neorg.tags.carryover.name.delimiter]) @neorg.tags.carryover.name\n    (tag_parameters\n      (tag_param) @neorg.tags.carryover.parameters.word)? @neorg.tags.carryover.parameters) @neorg.tags.carryover\n\n; Trailing Modifier\n(\"_trailing_modifier\") @neorg.modifiers.trailing\n\n; Link Modifier\n(link_modifier) @neorg.modifiers.link\n\n; Links\n(link\n  (link_location\n    (\"_begin\") @neorg.links.location.delimiter\n    [((\"_begin\") @neorg.links.file.delimiter\n        file: (link_file_text) @neorg.links.file\n        (\"_end\") @neorg.links.file.delimiter)\n     ((link_target_url) ; Doesn't require a highlight since it's a 0-width node\n        (paragraph) @neorg.links.location.url)\n     ((link_target_generic) @neorg.links.location.generic.prefix\n        (paragraph) @neorg.links.location.generic)\n     ((link_target_external_file) @neorg.links.location.external_file.prefix\n        (paragraph) @neorg.links.location.external_file)\n     ((link_target_definition) @neorg.links.location.definition.prefix\n        (paragraph) @neorg.links.location.definition)\n     ((link_target_footnote) @neorg.links.location.footnote.prefix\n        (paragraph) @neorg.links.location.footnote)\n     ((link_target_heading1) @neorg.links.location.heading.1.prefix\n        (paragraph) @neorg.links.location.heading.1)\n     ((link_target_heading2) @neorg.links.location.heading.2.prefix\n        (paragraph) @neorg.links.location.heading.2)\n     ((link_target_heading3) @neorg.links.location.heading.3.prefix\n        (paragraph) @neorg.links.location.heading.3)\n     ((link_target_heading4) @neorg.links.location.heading.4.prefix\n        (paragraph) @neorg.links.location.heading.4)\n     ((link_target_heading5) @neorg.links.location.heading.5.prefix\n        (paragraph) @neorg.links.location.heading.5)\n     ((link_target_heading6) @neorg.links.location.heading.6.prefix\n        (paragraph) @neorg.links.location.heading.6)\n     ((link_target_wiki) @neorg.links.location.wiki.prefix\n        (paragraph) @neorg.links.location.wiki)\n     ((link_target_timestamp) @neorg.links.location.timestamp.prefix\n        (paragraph) @neorg.links.location.timestamp)]\n    (\"_end\") @neorg.links.location.delimiter)\n  (link_description\n    (\"_begin\") @neorg.links.description.delimiter\n    text: (paragraph) @neorg.links.description\n    (\"_end\") @neorg.links.description.delimiter)?)\n\n; Anchors\n(anchor_declaration\n  (link_description\n    (\"_begin\") @neorg.anchors.declaration.delimiter\n    text: (paragraph) @neorg.anchors.declaration\n    (\"_end\") @neorg.anchors.declaration.delimiter))\n\n(anchor_definition\n    (link_description\n        (\"_begin\") @neorg.anchors.definition.delimiter\n        text: (paragraph) @neorg.anchors.declaration\n        (\"_end\") @neorg.anchors.definition.delimiter) @neorg.anchors\n    (link_location\n      (\"_begin\") @neorg.links.location.delimiter\n      [((\"_begin\") @neorg.links.file.delimiter\n          file: (link_file_text) @neorg.links.file\n          (\"_end\") @neorg.links.file.delimiter)\n       ((link_target_url) ; Doesn't require a highlight since it's a 0-width node\n          (paragraph) @neorg.links.location.url)\n       ((link_target_generic) @neorg.links.location.generic.prefix\n          (paragraph) @neorg.links.location.generic)\n       ((link_target_external_file) @neorg.links.location.external_file.prefix\n          (paragraph) @neorg.links.location.external_file)\n       ((link_target_definition) @neorg.links.location.definition.prefix\n          (paragraph) @neorg.links.location.definition)\n       ((link_target_footnote) @neorg.links.location.footnote.prefix\n          (paragraph) @neorg.links.location.footnote)\n       ((link_target_heading1) @neorg.links.location.heading.1.prefix\n          (paragraph) @neorg.links.location.heading.1)\n       ((link_target_heading2) @neorg.links.location.heading.2.prefix\n          (paragraph) @neorg.links.location.heading.2)\n       ((link_target_heading3) @neorg.links.location.heading.3.prefix\n          (paragraph) @neorg.links.location.heading.3)\n       ((link_target_heading4) @neorg.links.location.heading.4.prefix\n          (paragraph) @neorg.links.location.heading.4)\n       ((link_target_heading5) @neorg.links.location.heading.5.prefix\n          (paragraph) @neorg.links.location.heading.5)\n       ((link_target_heading6) @neorg.links.location.heading.6.prefix\n          (paragraph) @neorg.links.location.heading.6)\n       ((link_target_wiki) @neorg.links.location.wiki.prefix\n          (paragraph) @neorg.links.location.wiki)\n       ((link_target_timestamp) @neorg.links.location.timestamp.prefix\n          (paragraph) @neorg.links.location.timestamp)]\n      (\"_end\") @neorg.links.location.delimiter))\n\n; Headings\n(heading1\n  (heading1_prefix) @neorg.headings.1.prefix\n  title: (paragraph_segment) @neorg.headings.1.title)\n(heading2\n  (heading2_prefix) @neorg.headings.2.prefix\n  title: (paragraph_segment) @neorg.headings.2.title)\n(heading3\n  (heading3_prefix) @neorg.headings.3.prefix\n  title: (paragraph_segment) @neorg.headings.3.title)\n(heading4\n  (heading4_prefix) @neorg.headings.4.prefix\n  title: (paragraph_segment) @neorg.headings.4.title)\n(heading5\n  (heading5_prefix) @neorg.headings.5.prefix\n  title: (paragraph_segment) @neorg.headings.5.title)\n(heading6\n  (heading6_prefix) @neorg.headings.6.prefix\n  title: (paragraph_segment) @neorg.headings.6.title)\n\n; Display errors\n(ERROR) @neorg.error\n\n; Definitions\n(single_definition\n  (single_definition_prefix) @neorg.definitions.prefix\n  title: (paragraph_segment) @neorg.definitions.title\n  content: [(_) \"_paragraph_break\"]* @neorg.definitions.content)\n(multi_definition\n  (multi_definition_prefix) @neorg.definitions.prefix\n  title: (paragraph_segment) @neorg.definitions.title\n  content: [(_) \"_paragraph_break\"]* @neorg.definitions.content\n  end: (multi_definition_suffix) @neorg.definitions.suffix)\n\n; Footnotes\n(single_footnote\n  (single_footnote_prefix) @neorg.footnotes.prefix\n  title: (paragraph_segment) @neorg.footnotes.title\n  content: [(_) \"_paragraph_break\"]* @neorg.footnotes.content)\n(multi_footnote\n  (multi_footnote_prefix) @neorg.footnotes.prefix\n  title: (paragraph_segment) @neorg.footnotes.title\n  content: [(_) \"_paragraph_break\"]* @neorg.footnotes.content\n  end: (multi_footnote_suffix) @neorg.footnotes.suffix)\n\n; Escape sequences (\\char)\n(escape_sequence) @neorg.modifiers.escape\n\n; Detached Modifier extensions\n(detached_modifier_extension (todo_item_undone)) @neorg.todo_items.undone\n(detached_modifier_extension (todo_item_done)) @neorg.todo_items.done\n(detached_modifier_extension (todo_item_pending)) @neorg.todo_items.pending\n(detached_modifier_extension (todo_item_on_hold)) @neorg.todo_items.on_hold\n(detached_modifier_extension (todo_item_cancelled)) @neorg.todo_items.cancelled\n(detached_modifier_extension (todo_item_uncertain)) @neorg.todo_items.uncertain\n(detached_modifier_extension (todo_item_urgent)) @neorg.todo_items.urgent\n(detached_modifier_extension (todo_item_recurring)) @neorg.todo_items.recurring\n\n; ; Unordered lists\n[(unordered_list1_prefix)\n (unordered_list2_prefix)\n (unordered_list3_prefix)\n (unordered_list4_prefix)\n (unordered_list5_prefix)\n (unordered_list6_prefix)] @neorg.lists.unordered.prefix\n\n; Ordered lists\n[(ordered_list1_prefix)\n (ordered_list2_prefix)\n (ordered_list3_prefix)\n (ordered_list4_prefix)\n (ordered_list5_prefix)\n (ordered_list6_prefix)] @neorg.lists.ordered.prefix\n\n; Quotes\n(quote1\n  (quote1_prefix) @neorg.quotes.1.prefix\n  content: (paragraph) @neorg.quotes.1.content)\n(quote2\n  (quote2_prefix) @neorg.quotes.2.prefix\n  content: (paragraph) @neorg.quotes.2.content)\n(quote3\n  (quote3_prefix) @neorg.quotes.3.prefix\n  content: (paragraph) @neorg.quotes.3.content)\n(quote4\n  (quote4_prefix) @neorg.quotes.4.prefix\n  content: (paragraph) @neorg.quotes.4.content)\n(quote5\n  (quote5_prefix) @neorg.quotes.5.prefix\n  content: (paragraph) @neorg.quotes.5.content)\n(quote6\n  (quote6_prefix) @neorg.quotes.6.prefix\n  content: (paragraph) @neorg.quotes.6.content)\n\n; Paragraph Delimiters\n(strong_paragraph_delimiter) @neorg.delimiters.strong\n(weak_paragraph_delimiter) @neorg.delimiters.weak\n(horizontal_line) @neorg.delimiters.horizontal_line\n\n; Markup\n(bold [\"_open\" \"_close\"] @neorg.markup.bold.delimiter) @neorg.markup.bold\n(italic [\"_open\" \"_close\"] @neorg.markup.italic.delimiter) @neorg.markup.italic\n(strikethrough [\"_open\" \"_close\"] @neorg.markup.strikethrough.delimiter) @neorg.markup.strikethrough\n(underline [\"_open\" \"_close\"] @neorg.markup.underline.delimiter) @neorg.markup.underline\n(spoiler [\"_open\" \"_close\"] @neorg.markup.spoiler.delimiter) @neorg.markup.spoiler\n(verbatim [\"_open\" \"_close\"] @neorg.markup.verbatim.delimiter) @neorg.markup.verbatim\n(superscript [\"_open\" \"_close\"] @neorg.markup.superscript.delimiter) @neorg.markup.superscript\n(subscript [\"_open\" \"_close\"] @neorg.markup.subscript.delimiter) @neorg.markup.subscript\n(inline_comment [\"_open\" \"_close\"] @neorg.markup.inline_comment.delimiter) @neorg.markup.inline_comment\n(inline_math [\"_open\" \"_close\"] @neorg.markup.inline_math.delimiter) @neorg.markup.inline_math\n(inline_macro [\"_open\" \"_close\"] @neorg.markup.variable.delimiter) @neorg.markup.variable\n\n; Free-form Markup\n[(free_form_open)\n (free_form_close)] @neorg.markup.free_form_delimiter\n\n(superscript\n  (subscript) @neorg.error\n  (#set! priority 300))\n(subscript\n  (superscript) @neorg.error\n  (#set! priority 300))\n\n; Comments\n(inline_comment) @comment\n\n; Conceals\n(\n    [\n        \"_open\"\n        \"_close\"\n        \"_trailing_modifier\"\n        (link_modifier)\n        (free_form_open)\n        (free_form_close)\n    ] @conceal\n    (#set! conceal \"\")\n)\n\n(\n    [\n        (link_description\n            [\n                \"_begin\"\n                type: (_)\n                \"_end\"\n            ] @conceal\n        )\n        (link_location\n            [\n                \"_begin\"\n                type: (_)\n                \"_end\"\n            ] @conceal\n        )\n        (link\n            (link_location) @conceal\n            (link_description)\n        )\n    ]\n    (#set! conceal \"\")\n)\n\n(\n    [\n        (anchor_definition\n            (link_description)\n            (link_location) @conceal\n        )\n    ]\n    (#set! conceal \"\")\n)\n\n(\n    (escape_sequence_prefix) @conceal\n    (#set! conceal \"\")\n)\n\n; Spell\n(paragraph_segment) @spell\n"
  },
  {
    "path": "queries/norg/injections.scm",
    "content": "; Injection for code blocks\n(ranged_verbatim_tag (tag_name) @_tagname (tag_parameters .(tag_param) @injection.language) (ranged_verbatim_tag_content) @injection.content (#any-of? @_tagname \"code\" \"embed\"))\n(ranged_verbatim_tag (tag_name) @_tagname (tag_parameters)? (ranged_verbatim_tag_content) @injection.content (#eq? @_tagname \"math\") (#set! injection.language \"latex\"))\n\n(\n    (inline_math) @injection.content\n    (#offset! @injection.content 0 1 0 -1)\n    (#set! injection.language \"latex\")\n)\n\n(ranged_verbatim_tag (tag_name) @_tagname (ranged_verbatim_tag_content) @injection.content (#eq? @_tagname \"document.meta\") (#set! injection.language \"norg_meta\"))\n"
  },
  {
    "path": "queries/norg_meta/highlights.scm",
    "content": "; Regular keys and values\n(key) @neorg.tags.ranged_verbatim.document_meta.key\n(string) @neorg.tags.ranged_verbatim.document_meta.string\n(number) @neorg.tags.ranged_verbatim.document_meta.number\n\n; Literals\n\"{\" @neorg.tags.ranged_verbatim.document_meta.object.bracket\n\"}\" @neorg.tags.ranged_verbatim.document_meta.object.bracket\n\"[\" @neorg.tags.ranged_verbatim.document_meta.array.bracket\n\"]\" @neorg.tags.ranged_verbatim.document_meta.array.bracket\n\n; Special Highlights\n(pair\n    (key) @_key\n    (string) @neorg.tags.ranged_verbatim.document_meta.title\n    (#eq? @_key \"title\")\n)\n\n(pair\n    (key) @_key\n    (string) @neorg.tags.ranged_verbatim.document_meta.description\n    (#eq? @_key \"description\")\n)\n\n(pair\n    (key) @_key\n    [\n        (string) @neorg.tags.ranged_verbatim.document_meta.authors\n        (array\n            (string) @neorg.tags.ranged_verbatim.document_meta.authors\n        )\n    ]\n    (#eq? @_key \"authors\")\n)\n\n(pair\n    (key) @_key\n    [\n        (string) @neorg.tags.ranged_verbatim.document_meta.categories\n        (array\n            (string) @neorg.tags.ranged_verbatim.document_meta.categories\n        )\n    ]\n    (#eq? @_key \"categories\")\n)\n\n(pair\n    (key) @_key\n    (string) @neorg.tags.ranged_verbatim.document_meta.created\n    (#eq? @_key \"created\")\n)\n\n(pair\n    (key) @_key\n    (string) @neorg.tags.ranged_verbatim.document_meta.updated\n    (#eq? @_key \"updated\")\n)\n\n(pair\n    (key) @_key\n    (string) @neorg.tags.ranged_verbatim.document_meta.version\n    (#eq? @_key \"version\")\n)\n"
  },
  {
    "path": "queries/norg_meta/indents.scm",
    "content": "(array) @indent.begin\n(object) @indent.begin\n(ERROR) @indent.begin\n\n[\n    \"]\"\n    \"}\"\n] @indent.branch\n"
  },
  {
    "path": "res/deps.json",
    "content": "{\n    \"nvim-nio\": \"1.9.3\",\n    \"lua-utils.nvim\": \"1.0.2\",\n    \"plenary.nvim\": \"0.1.4\",\n    \"nui.nvim\": \"0.3.0\",\n    \"pathlib.nvim\": \"2.2.2\"\n}\n"
  },
  {
    "path": "res/wiki/static/Cookbook.md",
    "content": "# Neorg Cookbook\n\nThis document details a bunch of snippets and prebuilt configurations for individual modules\nfor a just works experience.\n\n> [!TIP]\n> If you're looking for a quickstart setup to get you started with Neorg, check out the [kickstart page](https://github.com/nvim-neorg/neorg/wiki/Kickstart) instead.\n\n## Completion with `nvim-cmp`\n\n### Requirements\n- [`nvim-cmp`](https://github.com/hrsh7th/nvim-cmp)\n\n### Steps\n\n1. In your Neorg configuration:\n   ```lua\n   load = {\n       [\"core.defaults\"] = {},\n       [\"core.completion\"] = {\n           config = {\n               engine = \"nvim-cmp\",\n           }\n       },\n       [\"core.integrations.nvim-cmp\"] = {},\n   }\n   ```\n\n2. In your `nvim-cmp` configuration:\n   ```lua\n   sources = cmp.config.sources({\n       -- ... your other sources here\n       { name = \"neorg\" },\n   })\n   ```\n\n## Latex Rendering\n\n### Requirements\n- A terminal with kitty graphics protocol support (kitty/ghostty)\n- [`image.nvim`](https://github.com/3rd/image.nvim) installed and set up\n\n### Steps\n\n1. In your Neorg configuration:\n   ```lua\n   load = {\n       [\"core.integrations.image\"] = {},\n       [\"core.latex.renderer\"] = {},\n   }\n   ```\n\n2. Place some maths within maths blocks (`$| ... |$`):\n   ```norg\n   $|Hello, \\LaTeX|$\n   ```\n3. Run `:Neorg render-latex`.\n"
  },
  {
    "path": "res/wiki/static/Dependencies.md",
    "content": "TODO: Rewrite parts of this to be more clear (remove the quotes).\n\n# Understanding Neorg dependencies\n\nNeorg depends on a number of moving parts and will only continue to accrue more dependencies to deliver on many of the features in it's ROADMAP. Many of these features, like tree-sitter support in Neovim, are still marked experimental in their parent projects. This document is intended to help Neorg users navigate these dependencies, but users are always encouraged to refer to the parent projects for appropriate documentation.\n\n\n## Neovim\n\n**Neorg is a Neovim plugin, it is not an app.** Neorg attempts to utilize Neovim features when it makes sense to do so in order to provide a familiar editing environment that corresponds to Neovim and Neovim plugin conventions. However, the project reserves the right to break from those conventions in situations where the project determines that doing so will enable better execution on the project vision.\n\n\n### Terminal Support\n\nIf you are running neovim in a terminal emulator, be aware the terminal emulators are weird things. A particularly good description of terminals is provided by [https://wezfurlong.org/wezterm/what-is-a-terminal.html](https://wezfurlong.org/wezterm/what-is-a-terminal.html). A concise statement is that terminal emulators _emulate old terminal hardware, so that the kernel can pretend it is still talking to old terminal hardware_. Over the years, various emulators have wanted to offer capabilities above and beyond what those original terminals offered. Command line programs which wanted to use these extended capabilities needed to know whether or not they were on a supporting terminal, since attempting to use these features without appropriate support might cause bad behavior on terminals that did not support those features. Generally, a program can check the `TERM` environment variable and then lookup the relevant `termcap` or `terminfo` entry in the file system database of `term` files. This requires the system to have an appropriate `term` file installed in the relevant location. Alternatively, some terminal emulators support using escape sequences to query terminal capabilities via the XTVERSION and XTGETTCAP escape sequences, which directly query the emulator.\n\nIf neovim is unable to determine the correct set of terminal capabilities, it may choose conservative defaults that prevent rendering of certain text styles in the terminal, such as italic, strikethrough, undercurl, etc. **For example, `screen` and `tmux` frequently advertise a `TERM` with limited capabilities because they cannot guarantee what kind of terminal emulator will attach to them.**\n\n*If your terminal font settings do not include appropriate rendering styles, this may also prevent you from seeing text rending in the way you expect.*\n\nThere are a couple of ways that may help determine if terminal support is limiting the display of characters in neovim.\n\n\n### Test outside of neovim\n\nCheck the output of the following:\n\n```bash\necho -e \"\\e[1mbold\\e[0m\"\necho -e \"\\e[3mitalic\\e[0m\"\necho -e \"\\e[3m\\e[1mbold italic\\e[0m\"\necho -e \"\\e[4munderline\\e[0m\"\necho -e \"\\e[9mstrikethrough\\e[0m\"\necho -e \"\\x1B[31mred\\e[0m\"\n\nprintf \"\\x1b[58:2::255:0:0m\\x1b[4:1msingle\\x1b[4:2mdouble\\x1b[4:3mcurly\\x1b[4:4mdotted\\x1b[4:5mdashed\\x1b[0m\\n\"\n```\n\nIf this does not produce what you expect, there is a terminal or a font problem. If it works, but does not work in Neovim, then this may either be a problem with advertising/detecting terminal capabilities or setting highlight groups.\n\n\n#### Windows ConPTY does not support undercurl\n\nWindows terminal emulators almost all use ConPTY, which strips out certain escape sequences, including those used for more advanced underlines. There is an open ticket for this feature in `microsoft/terminal` but it is unknown when it will become a priority to address given that terminal support is not generally a Microsoft business priority.\n\n\n### Check neovim diagnostics\n\n`nvim -V3log` will produce a file named `log`. After exiting, check the `log` file for a section beginning with `--- Terminal info ---`.\n\n`:checkhealth` will run some neovim diagnostics and open them in a new `:h tabpage`. You can use `:q` to exit the tab. This will include a section on the `terminal` and in some cases identifies `TERM` settings which may cause problems and how to address them\n\n#### Check that neovim is able to render these characters in other contexts\n\n1. `:highlight mytest cterm=italic gui=italic` will create a highlight group named `mytest`.\n2. `:highlight mytest` will show a line that looks like `mytest /xxx/ cterm=italic gui=italic`. The `xxx` should be rendered _in the format specified_ (in this case italics). If not, Neovim is likely not getting the correct terminal capabilities.\n3. Test for any other format which you are concerned is not appearing correctly.\n\n\n#### Check that highlight groups are getting assigned correctly and have an appropriate definition\n\nSee [Tree-sitter](#tree-sitter) and [Colorschemes](#colorschemes) for this.\n\n\n### Ensure your fonts support bold/italic/underline\n\nUsually terminal emulators automatically set up bold/italic/underline fonts, but these sometimes may fail.\nKitty is the most popularly used terminal emulator and some fonts are known to not have detectable \"auto\" bold fonts (for example Source Code Pro).\nTo fix this, go to your terminal emulator's configuration and manually set the bold and italic fonts (e.g. `Source Code Pro Bold` and `Source Code Pro Italic`).\nFor kitty, this means:\n```\nbold_font        Source Code Pro Bold\nitalic_font      Source Code Pro Italic\nbold_italic_font auto\n```\n\n## Tree-sitter\n\nParsing of `.norg` documents in `neorg` is primarily handled by the `tree-sitter` library.\n\n\n### Tree-sitter support in neovim\n\nTree-sitter functionality is provided natively by Neovim, **but native support is not the same as supported with no configuration**. Neovim is only responsible for loading a binary **\\*.so** file, providing facilities for executing queries against the parse tree, and for creating highlight groups and indent rules based on those queries when they are defined in an appropriate file location. Supplying these parser and query files is the responsibility of the user or may be delegated by the user to a plugin. See `:h treesitter-parsers` for more details on how Neovim locates its tree-sitter parsers and `:h treesitter-query` and `:h treesitter-highlight` for details on the runtimepath files like `queries/*/highlights.scm`.\n\n### Nvim-treesitter plugin\n\nTo make things easier the [`nvim-treesitter`](https://github.com/nvim-treesitter/nvim-treesitter) plugin provides best-effort configuration support for downloading tree-sitter grammars (source code) from their repositories, compiling them automatically, and placing them in the correct paths along with appropriate highlight and other queries.\n\n`nvim-treesitter`, in turn, outsources its language-specific efforts to repositories of the form `tree-sitter-<lang>`. See the project repo for more details.\n\n**In order for Neorg to work properly, these features must be enabled when you load and `setup()` nvim-treesitter, especially the highlight module.** See [https://github.com/nvim-treesitter/nvim-treesitter#modules](https://github.com/nvim-treesitter/nvim-treesitter#modules) for more details on how to do this. Within Neovim, you can run `:TSConfigInfo` to ensure that `modules.enable.highlight` has the expected value.\n\n\n#### C/C++ Toolchain\n\nIn order for `nvim-treesitter` to properly build the `tree-sitter-norg` parser, it requires an appropriate compiler toolchain. Ensure that the `CC` environment variable points to a compiler that has C++14 support.\n\n##### MacOS C/C++ Toolchain often outdated\n\nThe compiler bundled with many editions of MacOS lacks the appropriate level of support. You can run Neovim like so: `CC=/path/to/newer/compiler nvim -c \"TSInstallSync norg\"` in your shell of choice to install the Neorg parser with a newer compiler. You may also want to export the CC variable in general: `export CC=/path/to/newer/compiler`.\n\n\n### Tree-sitter-norg\n\nAs many of the features of `neorg` depend on proper `tree-sitter`-based parsing of the `.norg` document, it is important to ensure that you update `neorg` and the `tree-sitter-norg` parser at the same time.\n\nThe manual way to do this is that after your `git pull` to update the `neorg` plugin, you call `nvim -c \"TSInstallSync norg\"`.\n\nThe easiest way to do this is by using a [Plugin Manager](#plugin-manager).\n\n## Plugin Manager\n\nPlugin managers reduce the burden of maintaining a (Neo)vim configuration by providing mechanisms to automate repetitive tasks, most notably for updating groups of plugins and ensuring that plugin updates trigger additional commands, such as updating the `tree-sitter-norg` parser.\n\nMany users also appreciate the ability to lazy-load plugins and reduce Neovim starting time; however, lazy loading complicates the loading order of plugins and this is frequently misconfigured. **Therefore, we recommend lazy loading be disabled when troubleshooting an issue to ensure that packages are loading in the order you believe.** In many plugin managers, this includes keys like `ft`, `cmd`, `event`, etc.\n\nImportantly, plugin managers are not meant to prevent users from understanding [Neovim](#neovim). Neovim understands how to find plugin files on the basis of its `:h runtimepath`. Plugin managers add appropriate entries to that path when it is time to load a plugin.\n\nThey also make it possible to express a `Neovim` configuration which has multiple plugins (and therefore spans many files and directories) within a single file for the purposes of sharing configurations.\n\n\n### Lazy.nvim\n\nHere is an example minimal `init.lua` which utilizes ['lazy.nvim'](https://github.com/folke/lazy.nvim):\n\n```lua\n-- bootstrap lazy.nvim\nlocal lazypath = vim.fn.stdpath(\"data\") .. \"/lazy/lazy.nvim\"\nif not vim.loop.fs_stat(lazypath) then\n    vim.fn.system({\n        \"git\",\n        \"clone\",\n        \"--filter=blob:none\",\n        \"https://github.com/folke/lazy.nvim.git\",\n        \"--branch=stable\", -- latest stable release\n        lazypath,\n    })\nend\nvim.opt.rtp:prepend(lazypath)\n\nrequire('lazy').setup({\n    {\n        \"nvim-neorg/neorg\",\n        build = \":Neorg sync-parsers\",\n        opts = {\n            load = {\n                [\"core.defaults\"] = {}, -- Loads default behaviour\n                [\"core.concealer\"] = {}, -- Adds pretty icons to your documents\n                [\"core.dirman\"] = { -- Manages Neorg workspaces\n                    config = {\n                        workspaces = {\n                            notes = \"~/notes\",\n                        },\n                        default_workspace = \"notes\",\n                    },\n                },\n            },\n        },\n        dependencies = {\n            { \"nvim-lua/plenary.nvim\", },\n            {\n                -- YOU ALMOST CERTAINLY WANT A MORE ROBUST nvim-treesitter SETUP\n                -- see https://github.com/nvim-treesitter/nvim-treesitter\n                \"nvim-treesitter/nvim-treesitter\",\n                opts = {\n                    auto_install = true,\n                    highlight = {\n                        enable = true,\n                        additional_vim_regex_highlighting = false,\n                    },\n                },\n                config = function(_,opts)\n                    require('nvim-treesitter.configs').setup(opts)\n                end\n            },\n            { \"folke/tokyonight.nvim\", config=function(_,_) vim.cmd.colorscheme \"tokyonight-storm\" end,},\n        },\n    },\n})\n```\n\n### Packer.nvim\n\nI do not use Packer.nvim and cannot provide direct help for this package manager. However, it is common to see Packer problems which stem from `:PackerCompile` not being called after an update, resulting in incorrect things being cached.\n\n\n## Colorschemes\n\nA lightweight text markup plugin like `neorg` benefits from being able to display text-decorations like **bold** and _italic_, as well as highlighting headings in different colors. Neovim users often have colorschemes configured already, which may be among the default colorschemes bundled with Neovim or be one installed from an online source like Github.\n\nA full description of vim highlighting is best left to `:h highlight` and `:h syntax`. Suffice it to say that _syntax_ files use regular expressions to identify areas of text and mark them as belonging to some _highlight group_. Neovim extends this with `:h treesitter-highlight` to provide another mechanism of assigning _highlight groups_. `:h highlight` then allows users or colorschemes to define how highlight groups should appear.\n\nVim has defined a set of `:h highlight-groups` with `:h group-name` naming conventions which have been conserved for a long time. However, many plugins define their own highlight-groups to allow for more specific theming. Well-behaved plugins generally provide fallbacks which link to one of these conserved highlight-groups in case the colorscheme does not define an appearance for the plugin-specific highlight group. However, the conserved set is primarily defined with programming in mind. Thus, there is no highlight-group which is guaranteed to be **bold**. `nvim-treesitter` attempts to standardize a set of names for highlight-groups which provide expanded functionality, such as `@text.strong`, based on community consensus. See [https://github.com/nvim-treesitter/nvim-treesitter/blob/master/CONTRIBUTING.md](https://github.com/nvim-treesitter/nvim-treesitter/blob/master/CONTRIBUTING.md) for a full list of these groups.\n\n`neorg` uses many of these new conserved tree-sitter highlight groups as fallbacks for its plugin-specific highlight groups. Because this is a relatively new development, and because these groups are being promulgated by a plugin instead of Neovim core, many colorschemes **including the ones bundled with Neovim** do not support these new highlight groups. `:highlight @text.strong` will tell you the definition that Neovim currently has for that group. `tokyonight` and `kanagawa` are two themes which are known to support the relevant tree-sitter highlight-groups.\n\n\n## Concealing\n\nAlthough the purpose of a lightweight markup language is to produce documents that remain readable even in plain-text, it is often nice from a readability standpoint to be able to conceal the markup from view.\n\nNeorg, through it's `core.conceal` module, is designed to substitute many characters with more aesthetic choices, hide the multiple characters in e.g. headlines and lists, and conceal link URLs and other markup. It achieves this by setting the `:h conceal` argument on the highlight group. On its own, this only applies a `conceal` tag to the highlight group. Users must configure their `:h conceallevel` and `:h concealcursor` to actually hide the text, as the default `conceallevel` setting does not conceal any text. This is good default Neovim behavior since only people who are aware of the `conceallevel` options and functionality will have text hidden from them.\n\n\n### Ugly line wraps when concealing text\n\nThere is a known bug in how concealing interacts with wrapped lines. This is a long-standing vim and Neovim behavior with no easy fix since it touches on complex rendering and UI logic to allow performant and logical editing on a file from within a window which displays contents which differ from buffer contents.\n\nThere is a Neovim effort underway known as `anticonceal` which should hopefully address this bug. However, like all things, time is limited and priorities must be balanced and this is not an easy bug to address. Discussions around this bug have been ongoing for several years - if it is so pressing to your use case, implement a fix yourself or pay someone to do so at market rates.\n\nHaving said all that, as users of Neorg, we also regularly deal with this bug. Some suggested option of workflows that accommodate for this behavior include the following:\n1. Do not use conceal\n2. Use hard wrap (`:h formatting`) and manually insert line breaks so that the concealed text wraps in a manner that suits your requirements\n3. Use soft wrap (`:h wrap`) in combination with the anchors features described in the [norg-specs](https://github.com/nvim-neorg/norg-specs) to separate the link usage from its definition. This mitigates the rendering issues in Neovim by reducing the number of characters which must be concealed, resulting in better reflow behavior in most cases. Be cognizant that anchor names will need to be unique for linking to operate as expected.\n    - For a better editing experience, you may also be interested in `:h linebreak`, `:h breakindent` and `:h breakindentopt`.\n"
  },
  {
    "path": "res/wiki/static/Kickstart.md",
    "content": "<div align=\"center\">\n\n# Instantaneous Neorg Setup\n\nGet up and running with Neorg even with zero Neovim knowledge.\n\n</div>\n\n## Prerequisites\n\nTo use this configuration, a set of prerequisites must be fulfilled beforehand.\n\n1. Install one of the Nerd fonts, for example the Meslo Nerd Font from [Nerd Fonts](https://www.nerdfonts.com/font-downloads).\n2. Set your terminal font to the installed Nerd Font.\n3. Make sure you have git by running `git --version`.\n4. Ensure you have Luarocks installed on your system:\n   - **Windows**: install [lua for windows](https://github.com/rjpcomputing/luaforwindows/releases/tag/v5.1.5-52).\n   - **MacOS**: install via `brew install luarocks`.\n   - **`apk`**: `sudo apk add luarocks wget`\n   - **`apt`**: `sudo apt install luarocks`\n   - **`dnf`**: `sudo dnf install luarocks`\n   - **`pacman`**: `sudo pacman -Syu luarocks`\n\n## Troubleshooting\n\nIf you have any issues like bold/italic not rendering or highlights being improperly applied\nI encourage you to check out the [dependencies document](https://github.com/nvim-neorg/neorg/wiki/Dependencies) which explains troubleshooting steps\nfor different kinds of terminals.\n\nWith that, let's begin!\n\n## Creating our Init File\n \n- Open up a Neovim instance and run the following command: `:echo stdpath(\"config\")`.\n  This will return the path where Neovim expects your `init.lua` to exist.\n- Navigate to that directory (create it if it doesn't exist) and create a file\n  called `init.lua` there. On Linux this will likely be `~/.config/nvim/init.lua`.\n\n  Put the following into the `init.lua` file:\n  ```lua\n  -- Bootstrap lazy.nvim\n  local lazypath = vim.fn.stdpath(\"data\") .. \"/lazy/lazy.nvim\"\n  if not (vim.uv or vim.loop).fs_stat(lazypath) then\n    local lazyrepo = \"https://github.com/folke/lazy.nvim.git\"\n    local out = vim.fn.system({ \"git\", \"clone\", \"--filter=blob:none\", \"--branch=stable\", lazyrepo, lazypath })\n    if vim.v.shell_error ~= 0 then\n      vim.api.nvim_echo({\n        { \"Failed to clone lazy.nvim:\\n\", \"ErrorMsg\" },\n        { out, \"WarningMsg\" },\n        { \"\\nPress any key to exit...\" },\n      }, true, {})\n      vim.fn.getchar()\n      os.exit(1)\n    end\n  end\n  vim.opt.rtp:prepend(lazypath)\n  \n  -- Set up both the traditional leader (for keymaps) as well as the local leader (for norg files)\n  vim.g.mapleader = \" \"\n  vim.g.maplocalleader = \",\"\n  \n  -- Setup lazy.nvim\n  require(\"lazy\").setup({\n    spec = {\n      {\n        \"rebelot/kanagawa.nvim\", -- neorg needs a colorscheme with treesitter support\n        config = function()\n            vim.cmd.colorscheme(\"kanagawa\")\n        end,\n      },\n      {\n        \"nvim-treesitter/nvim-treesitter\",\n        version = \"0.9.3\",  -- the last supported version\n        build = \":TSUpdate\",\n        opts = {\n          ensure_installed = { \"c\", \"lua\", \"vim\", \"vimdoc\", \"query\" },\n          highlight = { enable = true },\n        },\n        config = function(_, opts)\n          require(\"nvim-treesitter.configs\").setup(opts)\n        end,\n      },\n      {\n        \"nvim-neorg/neorg\",\n        lazy = false,\n        version = \"*\",\n        config = function()\n          require(\"neorg\").setup {\n            load = {\n              [\"core.defaults\"] = {},\n              [\"core.concealer\"] = {},\n              [\"core.dirman\"] = {\n                config = {\n                  workspaces = {\n                    notes = \"~/notes\",\n                  },\n                  default_workspace = \"notes\",\n                },\n              },\n            },\n          }\n    \n          vim.wo.foldlevel = 99\n          vim.wo.conceallevel = 2\n        end,\n      },\n    },\n  })\n  ```\n- Close and reopen Neovim. Everything should just work!\n- Open up any `.norg` file and start typing! You may see an initial error, just hit enter a few times, after\n  which Neorg should immediately fix itself.\n"
  },
  {
    "path": "res/wiki/static/Philosophy.md",
    "content": "<div align=\"center\">\n\n# :rocket: Project Philosophy\n\n</div>\n\nNeorg is a project that stands on the shoulders of giants, namely `org-mode`, `markdown` and\npartially `asciidoc`.\n\nIf Neorg is a different approach to a similar problem that all other three systems have solved,\nwhy create yet another file format? Why go through all of the troubles?\n\n## Design Decisions\n\n> [!NOTE]\n> This page is a work-in-progress.\n> There is a design decisions document present in the norg file format [here](https://github.com/nvim-neorg/norg-specs/blob/main/design-decisions.norg) in the meantime.\n"
  },
  {
    "path": "res/wiki/static/Setup-Guide.md",
    "content": "<div align=\"center\">\n\n# Setting up Neorg\n\nA guide on configuring Neorg.\n\n</div>\n\n> [!NOTE]\n> If you're just getting started and have no idea why and where to use Neorg,\n> feel free to check out the [tutorial](https://github.com/nvim-neorg/neorg/wiki/Tutorial).\n\n## Where do I begin?\n\nNeorg, being a large organizational tool for many different types of people, needs to be\nextensible and highly configurable. It's for this reason that setting it up takes slightly\nlonger than your average Neovim plugin.\n\nBecause of this, learning to navigate the Neorg wiki as well as learning the basics of how the system\nworks will get you a very long way in making Neorg your own.\n\nThis guide assumes that you've completed the initial [setup process](https://github.com/nvim-neorg/neorg?tab=readme-ov-file#-installation) successfully and without errors.\n\n## Default Setup\n\nThe default configuration looks like follows:\n```lua\nrequire(\"neorg\").setup()\n```\n\nThis code initializes Neorg with the *default options*. In practice it's equivalent to the following:\n```lua\nrequire(\"neorg\").setup({\n    load = {\n        [\"core.defaults\"] = {},\n    }\n})\n```\n\nThis immediately introduces you to an important concept: **modules**.\n\nThe entire core of Neorg is modular, that is, all functionality is isolated into bits\nof code. Every module has its own configuration options, its own behaviours,\nits own keymaps, etc.\n\n`core.defaults` is a special type of module called a metamodule - it acts as a quick way to load a bunch of other modules\nat once. If you'd like to see what sort of modules `core.defaults` loads, check out [this wiki entry](https://github.com/nvim-neorg/neorg/wiki#default-modules).\n\n## Configuring Parts of Neorg\n\nBy default, Neorg comes with the bare experience. It loads modules that are critical for Neorg to function\nproperly. This workflow is mainly tailored for people who use Neorg to write documentation in the Norg file format, but nothing more than that.\n\nIf you're using Neorg to write notes or to perform time-tracking tasks you may want to load *extra modules*\nthat provide more functionality than the default.\n\nThese extra modules are listed [in a separate heading in the wiki](https://github.com/nvim-neorg/neorg/wiki#other-modules). Feel free to check some of them out!\nThey all have documentation and their behaviours are explained.\n\n## Loading New Modules\n\nThe most common module people set up is the concealer module - it converts our\nnotes from regular plaintext to beautified plaintext thanks to its use of\nicons. If you click on the module name in the wiki, it'll take you to [this\npage](https://github.com/nvim-neorg/neorg/wiki/Concealer).\n\nTo load the module, we use the full path of the module (in this case `core.concealer`).\nLet's adapt our configuration:\n```lua\nrequire(\"neorg\").setup({\n    load = {\n        [\"core.defaults\"] = {},\n        [\"core.concealer\"] = {}, -- We added this line!\n    }\n})\n```\n\nIf you restart Neovim and enter a `test.norg` file with the following content:\n```norg\n* Hello from Neorg!\n```\nYou should see that the heading has a nice icon!\n\nA decent chunk of modules require no configuration and work out of the box. Feel free to try as\nmany as you like!\n\n### Dependence on Neovim Options\n\nIf you carefully read the [overview page for the concealer](https://github.com/nvim-neorg/neorg/wiki/Concealer#overview),\nyou'll notice that the concealer respects the Neovim `conceallevel` and `concealcursor` options.\n\nIf possible, Neorg will not mess with your Neovim options. Check out the help pages for both options\nand tweak them as you see fit. For example, `vim.opt.conceallevel = 3` will cause many elements of the document\nlike the asterisks around `*bold*` words to disappear!\n\n## Configuring Individual Modules\n\nIf you had read onwards in the concealer's page you may have noticed a [configuration section](https://github.com/nvim-neorg/neorg/wiki/Concealer#configuration).\nThis section details all of the possible configuration options as well as their descriptions. Click around and see what you can find!\n\nContinuing with our concealer example, let's say we wanted to change the `icon_preset` setting.\nThe documentation tells us this option is a string, and that its default value is `\"basic\"`.\n\nTo configure the module, let's extend our configuration code:\n```lua\nrequire(\"neorg\").setup({\n    load = {\n        [\"core.defaults\"] = {},\n        [\"core.concealer\"] = {\n            config = { -- We added a `config` table!\n                icon_preset = \"varied\", -- And we set our option here.\n            },\n        },\n    }\n})\n```\n\nNotice that, to configure a module, we first provide a `config` table, and then all of our configuration inside!\n\nIf you save, quit and re-enter Neovim, the concealer should now use a completely different set of icons than it did before!\n\n## Running `:checkhealth` to Find Mistakes\n\nIf you find something that isn't behaving as intended feel free to run `:checkhealth neorg` - this will open up a window with a set of checkboxes (or crosses if there were errors).\n\nThe builtin checkhealth ensures that your configuration is \"clean\" - that means that there are no errors or mistakes on your end. If something is wrong (e.g. you forgot to wrap your\nmodule configuration in a `config = {}` block) then the health check will issue a warning or error with detailed instructions on how to fix the issue :)\n\n<!-- TODO: dirman -->\n\n## That's it!\n\nThese are the most critical things that you need to know to get started with configuring Neorg. If you would like to understand *how* to start using Neorg, check out the [tutorial](https://github.com/nvim-neorg/neorg/wiki/Tutorial)!\n"
  },
  {
    "path": "res/wiki/static/Tutorial.md",
    "content": "<div align=\"center\">\n\n# Getting Started\n\nA guide on getting started with Neorg.\n\n</div>\n\n## Neorg as a Concept\n\nFirst of all, welcome! There's quite a journey ahead of you, hope you stick around :)\n\nThis file will get you started with Neorg as your general note taking and life management tool.\n\nBefore we get into any of the details, let us discuss what Neorg *is* and what it *isn't*. Neorg is a frontend for a markup format called norg - it provides a load\nof functions for interacting with these norg files. Think of Norg files as highly advanced Markdown files (if you've ever had experience with Markdown before).\n\nThe beauty of markup is that it can contain *anything* - general documentation for your project, notes about a school subject,\ntechnical documentation for software, theses, presentation slides... you get the idea. Because plain text is so vertasile, Neorg is built to account for as many\nuse cases as possible.\n\nIf you haven't encountered this type of tool before, it's officially called an *organizational tool*. The name is vague because, well, the concept is pretty vague!\nNeorg is a tool that can handle any task you throw at it, as long as you can represent that problem in plaintext.\n\nBecause of this, you'll never learn *all* of Neorg, and that's okay! As you get more comfortable in a certain part of Neorg (e.g. the note-taking side or the\ntask management side) you can then extend your knowledge to other domains if you need.\n\n## Our First Steps\n\n**The first thing you'll want to do is set up Neorg**. There's an entire guide present [in the readme](https://github.com/nvim-neorg/neorg?tab=readme-ov-file#-installation)! Feel free to check it out and - once you're done - hop back into this file and get going.\n\nNeorg is generally always active whenever you start Neovim (unless you lazy load it) - this allows you to jump to convenient locations and files whenever you need.\n\nBefore we get ahead of ourselves, let's get a feel for the default state of the plugin. By default, Neorg is merely an advanced editing tool. To see why and how, open up\nany directory of your choice and open up a `note.norg` file. Ready?\n\n## Basic Syntax\n\nTerminology alert: **Neorg** is the name of the Neovim plugin. **Norg** is the name of the markup format that you save onto your disk. Keep this in mind as we progress!\n\nNorg has a lot of default syntax that should immediately feel familiar if you've used `org-mode` or some Markdown. First, let's type some text:\n\n```norg\nThis is my note!\n```\n\nJust like in a basic `.txt` file or like in Markdown, any text is allowed within the document.\nThe power of markup languages shines in the ways you can alter the text though, so let's learn some!\n\n#### Inline Markup\n\nAny word can be made bold, italic, underline, superscript, subscript - all through the use of modifiers. Below are a bunch of examples:\n\n```norg\nThis is my note! In here I will have some *bold* text, some /italic/, _underline_, ^superscript^ and ,subscript,!\n```\n\nAnd indeed, upon typing those, you should see Neorg appropriately make the text **bold**, *italic* and __underline__! Superscript and subscript cannot be properly rendered\nwithin terminals, so they're just coloured instead.\n\n*Fantastic!* Being able to mark up any text like this without any effort is very nice.\n\n> [!NOTE]\n> If the bold, italic or underline are not showing properly you can find a troubleshooting guide [here](https://github.com/nvim-neorg/neorg/wiki/Dependencies)! Terminals can be tricky sometimes.\n\n#### Lists\n\nAs you're writing, you'll naturally want to enumerate some things in the form of a list. Below is a simple example of a list:\n\n```norg\nBelow I'll create a list of things that I should buy:\n- Apples\n- Oranges\n- Milk\n```\n\nAll it takes is to prefix a line with a `- ` and you're set! Adding more than one new line will break the list apart into two:\n\n```norg\nBelow I'll create two lists of things that I should buy:\n- Apples\n- Oranges\n\n- Milk\n```\n\nApart from unordered lists, you may want to enforce an order on the list. You may be inclined to do something like this:\n\n```norg\nBelow I'll create an ordered list of stuff:\n\n1. First thing\n2. Second thing\n```\n\nNobody's stopping you from doing this, but Neorg won't recognize the list, it'll just think it's text. In norg, ordered lists are defined using the tilde (`~`):\n\n```norg\n~ First thing\n~ Second thing\n```\n\nIf you have the [concealer](https://github.com/nvim-neorg/neorg/wiki/Concealer) module enabled this will automatically get visually converted into a numeric list.\n\nThis approach may seem very odd initially until it clicks. When you write lists using the `1.` syntax, any time you want to add an item or reorder the list, you have to manually update *all* of the\nitems afterwards, or keep track of what the index of the previous item was. Let's say I have a list of 10 items, and I want to move the second item to the end. Now I have to change the numbers of all\nthe other entries! What if I want to add a new item at the end of the list? You have to look through the list to see what the number of the last entry was so that you can increment it by one.\n\nUsing the `~` syntax, Neorg does all of the counting for you, while still displaying the appropriate list index through the concealer. Neat!\n\n#### Headings\n\nAs you write more complex notes you'll want to divide them up in some logical fashion. Within norg, this is done through headings:\n```norg\n* Shopping\n  My shopping list consists of:\n  - Milk\n  - Butter\n  - Bread\n```\n\nAs you write, you may notice that Neorg will automatically indent things for you. The indentation is based on how far the heading's title is indented! For example, if you were to have this document:\n```norg\n*      Shopping\n  My shopping list consists of:\n  - Milk\n  - Butter\n  - Bread\n```\n\nPressing `gg=G` (`gg` - go to top of document, `=` - indent, `G` - to the bottom of the document) will indent all of the content to match the indentation of the title!\nNeorg calls this *dynamic indentation*. It can of course be tweaked.\n\nHeadings can be nested as much as you want by simply repeating the amount of asterisks!\n\n```norg\n* Shopping\n** Important Shopping\n   This is stuff I must immediately buy:\n   - Bread\n** Other Stuff\n   This is not as important:\n   - Milk\n   - Tomatoes (yum)\n```\n\n## Next Steps: Marking Things as Tasks\n\nAlright! We've learned how to make basic norg files, but where's the whole \"organizational\" part? When will I understand? One step at a time :)\n\nYou've now learned to create a basic file *somewhere* on your filesystem, and how to write some norg markup! Before we start organizing your files into meaningful places, let's learn about TODO\nstatuses.\n\nSay I have a list of things I'd like to do:\n```norg\n- Take out the trash\n- Watch \"how to delete emacs\" video\n- Wish Mum a happy birthday\n```\n\nOn its own, this is just a list... not very useful, is it? However, norg allows us to annotate *anything* within our documents as a task by giving it a TODO status:\n```norg\n- ( ) Take out the trash\n- ( ) Watch \"how to delete emacs\" video\n- ( ) Wish Mum a happy birthday\n```\n\nA TODO status is marked by the regular brackets right after the item itself. In our case, there's just a space inbetween the brackets - this means the task is *undone*.\n\nNow let's say I finished watching the \"how to delete emacs\" video, and I would like to mark it as complete. Put your cursor on the same line as the task and press `Ctrl + Space`!\nThis will toggle the state of the item to \"done\".\n\n> [!TIP]\n> If you have the concealer enabled, this will change the task to a checkmark, but what has really happened under the hood is that\n> the space has turned into an `x`! If you ever want to see the raw text of your file, run `:Neorg toggle-concealer` :)\n\nPressing `Ctrl + Space` again will toggle the task between three states - undone, done and pending. Pending is marked by the `-` character.\n\nApart from just these three states (which is what other markup formats commonly support), norg extends the amount of TODOs to a set of **twelve**!\n\nApart from just undone, done and pending, all of these TODO states are available:\n```norg\n- (=) On hold\n- (_) Cancelled\n- (!) Urgent\n- (?) Ambiguous (uncertain)\n- (+) Recurring (happens every so often)\n```\n\nTo set a TODO item to a specific state you can use one of the many keybinds listed in the [`todo_items` module's wiki](https://github.com/nvim-neorg/neorg/wiki/Todo-Items#overview).\n\n> [!NOTE]\n> If you're a keen reader you will have noticed that this is nowhere near 12 TODO states.\n> The others are a bit more advanced, you'll learn them as necessary!\n\n### But Wait, There's More\n\nTasks aren't exclusive to lists. In norg, you can assign a task to *anything*. Not quite\ndone with a heading yet? No problem!\n\n```norg\n* ( ) Undone Heading\n```\n\nThis will work, as will a task status attached to anything else in this fashion.\n\n## Part Two: File Management\n\nNow that you understand the basics of norg files and what you can put inside of them, let's give these files some meaning.\n\nThe simplest thing to get started with is writing your own diary/journal with Neorg! By default, your journal will be placed in the current working directory.\nTo open up the journal run `:Neorg journal today`, it should launch you into a new file!\n\nA core concept of Neorg is the concept of *workspaces*. A workspace is a directory which contains an **index file**\n(most commonly referred to as `index.norg`) in its root. This index file usually contains links to other parts in the workspace, sorted in some manner.\n\nHow do we manage such workspaces, you may ask? With `core.dirman` (short for directory manager) of course!\nIn your configuration, add a `core.dirman` entry as illustrated below:\n```lua\nrequire(\"neorg\").setup({\n    load = {\n        [\"core.defaults\"] = {},\n        [\"core.dirman\"] = {\n            config = {\n                workspaces = {\n                    notes = \"~/notes\",\n                },\n            },\n        },\n    },\n})\n```\n\nIn that snippet we set up a list of workspaces, currently with just one workspace: `notes`.\nThis `notes` workspace points to, you guessed it, `~/notes`!\n\nWhen in Neovim, `:Neorg workspace` will show you the current workspace you're in. This will usually be the `default` workspace - the `default` workspace\nalways gets created on-the-fly and points to Neovim's current working directory (seen via the `:pwd` command).\n\nTo switch to the notes workspace, run `:Neorg workspace notes`. This will switch the active workspace to `notes` and immediately drop you into its\n`index.norg` file! You're free to perform any edits you may like in that workspace.\n\nIf you were working on something beforehand (e.g. some code) you may use the\n`:Neorg return` command to close all norg buffers you opened since running the\n`:Neorg workspace ...` command and put you right back where you started. Neat!\n\n## To be continued...\n\nYou thought it ends here! Wrong. You've learned that Neorg is an editing mode as well as a diary writer\nand a quasi file manager. Apart from this, it has many other features that are likely to be useful for a specific use case of yours.\n\nThe next parts of this tutorial are under construction... head over to either the [youtube series](https://www.youtube.com/playlist?list=PLx2ksyallYzVI8CN1JMXhEf62j2AijeDa) or \nthe [list of modules](https://github.com/nvim-neorg/neorg/wiki#other-modules) to further your journey with Neorg! Have fun :)\n"
  },
  {
    "path": "stylua.toml",
    "content": "column_width = 120\nline_endings = \"Unix\"\nindent_type = \"Spaces\"\nindent_width = 4\nquote_style = \"AutoPreferDouble\"\nno_call_parentheses = false\n"
  }
]