pax_global_header00006660000000000000000000000064147660364450014531gustar00rootroot0000000000000052 comment=005b56001b2cb30bfa61b7986bc50657816ba4ba lukas-reineke-indent-blankline.nvim-005b560/000077500000000000000000000000001476603644500207175ustar00rootroot00000000000000lukas-reineke-indent-blankline.nvim-005b560/.cbfmt.toml000066400000000000000000000000421476603644500227610ustar00rootroot00000000000000[languages] lua = ["stylua -s -"] lukas-reineke-indent-blankline.nvim-005b560/.github/000077500000000000000000000000001476603644500222575ustar00rootroot00000000000000lukas-reineke-indent-blankline.nvim-005b560/.github/FUNDING.yml000066400000000000000000000000301476603644500240650ustar00rootroot00000000000000github: [lukas-reineke] lukas-reineke-indent-blankline.nvim-005b560/.github/ISSUE_TEMPLATE/000077500000000000000000000000001476603644500244425ustar00rootroot00000000000000lukas-reineke-indent-blankline.nvim-005b560/.github/ISSUE_TEMPLATE/bug_report.yml000066400000000000000000000026051476603644500273400ustar00rootroot00000000000000name: Bug Report description: Report a problem with indent-blankline labels: [bug] body: - type: markdown attributes: value: | _Before reporting:_ Make sure you are on the latest version of the plugin, and either the latest stable or nightly release of Neovim. Search [existing issues](https://github.com/lukas-reineke/indent-blankline.nvim/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc) - type: textarea attributes: label: 'Problem' description: 'Describe the current behavior. Include images, or videos if possible.' validations: required: true - type: textarea attributes: label: 'Steps to reproduce' description: | - Share your configurtaion and describe the steps to reproduce the issue. - See [Minimal-reproduction-template](https://github.com/lukas-reineke/indent-blankline.nvim/wiki/Minimal-reproduction-template#minimal-config) for how to create a minimal configuration. placeholder: | nvim --clean -u min-init.lua :edit foo yiwp validations: required: true - type: textarea attributes: label: 'Expected behavior' description: 'Describe the behavior you expect.' validations: required: true - type: input attributes: label: 'Neovim version (nvim -v)' placeholder: '0.6.0 commit db1b0ee3b30f' validations: required: true lukas-reineke-indent-blankline.nvim-005b560/.github/ISSUE_TEMPLATE/feature_request.yml000066400000000000000000000014331476603644500303710ustar00rootroot00000000000000name: Feature request description: Request an enhancement for indent-blankline labels: [enhancement] body: - type: markdown attributes: value: | Before requesting: search [existing issues](https://github.com/lukas-reineke/indent-blankline.nvim/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc) and read the documentation `:help indent-blankline`. - type: textarea attributes: label: 'Problem' description: 'Describe the problem to be solved.' placeholder: 'Add bongocat' validations: required: true - type: textarea attributes: label: 'Expected behavior' description: 'Describe what the new feature or behavior would look like. How does it solve the problem? Is it worth the cost?' validations: required: true lukas-reineke-indent-blankline.nvim-005b560/.github/workflows/000077500000000000000000000000001476603644500243145ustar00rootroot00000000000000lukas-reineke-indent-blankline.nvim-005b560/.github/workflows/lua_language_server.yml000066400000000000000000000035311476603644500310530ustar00rootroot00000000000000name: Lua Language Server Check on: workflow_call: inputs: lua_ls_version: required: true type: string neovim_versions: required: true type: string jobs: lua-language-server: name: lua language server runs-on: ubuntu-22.04 strategy: fail-fast: false matrix: rev: ${{ fromJson(inputs.neovim_versions) }} steps: - uses: actions/checkout@v3 - run: date +%F > todays-date - name: Restore cache for Neovim uses: actions/cache@v3 with: path: .ci/neovim key: ${{ matrix.rev }}-${{ hashFiles('todays-date') }} - name: Restore cache for vendor dependencies uses: actions/cache@v3 with: path: .ci/vendor key: ${{ hashFiles('todays-date') }} - name: Restore cache for lua LS uses: actions/cache@v3 with: path: .ci/lua-ls key: ${{ inputs.lua_ls_version }} - name: Prepare run: | test -d .ci/neovim || { mkdir -p .ci/neovim curl -sL "https://github.com/neovim/neovim/releases/download/${{ matrix.rev }}/nvim-linux64.tar.gz" | tar xzf - --strip-components=1 -C "${PWD}/.ci/neovim" } test -d .ci/lua-ls || { mkdir -p .ci/lua-ls curl -sL "https://github.com/LuaLS/lua-language-server/releases/download/${{ inputs.lua_ls_version }}/lua-language-server-${{ inputs.lua_ls_version }}-linux-x64.tar.gz" | tar xzf - -C "${PWD}/.ci/lua-ls" } - name: Run check run: | export PATH="${PWD}/.ci/neovim/bin:${PATH}" export PATH="${PWD}/.ci/lua-ls/bin:${PATH}" export VIM="${PWD}/.ci/neovim/share/nvim/runtime" nvim --version make lua-language-server version=${{ matrix.rev == 'nightly' && 'nightly' || 'stable' }} lukas-reineke-indent-blankline.nvim-005b560/.github/workflows/nightly_check.yml000066400000000000000000000010431476603644500276500ustar00rootroot00000000000000name: Nightly Neovim Check # Checks LSP and unit tests against new Neovim nightly once a week on: schedule: - cron: '30 21 * * 0' # 6:30 AM JST, Monday workflow_dispatch: concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: tests: uses: ./.github/workflows/tests.yml with: neovim_versions: | [ "nightly" ] lua-language-server: uses: ./.github/workflows/lua_language_server.yml with: lua_ls_version: 3.9.1 neovim_versions: | [ "nightly" ] lukas-reineke-indent-blankline.nvim-005b560/.github/workflows/pr_check.yml000066400000000000000000000040111476603644500266110ustar00rootroot00000000000000name: Pull Request Check on: pull_request: concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: tests: uses: ./.github/workflows/tests.yml with: neovim_versions: | [ "nightly", "v0.10.0" ] lua-language-server: uses: ./.github/workflows/lua_language_server.yml with: lua_ls_version: 3.9.1 neovim_versions: | [ "nightly", "v0.10.0" ] stylua: name: stylua runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v3 - uses: JohnnyMorganz/stylua-action@v3 with: token: ${{ secrets.GITHUB_TOKEN }} version: latest args: --color always --check . luacheck: name: luacheck runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v3 - name: Prepare run: | sudo apt-get update sudo apt-get install -y luarocks sudo luarocks install luacheck - name: Lint run: sudo make luacheck block-fixup: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v2 - name: Block Fixup Commit Merge uses: 13rac1/block-fixup-merge-action@v2.0.0 conventional-commit: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v3 with: fetch-depth: 0 - name: Check Commit Messages run: | commits=$(git log --no-merges --pretty=format:"%s" HEAD~${{ github.event.pull_request.commits }}..HEAD) bad_commits=() for commit in "$commits"; do if ! echo $commit | grep -qE "^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\(.+\))?: .+"; then bad_commits+=("$commit") fi done if [[ ${#bad_commits[@]} -ne 0 ]]; then echo "The following commits do not follow the Conventional Commit format:" for bad_commit in "${bad_commits[@]}"; do echo " - $bad_commit" done exit 1 fi lukas-reineke-indent-blankline.nvim-005b560/.github/workflows/tests.yml000066400000000000000000000020001476603644500261710ustar00rootroot00000000000000name: Unit Tests on: workflow_call: inputs: neovim_versions: required: true type: string jobs: tests: name: unit tests runs-on: ubuntu-22.04 strategy: fail-fast: false matrix: rev: ${{ fromJson(inputs.neovim_versions) }} steps: - uses: actions/checkout@v3 - run: date +%F > todays-date - name: Restore cache for Neovim uses: actions/cache@v3 with: path: .ci/neovim key: ${{ matrix.rev }}-${{ hashFiles('todays-date') }} - name: Prepare run: | test -d .ci/neovim || { mkdir -p .ci/neovim curl -sL "https://github.com/neovim/neovim/releases/download/${{ matrix.rev }}/nvim-linux64.tar.gz" | tar xzf - --strip-components=1 -C "${PWD}/.ci/neovim" } - name: Run tests run: | export PATH="${PWD}/.ci/neovim/bin:${PATH}" export VIM="${PWD}/.ci/neovim/share/nvim/runtime" nvim --version make test lukas-reineke-indent-blankline.nvim-005b560/.gitignore000066400000000000000000000000201476603644500226770ustar00rootroot00000000000000.ci/* /doc/tags lukas-reineke-indent-blankline.nvim-005b560/.luacheckrc000066400000000000000000000002211476603644500230170ustar00rootroot00000000000000globals = { "vim", "_", "describe", "it", "after_each", "before_each", "assert" } max_line_length = false exclude_files = { ".ci/vendor", } lukas-reineke-indent-blankline.nvim-005b560/.luarc.nightly.json000066400000000000000000000007161476603644500244570ustar00rootroot00000000000000{ "runtime.version": "LuaJIT", "diagnostics.globals": [ "it", "describe", "before_each", "after_each", "setup", "teardown" ], "diagnostics.ignoredFiles": "Disable", "diagnostics.libraryFiles": "Disable", "workspace.library": [ "/usr/local/share/nvim/runtime/lua", ".ci/neovim/share/nvim/runtime/lua", ".ci/vendor/pack/vendor/start/neodev.nvim/types/nightly" ] } lukas-reineke-indent-blankline.nvim-005b560/.luarc.stable.json000066400000000000000000000007151476603644500242520ustar00rootroot00000000000000{ "runtime.version": "LuaJIT", "diagnostics.globals": [ "it", "describe", "before_each", "after_each", "setup", "teardown" ], "diagnostics.ignoredFiles": "Disable", "diagnostics.libraryFiles": "Disable", "workspace.library": [ "/usr/local/share/nvim/runtime/lua", ".ci/neovim/share/nvim/runtime/lua", ".ci/vendor/pack/vendor/start/neodev.nvim/types/stable" ] } lukas-reineke-indent-blankline.nvim-005b560/.styluaignore000066400000000000000000000000061476603644500234410ustar00rootroot00000000000000.ci/* lukas-reineke-indent-blankline.nvim-005b560/LICENSE.md000066400000000000000000000020621476603644500223230ustar00rootroot00000000000000The MIT Licence Copyright (c) 2023 Lukas Reineke Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. lukas-reineke-indent-blankline.nvim-005b560/Makefile000066400000000000000000000016461476603644500223660ustar00rootroot00000000000000ifndef VERBOSE .SILENT: endif test: dependencies @echo "Running indent-blankline tests..." timeout 300 nvim -e \ --headless \ --noplugin \ -u specs/spec.lua \ -c "PlenaryBustedDirectory specs/features {minimal_init = 'specs/spec.lua'}" luacheck: luacheck . stylua: stylua --check . lua-language-server: dependencies rm -rf .ci/lua-language-server-log lua-language-server --configpath .luarc.$(version).json --logpath .ci/lua-language-server-log --check . @if jq -e 'if . | length > 0 then true else false end' .ci/lua-language-server-log/check.json > /dev/null; then \ cat .ci/lua-language-server-log/check.json; \ exit 1; \ fi dependencies: if [ ! -d .ci/vendor ]; then \ git clone --depth 1 \ https://github.com/nvim-lua/plenary.nvim \ .ci/vendor/pack/vendor/start/plenary.nvim; \ git clone --depth 1 \ https://github.com/folke/neodev.nvim \ .ci/vendor/pack/vendor/start/neodev.nvim; \ fi lukas-reineke-indent-blankline.nvim-005b560/README.md000066400000000000000000000115111476603644500221750ustar00rootroot00000000000000# Indent Blankline This plugin adds indentation guides to Neovim. It uses Neovim's virtual text feature and **no conceal** To start using indent-blankline, call the `ibl.setup()` function. This plugin requires the latest stable version of Neovim. ## Install Use your favorite plugin manager to install. For [lazy.nvim](https://github.com/folke/lazy.nvim): ```lua { "lukas-reineke/indent-blankline.nvim", main = "ibl", ---@module "ibl" ---@type ibl.config opts = {}, } ``` For [pckr.nvim](https://github.com/lewis6991/pckr.nvim): ```lua use "lukas-reineke/indent-blankline.nvim" ``` ## Setup To initialize and configure indent-blankline, run the `setup` function. ```lua require("ibl").setup() ``` Optionally, you can pass a configuration table to the setup function. For all available options, take a look at `:help ibl.config`. ## Screenshots ### Simple ```lua require("ibl").setup() ``` Screenshot ### Scope Scope requires treesitter to be set up. ```lua require("ibl").setup() ``` Screenshot The scope is _not_ the current indentation level! Instead, it is the indentation level where variables or functions are accessible, as in [Wikipedia Scope (Computer Science)](). This depends on the language you are writing. For more information, see `:help ibl.config.scope`. The start and end of scope uses underline, so to achieve the best result you might need to tweak the underline position. In Kitty terminal for example you can do that with [modify_font](https://sw.kovidgoyal.net/kitty/conf/#opt-kitty.modify_font) ### Mixed indentation ```lua require("ibl").setup() ``` Screenshot ### Multiple indent colors ```lua local highlight = { "RainbowRed", "RainbowYellow", "RainbowBlue", "RainbowOrange", "RainbowGreen", "RainbowViolet", "RainbowCyan", } local hooks = require "ibl.hooks" -- create the highlight groups in the highlight setup hook, so they are reset -- every time the colorscheme changes hooks.register(hooks.type.HIGHLIGHT_SETUP, function() vim.api.nvim_set_hl(0, "RainbowRed", { fg = "#E06C75" }) vim.api.nvim_set_hl(0, "RainbowYellow", { fg = "#E5C07B" }) vim.api.nvim_set_hl(0, "RainbowBlue", { fg = "#61AFEF" }) vim.api.nvim_set_hl(0, "RainbowOrange", { fg = "#D19A66" }) vim.api.nvim_set_hl(0, "RainbowGreen", { fg = "#98C379" }) vim.api.nvim_set_hl(0, "RainbowViolet", { fg = "#C678DD" }) vim.api.nvim_set_hl(0, "RainbowCyan", { fg = "#56B6C2" }) end) require("ibl").setup { indent = { highlight = highlight } } ``` Screenshot ### Background color indentation guides ```lua local highlight = { "CursorColumn", "Whitespace", } require("ibl").setup { indent = { highlight = highlight, char = "" }, whitespace = { highlight = highlight, remove_blankline_trail = false, }, scope = { enabled = false }, } ``` Screenshot ### rainbow-delimiters.nvim integration [rainbow-delimiters.nvim](https://gitlab.com/HiPhish/rainbow-delimiters.nvim) ```lua local highlight = { "RainbowRed", "RainbowYellow", "RainbowBlue", "RainbowOrange", "RainbowGreen", "RainbowViolet", "RainbowCyan", } local hooks = require "ibl.hooks" -- create the highlight groups in the highlight setup hook, so they are reset -- every time the colorscheme changes hooks.register(hooks.type.HIGHLIGHT_SETUP, function() vim.api.nvim_set_hl(0, "RainbowRed", { fg = "#E06C75" }) vim.api.nvim_set_hl(0, "RainbowYellow", { fg = "#E5C07B" }) vim.api.nvim_set_hl(0, "RainbowBlue", { fg = "#61AFEF" }) vim.api.nvim_set_hl(0, "RainbowOrange", { fg = "#D19A66" }) vim.api.nvim_set_hl(0, "RainbowGreen", { fg = "#98C379" }) vim.api.nvim_set_hl(0, "RainbowViolet", { fg = "#C678DD" }) vim.api.nvim_set_hl(0, "RainbowCyan", { fg = "#56B6C2" }) end) vim.g.rainbow_delimiters = { highlight = highlight } require("ibl").setup { scope = { highlight = highlight } } hooks.register(hooks.type.SCOPE_HIGHLIGHT, hooks.builtin.scope_highlight_from_extmark) ``` Screenshot lukas-reineke-indent-blankline.nvim-005b560/after/000077500000000000000000000000001476603644500220205ustar00rootroot00000000000000lukas-reineke-indent-blankline.nvim-005b560/after/ftplugin/000077500000000000000000000000001476603644500236505ustar00rootroot00000000000000lukas-reineke-indent-blankline.nvim-005b560/after/ftplugin/c.lua000066400000000000000000000001711476603644500245740ustar00rootroot00000000000000local hooks = require "ibl.hooks" hooks.register(hooks.type.SKIP_LINE, hooks.builtin.skip_preproc_lines, { bufnr = 0 }) lukas-reineke-indent-blankline.nvim-005b560/after/ftplugin/cpp.lua000066400000000000000000000001711476603644500251340ustar00rootroot00000000000000local hooks = require "ibl.hooks" hooks.register(hooks.type.SKIP_LINE, hooks.builtin.skip_preproc_lines, { bufnr = 0 }) lukas-reineke-indent-blankline.nvim-005b560/after/plugin/000077500000000000000000000000001476603644500233165ustar00rootroot00000000000000lukas-reineke-indent-blankline.nvim-005b560/after/plugin/commands.lua000066400000000000000000000024261476603644500256260ustar00rootroot00000000000000local ibl = require "ibl" local conf = require "ibl.config" vim.api.nvim_create_user_command("IBLEnable", function() ibl.update { enabled = true } end, { bar = true, desc = "Enables indent-blankline", }) vim.api.nvim_create_user_command("IBLDisable", function() ibl.update { enabled = false } end, { bar = true, desc = "Disables indent-blankline", }) vim.api.nvim_create_user_command("IBLToggle", function() if ibl.initialized then ibl.update { enabled = not conf.get_config(-1).enabled } else ibl.setup {} end end, { bar = true, desc = "Toggles indent-blankline on and off", }) vim.api.nvim_create_user_command("IBLEnableScope", function() ibl.update { scope = { enabled = true } } end, { bar = true, desc = "Enables indent-blanklines scope", }) vim.api.nvim_create_user_command("IBLDisableScope", function() ibl.update { scope = { enabled = false } } end, { bar = true, desc = "Disables indent-blanklines scope", }) vim.api.nvim_create_user_command("IBLToggleScope", function() if ibl.initialized then ibl.update { scope = { enabled = not conf.get_config(-1).scope.enabled } } else ibl.setup {} end end, { bar = true, desc = "Toggles indent-blanklines scope on and off", }) lukas-reineke-indent-blankline.nvim-005b560/doc/000077500000000000000000000000001476603644500214645ustar00rootroot00000000000000lukas-reineke-indent-blankline.nvim-005b560/doc/indent_blankline.txt000066400000000000000000000731031476603644500255310ustar00rootroot00000000000000*indent-blankline.txt* Adds indentation guides to Neovim Author: Lukas Reineke ============================================================================== CONTENTS *ibl* *indent-blankline* 1. Introduction |ibl.introduction| 2. Functions |ibl.functions| 3. Types |ibl.types| 4. Highlights |ibl.highlights| 5. Commands |ibl.commands| 6. License |ibl.license| ============================================================================== 1. INTRODUCTION *ibl.introduction* This plugin adds indentation guides to Neovim. It uses Neovim's virtual text feature and **no conceal** To start using indent-blankline, call the |ibl.setup()| function. This plugin requires the latest stable version of Neovim. ============================================================================== 2. FUNCTIONS *ibl.functions* setup({config}) *ibl.setup()* Initializes and configures indent-blankline. Optionally, the first parameter can be a configuration table. All values that are not passed in the table are set to the default value. List values get merged with the default list value. `setup` is idempotent, meaning you can call it multiple times, and each call will reset indent-blankline. If you want to only update the current configuration, use |ibl.update()| or |ibl.overwrite()|. Parameters: ~ • {config} (|ibl.config|?) Configuration table Example: ~ >lua require "ibl".setup() update({config}) *ibl.update()* Updates the indent-blankline configuration The first parameter is a configuration table. All values that are not passed in the table are kept as they are. List values get merged with the current list value. Parameters: ~ • {config} (|ibl.config|) Configuration table Example: ~ >lua require "ibl".update { enabled = false } < overwrite({config}) *ibl.overwrite()* Overwrites the indent-blankline configuration The first parameter is a configuration table. All values that are not passed in the table are kept as they are. All values that are passed overwrite existing and default values. In case you use both |ibl.setup()| and |ibl.overwrite()|, make sure to call setup first. Parameters: ~ • {config} (|ibl.config|) Configuration table Example: ~ >lua require "ibl".overwrite { exclude = { filetypes = {} } } < setup_buffer({bufnr}, {config}) *ibl.setup_buffer()* Configures indent-blankline for one buffer All values that are not passed are cleared, and will fall back to the global config. List values get merged with the global config values. Parameters: ~ • {bufnr} (number) Buffer number (0 for current buffer) • {config} (|ibl.config|?) Configuration table refresh({bufnr}) *ibl.refresh()* Refreshes indent-blankline in one buffer Only use this directly if you know what you are doing, consider |ibl.debounced_refresh| instead Parameters: ~ • {bufnr} (number) Buffer number (0 for current buffer) debounced_refresh({bufnr}) *ibl.debounced_refresh()* Refreshes indent-blankline in one buffer, debounced Parameters: ~ • {bufnr} (number) Buffer number (0 for current buffer) refresh_all() *ibl.refresh_all()* Refreshes indent-blankline in all buffers hooks.register({type}, {fn}, {opts}) *ibl.hooks.register()* Registers a hook. See |ibl.hooks| for more information Each hook type takes a different callback, and a configuration table Parameters: ~ • {type} (|ibl.hooks.type|) Type of the hook • {cb} (|ibl.hooks.cb|) Callback function • {opts} (|ibl.hooks.options|?) Optional options for the hook Return: ~ (string) ID of the hook Example: ~ >lua local hooks = require "ibl.hooks" hooks.register( hooks.type.ACTIVE, function(bufnr) return vim.api.nvim_buf_line_count(bufnr) < 5000 end ) < hooks.clear({id}) *ibl.hooks.clear()* Clears a hook by id Parameters: ~ • {id} (string) ID of the hook hooks.clear_all() *ibl.hooks.clear_all()* Clears all hooks hooks.get({bufnr}, {type}) *ibl.hooks.get()* Returns a list of all hooks for that buffer with the type Parameters: ~ • {bufnr} (number) Buffer number (0 for current buffer) • {type} (|ibl.hooks.type|) Type of the hook Return: ~ (|ibl.hooks.cb|[]) List of hooks ============================================================================== 3. TYPES *ibl.types* config *ibl.config* Configuration table for indent-blankline. Fields: ~ *ibl.config.enabled* • {enabled} (boolean) Enables or disables indent-blankline Default: `true` ~ *ibl.config.debounce* • {debounce} (number) Sets the amount indent-blankline debounces refreshes in milliseconds Default: `200` ~ • {viewport_buffer} (|ibl.config.viewport_buffer|) Configures the viewport of where indentation guides are generated • {indent} (|ibl.config.indent|) Configures the indentation • {whitespace} (|ibl.config.whitespace|) Configures the whitespace • {scope} (|ibl.config.scope|) Configures the scope • {exclude} (|ibl.config.exclude|) Configures what is excluded from indent-blankline Example: ~ >lua { debounce = 100, indent = { char = "|" }, whitespace = { highlight = { "Whitespace", "NonText" } }, scope = { exclude = { language = { "lua" } } }, } < config.viewport_buffer *ibl.config.viewport_buffer* Configures the viewport of where indentation guides are generated Fields: ~ *ibl.config.viewport_buffer.min* • {min} (number) Minimum number of lines above and below of what is currently visible in the window for which indentation guides will be generated Default: `30` ~ *ibl.config.viewport_buffer.max* • {max} (number) [deprecated] Maximum number of lines above and below of what is currently visible in the window for which indentation guides will be generated (This functionality has been deprecated, and the exact offset can be chosen with `min` instead) Default: `500` ~ Example: ~ >lua { min = 100, max = 600 } < config.indent *ibl.config.indent* Configures the indentation Fields: ~ *ibl.config.indent.char* • {char} (string|string[]) Character, or list of characters, that get used to display the indentation guide Each character has to have a display width of 0 or 1 Default: `▎` ~ Alternatives: ~ • left aligned solid • `▏` • `▎` (default) • `▍` • `▌` • `▋` • `▊` • `▉` • `█` • center aligned solid • `│` • `┃` • right aligned solid • `▕` • `▐` • center aligned dashed • `╎` • `╏` • `┆` • `┇` • `┊` • `┋` • center aligned double • `║` *ibl.config.indent.tab_char* • {tab_char} (string|string[]) Character, or list of characters, that get used to display the indentation guide for tabs Each character has to have a display width of 0 or 1 Default: uses |lcs-tab| if |'list'| is set, ~ otherwise, uses |ibl.config.indent.char| ~ *ibl.config.indent.highlight* • {highlight} (string|string[]) Highlight group, or list of highlight groups, that get applied to the indentation guide Default: |hl-IblIndent| ~ *ibl.config.indent.smart_indent_cap* • {smart_indent_cap} (boolean) Caps the number of indentation levels by looking at the surrounding code Default: `true` ~ Example: ~ >c # OFF { ▎ foo_bar(a, b, ▎ ▎ ▎ ▎ ▎ c, d); } # ON { ▎ foo_bar(a, b, ▎ ▎ c, d); } < *ibl.config.indent.priority* • {priority} (number) Virtual text priority for the indentation guide Default: `1` ~ *ibl.config.indent.repeat_linebreak* • {repeat_linebreak} (boolean) Repeat indentation guide virtual text on wrapped lines if |'breakindent'| is set, and |'breakindentopt'| does not contain any of the following: • `column` • `sbr` • `shift` with a negative value Default: `true` ~ Note: requires Neovim version 0.10 or higher ~ Example: ~ >lua { char = "|", tab_char = { "a", "b", "c" }, highlight = { "Function", "Label" }, smart_indent_cap = true, priority = 2, repeat_linebreak = false, } < config.whitespace *ibl.config.whitespace* Configures the whitespace Fields: ~ *ibl.config.whitespace.highlight* • {highlight} (string|string[]) Highlight group, or list of highlight groups, that get applied to the whitespace Default: |hl-IblWhitespace| ~ *ibl.config.whitespace.remove_blankline_trail* • {remove_blankline_trail} (boolean) Removes trailing whitespace on blanklines Turn this off if you want to add background color to the whitespace highlight group Default: `true` ~ Example: ~ >lua { highlight = { "Function", "Label" }, remove_blankline_trail = true, } < config.scope *ibl.config.scope* Configures the scope The scope is *not* the current indentation level! Instead, it is the indentation level where variables or functions are accessible. This depends on the language you are writing. Example: ~ In Python, an `if` block is not a new scope, variables defined inside `if` are accessible outside. The scope is the function `foo`. (please don't actually write code like this) >python def foo(); ┋ if True: ┋ a = "foo █ar" ┋ # ↳ cursor here ┋ print(a) < In Rust on the other hand, `if` blocks are a new scope. Variables defined inside are not accesible outside. Indent-blanklines scope reflects this, and the scope is just the `if` block. (this code would not compile) >rust fn foo() { if true { ┋ let a = "foo █ar"; ┋ // ↳ cursor here } print(a); } < Note: Scope requires treesitter to be set up ~ Fields: ~ *ibl.config.scope.enabled* • {enabled} (boolean) Enables or disables scope Default: `true` ~ *ibl.config.scope.char* • {char} (string|string[]) Character, or list of characters, that get used to display the scope indentation guide Each character has to have a display width of 0 or 1 Default: |ibl.config.indent.char| ~ *ibl.config.scope.show_start* • {show_start} (boolean) Shows an underline on the first line of the scope Default: `true` ~ *ibl.config.scope.show_end* • {show_end} (boolean) Shows an underline on the last line of the scope Default: `true` ~ *ibl.config.scope.show_exact_scope* • {show_exact_scope} (boolean) Shows an underline on the first line of the scope starting at the exact start of the scope (even if this is to the right of the indent guide) and an underline on the last line of the scope ending at the exact end of the scope. Default: `false` ~ *ibl.config.scope.injected_languages* • {injected_languages} (boolean) Checks for the current scope in injected treesitter languages This also influences if the scope gets excluded or not Default: `true` ~ *ibl.config.scope.highlight* • {highlight} (string|string[]) Highlight group, or list of highlight groups, that get applied to the scope Default: |hl-IblScope| ~ *ibl.config.scope.priority* • {priority} (number) Virtual text priority for the scope Default: `1024` ~ • {include} (|ibl.config.scope.include|) Configures additional nodes to be used as scope • {exclude} (|ibl.config.scope.exclude|) Configures nodes or languages to be excluded from scope Example: ~ >lua { enabled = true, show_start = true, show_end = false, injected_languages = false, highlight = { "Function", "Label" }, priority = 500, } < config.scope.include *ibl.config.scope.include* Configures additional nodes to be used as scope Fields: ~ *ibl.config.scope.include.node_type* • {node_type} (table) map of language to a list of node types which can be used as scope Use `*` as the language to act as a wildcard for all languages Use `*` as a node type to act as a wildcard for all node types Default: empty ~ Example: ~ >lua -- Add some node types to lua { node_type = { lua = { "return_statement", "table_constructor" } }, } -- Make every node type valid. Note that this can lead to some weird -- behavior { node_type = { ["*"] = { "*" } }, } config.scope.exclude *ibl.config.scope.exclude* Configures nodes or languages to be excluded from scope Fields: ~ *ibl.config.scope.exclude.language* • {language} (string[]) List of treesitter languages for which scope is disabled Default: empty ~ *ibl.config.scope.exclude.node_type* • {node_type} (table) map of language to a list of node types which should not be used as scope Use `*` as a wildcard for all languages Default: ~ • `*`: • `source_file` • `program` • `lua`: • `chunk` • `python`: • `module` Example: ~ >lua { language = { "rust" }, node_type = { lua = { "block", "chunk" } }, } < config.exclude *ibl.config.exclude* Configures what is excluded from indent-blankline Fields: ~ *ibl.config.exclude.filetypes* • {filetypes} (string[]) List of |'filetype'|s for which indent-blankline is disabled Default: ~ • `lspinfo` • `packer` • `checkhealth` • `help` • `man` • `gitcommit` • `TelescopePrompt` • `TelescopeResults` • `''` *ibl.config.exclude.buftypes* • {buftypes} (string[]) List of |'buftype'|s for which indent-blankline is disabled Default: ~ • `terminal` • `nofile` • `quickfix` • `prompt` Example: ~ >lua { filetypes = { "rust" }, buftypes = { "terminal" }, } < indent.whitespace *ibl.indent.whitespace* Enum of whitespace types Variants: ~ • {TAB_START} • {TAB_START_SINGLE} • {TAB_FILL} • {TAB_END} • {SPACE} • {INDENT} hooks *ibl.hooks* Hooks provide a way to extend the functionality of indent-blankline. Either from your own config, or even from other third part plugins. Hooks consist of a type (|ibl.hooks.type|) and a callback function (|ibl.hooks.cb|). When indent-blankline computes values for which hooks exist, for example if a buffer is active, it then calls all registered hooks for that type to get the final value. Most hooks can be global or buffer scoped. hooks.type *ibl.hooks.type* Enum of hook types Variants: ~ • {ACTIVE} • {SCOPE_ACTIVE} • {SKIP_LINE} • {WHITESPACE} • {VIRTUAL_TEXT} • {SCOPE_HIGHLIGHT} • {CLEAR} • {HIGHLIGHT_SETUP} hooks.cb *ibl.hooks.cb* Each hook type takes a different callback function hooks.cb.active({bufnr}) *ibl.hooks.cb.active()* Callback function for the |ibl.hooks.type|.ACTIVE hook. Gets called before refreshing indent-blankline for a buffer. If the callback returns false, the buffer will not be refreshed, and all existing indentation guides will be cleared. Parameters: ~ • {bufnr} (number) Buffer number Return: ~ (boolean) hooks.cb.scope_active({bufnr}) *ibl.hooks.cb.scope_active()* Callback function for the |ibl.hooks.type|.SCOPE_ACTIVE hook. Gets called before refreshing indent-blankline for a buffer. If the callback returns false, |ibl.config.scope| will be disabled. Parameters: ~ • {bufnr} (number) Buffer number Return: ~ (boolean) hooks.cb.skip_line({tick}, {bufnr}, {row}, {line}) *ibl.hooks.cb.skip_line()* Callback function for the |ibl.hooks.type|.SKIP_LINE hook. Gets called for every line before indentation is calculated. If the callback returns true, the line will get skipped. Parameters: ~ • {tick} (number) auto-incrementing id of the current refresh • {bufnr} (number) Buffer number • {row} (number) Row of the buffer • {line} (string) Text of that row Return: ~ (boolean) *ibl.hooks.cb.whitespace()* hooks.cb.whitespace({tick}, {bufnr}, {row}, {whitespace}) Callback function for the |ibl.hooks.type|.WHITESPACE hook. Gets called for every line after the whitespace is determined. The return value overwrites the whitespace for that line. Whitespace is a table of `ibl.indent.whitespace` enum values, where each value represents a display cell. Parameters: ~ • {tick} (number) auto-incrementing id of the current refresh • {bufnr} (number) Buffer number • {row} (number) Row of the buffer • {whitespace} (|ib.indent.whitespace|[]) List of whitespace enum Return: ~ (|ib.indent.whitespace|[]) *ibl.hooks.cb.virtual_text()* hooks.cb.virtual_text({tick}, {bufnr}, {row}, {virtual_text}) Callback function for the |ibl.hooks.type|.VIRTUAL_TEXT hook. Gets called for every line after the virtual text is determined. The return value overwrites the virtual text for that line. See |nvim_buf_set_extmark()| for more information about virtual text. Parameters: ~ • {tick} (number) auto-incrementing id of the current refresh • {bufnr} (number) Buffer number • {row} (number) Row of the buffer • {virtual_text} ({string, string|string[]}[]) Virtual text for the line Return: ~ ({string, string|string[]}[]) *ibl.hooks.cb.scope_highlight()* hooks.cb.scope_highlight({tick}, {bufnr}, {scope}, {scope_index}) Callback function for the |ibl.hooks.type|.SCOPE_HIGHLIGHT hook. Gets called once per refresh after the scope is determined. The return value overwrites the index of the highlight group defined in |ibl.config.scope.highlight| Parameters: ~ • {tick} (number) auto-incrementing id of the current refresh • {bufnr} (number) Buffer number • {scope} (|TSNode|) The current scope • {scope_index} (number) Index of the highlight group Return: ~ (number) hooks.cb.clear({bufnr}) *ibl.hooks.cb.clear()* Callback function for the |ibl.hooks.type|.CLEAR hook. Gets called when a buffer is cleared. Parameters: ~ • {bufnr} (number) Buffer number hooks.cb.highlight_setup() *ibl.hooks.cb.highlight_setup()* Callback function for the |ibl.hooks.type|.HIGHLIGHT_SETUP hook. Gets called before the highlight groups are created. If you want to define custom highlight groups, do it in this hook, so they are reset if you change the colorscheme with |:colorscheme|. hooks.options *ibl.hooks.options* Option table for hook registration Fields: ~ *ibl.hooks.options.bufnr* • {bufnr} (number) Buffer number (0 for current buffer) hooks.builtin *ibl.hooks.builtin* hooks.builtin.skip_preproc_lines *ibl.hooks.builtin.skip_preproc_lines* Skip preproc lines Automatically active for `c` and `cpp` Example: ~ >lua local hooks = require "ibl.hooks" hooks.register( hooks.type.SKIP_LINE, hooks.builtin.skip_preproc_lines, { bufnr = 0 } ) < *hooks.builtin.scope_highlight_from_extmark* hooks.builtin.scope_highlight_from_extmark Gets the highlight group from existing extmark highlights at the end or beginning of the scope. This can be used to get a somewhat reliable sync between "rainbow parentheses" plugins like https://gitlab.com/HiPhish/rainbow-delimiters.nvim and indent-blankline. Example: ~ >lua local highlight = { "RainbowDelimiterRed", "RainbowDelimiterYellow", "RainbowDelimiterBlue", "RainbowDelimiterOrange", "RainbowDelimiterGreen", "RainbowDelimiterViolet", "RainbowDelimiterCyan", } vim.g.rainbow_delimiters = { highlight = highlight } require "ibl".setup { scope = { highlight = highlight } } local hooks = require "ibl.hooks" hooks.register( hooks.type.SCOPE_HIGHLIGHT, hooks.builtin.scope_highlight_from_extmark ) < *ibl.hooks.builtin.hide_first_space_indent_level* hooks.builtin.hide_first_space_indent_level Replaces the first indentation guide for space indentation with a normal space. Example: ~ >lua local hooks = require "ibl.hooks" hooks.register( hooks.type.WHITESPACE, hooks.builtin.hide_first_space_indent_level ) < *ibl.hooks.builtin.hide_first_tab_indent_level* hooks.builtin.hide_tab_space_indent_level Replaces the first indentation guide for tab indentation with |lcs-tab| tab fill. Example: ~ >lua local hooks = require "ibl.hooks" hooks.register( hooks.type.WHITESPACE, hooks.builtin.hide_first_tab_indent_level ) < ============================================================================== 4. HIGHLIGHTS *ibl.highlights* IblIndent *hl-IblIndent* The default highlight group for indentation characters. Default: takes the values from |hl-Whitespace| when not defined ~ IblWhitespace *hl-IblWhitespace* The default highlight group for whitespace characters. Default: takes the values from |hl-Whitespace| when not defined ~ IblScope *hl-IblScope* The default highlight group for |ibl.config.scope| characters. Default: takes the values from |hl-LineNr| when not defined ~ ============================================================================== 5. COMMANDS *ibl.commands* :IBLEnable *:IBLEnable* Enables indent-blankline :IBLDisable *:IBLDisable* Disables indent-blankline :IBLToggle *:IBLToggle* Toggles indent-blankline on and off :IBLEnableScope *:IBLEnableScope* Enables indent-blanklines scope :IBLDisableScope *:IBLDisableScope* Disables indent-blanklines scope :IBLToggleScope *:IBLToggleScope* Toggles indent-blanklines scope on and off ============================================================================== 6. LICENSE *ibl.license* The MIT Licence http://www.opensource.org/licenses/mit-license.php Copyright (c) 2023 Lukas Reineke Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ============================================================================== vim:tw=78:ts=8:ft=help:norl lukas-reineke-indent-blankline.nvim-005b560/lua/000077500000000000000000000000001476603644500215005ustar00rootroot00000000000000lukas-reineke-indent-blankline.nvim-005b560/lua/ibl/000077500000000000000000000000001476603644500222465ustar00rootroot00000000000000lukas-reineke-indent-blankline.nvim-005b560/lua/ibl/autocmds.lua000066400000000000000000000033071476603644500245730ustar00rootroot00000000000000local highlights = require "ibl.highlights" local M = {} M.setup = function() local group = vim.api.nvim_create_augroup("IndentBlankline", {}) local ibl = require "ibl" local buffer_leftcol = {} vim.api.nvim_create_autocmd("VimEnter", { group = group, pattern = "*", callback = ibl.refresh_all, }) vim.api.nvim_create_autocmd({ "CursorMoved", "CursorMovedI", "BufWinEnter", "CompleteChanged", "FileChangedShellPost", "FileType", "TextChanged", "TextChangedI", }, { group = group, pattern = "*", callback = function(opts) ibl.debounced_refresh(opts.buf) end, }) vim.api.nvim_create_autocmd("OptionSet", { group = group, pattern = "list,listchars,shiftwidth,tabstop,vartabstop,breakindent,breakindentopt", callback = function(opts) ibl.debounced_refresh(opts.buf) end, }) vim.api.nvim_create_autocmd("WinScrolled", { group = group, pattern = "*", callback = function(opts) local win_view = vim.fn.winsaveview() or { leftcol = 0 } if buffer_leftcol[opts.buf] ~= win_view.leftcol then buffer_leftcol[opts.buf] = win_view.leftcol -- Refresh immediately for horizontal scrolling ibl.refresh(opts.buf) else ibl.debounced_refresh(opts.buf) end end, }) vim.api.nvim_create_autocmd("ColorScheme", { group = group, pattern = "*", callback = function() highlights.setup() ibl.refresh_all() end, }) end return M lukas-reineke-indent-blankline.nvim-005b560/lua/ibl/config.lua000066400000000000000000000246441476603644500242300ustar00rootroot00000000000000local utils = require "ibl.utils" local M = {} --- The current global configuration --- ---@type ibl.config.full? M.config = nil --- Map from buffer numbers to their partial configuration --- --- Anything not included here will fall back to the global configuration ---@type table M.buffer_config = {} --- The default configuration --- ---@type ibl.config.full M.default_config = { enabled = true, debounce = 200, viewport_buffer = { min = 30, max = 500, }, indent = { char = "▎", tab_char = nil, highlight = "IblIndent", smart_indent_cap = true, priority = 1, repeat_linebreak = true, }, whitespace = { highlight = "IblWhitespace", remove_blankline_trail = true, }, scope = { enabled = true, char = nil, show_start = true, show_end = true, show_exact_scope = false, injected_languages = true, highlight = "IblScope", priority = 1024, include = { node_type = {}, }, exclude = { language = {}, node_type = { ["*"] = { "source_file", "program", }, lua = { "chunk", }, python = { "module", }, }, }, }, exclude = { filetypes = { "lspinfo", "packer", "checkhealth", "help", "man", "gitcommit", "TelescopePrompt", "TelescopeResults", "", }, buftypes = { "terminal", "nofile", "quickfix", "prompt", }, }, } ---@param char string ---@return boolean, string? local validate_char = function(char) if type(char) == "string" then local length = vim.fn.strwidth(char) return length <= 1, string.format("'%s' has a display width of %d", char, length) else if #char == 0 then return false, "table is empty" end for i, c in ipairs(char) do local length = vim.fn.strwidth(c) if length > 1 then return false, string.format("index %d '%s' has a display width of %d", i, c, length) end end return true end end ---@param config ibl.config? local validate_config = function(config) if not config then return end utils.validate_config({ enabled = { config.enabled, "boolean", true }, debounce = { config.debounce, "number", true }, viewport_buffer = { config.viewport_buffer, "table", true }, indent = { config.indent, "table", true }, whitespace = { config.whitespace, "table", true }, scope = { config.scope, "table", true }, exclude = { config.exclude, "table", true }, }, config, "ibl.config") if config.viewport_buffer then utils.validate_config({ min = { config.viewport_buffer.min, "number", true }, max = { config.viewport_buffer.max, "number", true }, }, config.viewport_buffer, "ibl.config.viewport_buffer") end if config.indent then utils.validate_config({ char = { config.indent.char, { "string", "table" }, true }, tab_char = { config.indent.tab_char, { "string", "table" }, true }, highlight = { config.indent.highlight, { "string", "table" }, true }, smart_indent_cap = { config.indent.smart_indent_cap, "boolean", true }, priority = { config.indent.priority, "number", true }, repeat_linebreak = { config.indent.repeat_linebreak, "boolean", true }, }, config.indent, "ibl.config.indent") if config.indent.char then utils.validate { char = { config.indent.char, validate_char, "indent.char to have a display width of 0 or 1", }, } end if config.indent.tab_char then utils.validate { tab_char = { config.indent.tab_char, validate_char, "indent.tab_char to have a display width of 0 or 1", }, } end if type(config.indent.highlight) == "table" then utils.validate { tab_char = { config.indent.highlight, function(highlight) return #highlight > 0 end, "indent.highlight to be not empty", }, } end end if config.whitespace then utils.validate_config({ highlight = { config.whitespace.highlight, { "string", "table" }, true }, remove_blankline_trail = { config.whitespace.remove_blankline_trail, "boolean", true }, }, config.whitespace, "ibl.config.whitespace") if type(config.whitespace.highlight) == "table" then utils.validate { tab_char = { config.whitespace.highlight, function(highlight) return #highlight > 0 end, "whitespace.highlight to be not empty", }, } end end if config.scope then utils.validate_config({ enabled = { config.scope.enabled, "boolean", true }, char = { config.scope.char, { "string", "table" }, true }, show_start = { config.scope.show_start, "boolean", true }, show_end = { config.scope.show_end, "boolean", true }, show_exact_scope = { config.scope.show_exact_scope, "boolean", true }, injected_languages = { config.scope.injected_languages, "boolean", true }, highlight = { config.scope.highlight, { "string", "table" }, true }, priority = { config.scope.priority, "number", true }, include = { config.scope.include, "table", true }, exclude = { config.scope.exclude, "table", true }, }, config.scope, "ibl.config.scope") if config.scope.char then utils.validate { char = { config.scope.char, validate_char, "scope.char to have a display width of 0 or 1", }, } end if type(config.scope.highlight) == "table" then utils.validate { tab_char = { config.scope.highlight, function(highlight) return #highlight > 0 end, "scope.highlight to be not empty", }, } end if config.scope.exclude then utils.validate_config({ language = { config.scope.exclude.language, "table", true }, node_type = { config.scope.exclude.node_type, "table", true }, }, config.scope.exclude, "ibl.config.scope.exclude") end if config.scope.include then utils.validate_config({ node_type = { config.scope.include.node_type, "table", true }, }, config.scope.include, "ibl.config.scope.include") end end if config.exclude then utils.validate_config({ filetypes = { config.exclude.filetypes, "table", true }, buftypes = { config.exclude.buftypes, "table", true }, }, config.exclude, "ibl.config.exclude") end end ---@param behavior "merge"|"overwrite" ---@param base ibl.config.full ---@param input ibl.config? ---@return ibl.config.full local merge_configs = function(behavior, base, input) local result = vim.tbl_deep_extend("keep", input or {}, base) --[[@as ibl.config.full]] if behavior == "merge" and input then result.scope.exclude.language = utils.tbl_join(base.scope.exclude.language, vim.tbl_get(input, "scope", "exclude", "language")) local node_type = vim.tbl_get(input, "scope", "exclude", "node_type") if node_type then for k, v in pairs(node_type) do result.scope.exclude.node_type[k] = utils.tbl_join(v, base.scope.exclude.node_type[k]) end end result.exclude.filetypes = utils.tbl_join(base.exclude.filetypes, vim.tbl_get(input, "exclude", "filetypes")) result.exclude.buftypes = utils.tbl_join(base.exclude.buftypes, vim.tbl_get(input, "exclude", "buftypes")) end return result end --- Sets the global configuration --- --- All values that are not passed are set to the default value --- List values get merged with the default values ---@param config ibl.config? ---@return ibl.config.full M.set_config = function(config) validate_config(config) M.config = merge_configs("merge", M.default_config, config) return M.config end --- Updates the global configuration --- --- All values that are not passed are kept as they are ---@param config ibl.config ---@return ibl.config.full M.update_config = function(config) validate_config(config) M.config = merge_configs("merge", M.config or M.default_config, config or {}) return M.config end --- Overwrites the global configuration --- --- Same as `update_config`, but all list values are overwritten instead of merged ---@param config ibl.config ---@return ibl.config.full M.overwrite_config = function(config) validate_config(config) M.config = merge_configs("overwrite", M.config or M.default_config, config) return M.config end --- Sets the configuration for a buffer --- --- All values that are not passed are cleared, and will fall back to the global config ---@param bufnr number ---@param config ibl.config ---@return ibl.config.full M.set_buffer_config = function(bufnr, config) validate_config(config) bufnr = utils.get_bufnr(bufnr) M.buffer_config[bufnr] = config return M.get_config(bufnr) end --- Clears the configuration for a buffer --- ---@param bufnr number M.clear_buffer_config = function(bufnr) M.buffer_config[bufnr] = nil end --- Returns the configuration for a buffer --- ---@param bufnr number ---@return ibl.config.full M.get_config = function(bufnr) bufnr = utils.get_bufnr(bufnr) return merge_configs("merge", M.config or M.default_config, M.buffer_config[bufnr]) end return M lukas-reineke-indent-blankline.nvim-005b560/lua/ibl/config.types.lua000066400000000000000000000217201476603644500253630ustar00rootroot00000000000000 ---@meta --- Configuration table for indent-blankline ---@class ibl.config --- Enables or disables indent-blankline ---@field enabled boolean? --- Sets the amount indent-blankline debounces refreshes in milliseconds ---@field debounce number? --- Configures the viewport of where indentation guides are generated ---@field viewport_buffer ibl.config.viewport_buffer? --- Configures the indentation ---@field indent ibl.config.indent? --- Configures the whitespace ---@field whitespace ibl.config.whitespace? --- Configures the scope ---@field scope ibl.config.scope? --- Configures what is excluded from indent-blankline ---@field exclude ibl.config.exclude? ---@class ibl.config.viewport_buffer --- Minimum number of lines above and below of what is currently visible in the window for which indentation guides will --- be generated ---@field min number? --- Maximum number of lines above and below of what is currently visible in the window for which indentation guides will --- be generated ---@field max number? ---@class ibl.config.indent --- Character, or list of characters, that get used to display the indentation guide --- --- Each character has to have a display width of 0 or 1 ---@field char string|string[]? --- Character, or list of characters, that get used to display the indentation guide for tabs --- --- Defaults to what is set in `listchars` --- Each character has to have a display width of 0 or 1 ---@field tab_char string|string[]? --- Highlight group, or list of highlight groups, that get applied to the indentation guide ---@field highlight string|string[]? --- Caps the number of indentation levels by looking at the surrounding code ---@field smart_indent_cap boolean? --- Virtual text priority for the indentation guide ---@field priority number? --- Repeat indentation guide virtual text on wrapped lines if 'breakindent' is set ---@field repeat_linebreak boolean? ---@class ibl.config.whitespace --- Highlight group, or list of highlight groups, that get applied to the whitespace ---@field highlight string|string[]? --- Removes trailing whitespace on blanklines --- --- Turn this off if you want to add background color to the whitespace highlight group ---@field remove_blankline_trail boolean? ---@class ibl.config.scope --- Enables or disables scope ---@field enabled boolean? --- Character, or list of characters, that get used to display the scope indentation guide --- --- Each character has to have a display width of 0 or 1 ---@field char string|string[]? --- Shows an underline on the first line of the scope ---@field show_start boolean? --- Shows an underline on the last line of the scope ---@field show_end boolean? --- Always shows an underline on the last line of the scope (default is to ignore some cases) --- and starts the scope underline at the actual beginning of the scope (even if it is to the --- right of the indent level) ---@field show_exact_scope boolean? --- Checks for the current scope in injected treesitter languages --- --- This also influences if the scope gets excluded or not ---@field injected_languages boolean? --- Highlight group, or list of highlight groups, that get applied to the scope ---@field highlight string|string[]? --- Virtual text priority for the scope ---@field priority number? --- Configures additional nodes to be used as scope ---@field include ibl.config.scope.include? --- Configures nodes or languages to be excluded from scope ---@field exclude ibl.config.scope.exclude? ---@class ibl.config.scope.include --- map of language to a list of node types which can be used as scope --- --- Use `*` as a wildcard for all languages --- --- Example: --- --- { --- ["*"] = { "comment" }, --- rust = { "identifier" }, --- } --- ---@field node_type table? ---@class ibl.config.scope.exclude --- List of treesitter languages for which scope is disabled ---@field language string[]? --- map of language to a list of node types which should not be used as scope --- --- Use `*` as a wildcard for all languages --- --- Example: --- --- { --- ["*"] = { "comment" }, --- rust = { "identifier" }, --- } --- ---@field node_type table? ---@class ibl.config.exclude --- List of `filetypes` for which indent-blankline is disabled ---@field filetypes string[]? --- List of `buftypes` for which indent-blankline is disabled ---@field buftypes string[]? ------ --- Configuration table for indent-blankline ---@class ibl.config.full: ibl.config --- Enables or disables indent-blankline ---@field enabled boolean --- Sets the amount indent-blankline debounces refreshes in milliseconds ---@field debounce number --- Configures the viewport of where indentation guides are generated ---@field viewport_buffer ibl.config.full.viewport_buffer: ibl.config.viewport_buffer --- Configures the indentation ---@field indent ibl.config.full.indent: ibl.config.indent --- Configures the whitespace ---@field whitespace ibl.config.full.whitespace: ibl.config.whitespace --- Configures the scope ---@field scope ibl.config.full.scope: ig.config.scope --- Configures what is excluded from indent-blankline ---@field exclude ibl.config.full.exclude: ibl.config.exclude ---@class ibl.config.full.viewport_buffer: ibl.config.viewport_buffer --- Minimum number of lines above and below of what is currently visible in the window for which indentation guides will --- be generated ---@field min number --- Maximum number of lines above and below of what is currently visible in the window for which indentation guides will --- be generated ---@field max number ---@class ibl.config.full.indent: ibl.config.indent --- Character, or list of characters, that get used to display the indentation guide --- --- Each character has to have a display width of 0 or 1 ---@field char string|string[] --- Character, or list of characters, that get used to display the indentation guide for tabs --- --- Defaults to what is set in `listchars` --- Each character has to have a display width of 0 or 1 ---@field tab_char string|string[]? --- Highlight group, or list of highlight groups, that get applied to the indentation guide ---@field highlight string|string[] --- Caps the number of indentation levels by looking at the surrounding code ---@field smart_indent_cap boolean --- Virtual text priority for the indentation guide ---@field priority number --- Repeat indentation guide virtual text on wrapped lines if 'breakindent' is set ---@field repeat_linebreak boolean ---@class ibl.config.full.whitespace: ibl.config.whitespace --- Highlight group, or list of highlight groups, that get applied to the whitespace ---@field highlight string|string[] --- Removes trailing whitespace on blanklines --- --- Turn this off if you want to add background color to the whitespace highlight group ---@field remove_blankline_trail boolean ---@class ibl.config.full.scope: ibl.config.scope --- Enables or disables scope ---@field enabled boolean --- Character, or list of characters, that get used to display the scope indentation guide --- --- Each character has to have a display width of 0 or 1 ---@field char string|string[]? --- Shows an underline on the first line of the scope ---@field show_start boolean --- Shows an underline on the last line of the scope ---@field show_end boolean --- Always shows an underline on the last line of the scope (default is to ignore some cases) --- and starts the scope underline at the actual beginning of the scope (even if it is to the --- right of the indent level) ---@field show_exact_scope boolean --- Checks for the current scope in injected treesitter languages --- --- This also influences if the scope gets excluded or not ---@field injected_languages boolean --- Highlight group, or list of highlight groups, that get applied to the scope ---@field highlight string|string[] --- Virtual text priority for the scope ---@field priority number --- Configures additional nodes to be used as scope ---@field include ibl.config.full.scope.include --- Configures nodes or languages to be excluded from scope ---@field exclude ibl.config.full.scope.exclude: ibl.config.scope.exclude ---@class ibl.config.full.scope.include: ibl.config.scope.include --- map of language to a list of node types which can be used as scope --- --- Use `*` as a wildcard for all languages --- --- Example: --- --- { --- ["*"] = { "comment" }, --- rust = { "identifier" }, --- } --- ---@field node_type table ---@class ibl.config.full.scope.exclude: ibl.config.scope.exclude --- List of treesitter languages for which scope is disabled ---@field language string[] --- map of language to a list of node types which should not be used as scope --- --- Use `*` as a wildcard for all languages --- --- Example: --- --- { --- ["*"] = { "comment" }, --- rust = { "identifier" }, --- } --- ---@field node_type table ---@class ibl.config.full.exclude: ibl.config.exclude --- List of `filetypes` for which indent-blankline is disabled ---@field filetypes string[] --- List of `buftypes` for which indent-blankline is disabled ---@field buftypes string[] lukas-reineke-indent-blankline.nvim-005b560/lua/ibl/highlights.lua000066400000000000000000000063271476603644500251130ustar00rootroot00000000000000local conf = require "ibl.config" local hooks = require "ibl.hooks" local utils = require "ibl.utils" ---@class ibl.highlight ---@field char string ---@field underline string? local M = { ---@type ibl.highlight[] indent = {}, ---@type ibl.highlight[] whitespace = {}, ---@type ibl.highlight[] scope = {}, } ---@param name string local get = function(name) return vim.api.nvim_get_hl(0, { name = name }) --[[@as vim.api.keyset.highlight]] end ---@param hl table local not_set = function(hl) return not hl or utils.tbl_count(hl) == 0 end local setup_builtin_hl_groups = function() local whitespace_hl = get "Whitespace" local line_nr_hl = get "LineNr" local ibl_indent_hl_name = "IblIndent" local ibl_whitespace_hl_name = "IblWhitespace" local ibl_scope_hl_name = "IblScope" if not_set(get(ibl_indent_hl_name)) then vim.api.nvim_set_hl(0, ibl_indent_hl_name, whitespace_hl) end if not_set(get(ibl_whitespace_hl_name)) then vim.api.nvim_set_hl(0, ibl_whitespace_hl_name, whitespace_hl) end if not_set(get(ibl_scope_hl_name)) then vim.api.nvim_set_hl(0, ibl_scope_hl_name, line_nr_hl) end end M.setup = function() local config = conf.get_config(-1) for _, fn in pairs(hooks.get(-1, hooks.type.HIGHLIGHT_SETUP) --[=[@as ibl.hooks.cb.highlight_setup[]]=]) do fn() end setup_builtin_hl_groups() local indent_highlights = config.indent.highlight if type(indent_highlights) == "string" then indent_highlights = { indent_highlights } end M.indent = {} for i, name in ipairs(indent_highlights) do local hl = get(name) if not_set(hl) then error(string.format("No highlight group '%s' found", name)) end hl.nocombine = true M.indent[i] = { char = string.format("@ibl.indent.char.%d", i) } vim.api.nvim_set_hl(0, M.indent[i].char, hl) end local whitespace_highlights = config.whitespace.highlight if type(whitespace_highlights) == "string" then whitespace_highlights = { whitespace_highlights } end M.whitespace = {} for i, name in ipairs(whitespace_highlights) do local hl = get(name) if not_set(hl) then error(string.format("No highlight group '%s' found", name)) end hl.nocombine = true M.whitespace[i] = { char = string.format("@ibl.whitespace.char.%d", i) } vim.api.nvim_set_hl(0, M.whitespace[i].char, hl) end local scope_highlights = config.scope.highlight if type(scope_highlights) == "string" then scope_highlights = { scope_highlights } end M.scope = {} for i, scope_name in ipairs(scope_highlights) do local char_hl = get(scope_name) if not_set(char_hl) then error(string.format("No highlight group '%s' found", scope_name)) end char_hl.nocombine = true M.scope[i] = { char = string.format("@ibl.scope.char.%d", i), underline = string.format("@ibl.scope.underline.%d", i), } vim.api.nvim_set_hl(0, M.scope[i].char, char_hl) vim.api.nvim_set_hl(0, M.scope[i].underline, { sp = char_hl.fg, underline = true }) end end return M lukas-reineke-indent-blankline.nvim-005b560/lua/ibl/hooks.lua000066400000000000000000000230451476603644500241000ustar00rootroot00000000000000local conf = require "ibl.config" local utils = require "ibl.utils" local indent = require "ibl.indent" local M = {} ---@enum ibl.hooks.type M.type = { ACTIVE = "ACTIVE", SCOPE_ACTIVE = "SCOPE_ACTIVE", SKIP_LINE = "SKIP_LINE", WHITESPACE = "WHITESPACE", VIRTUAL_TEXT = "VIRTUAL_TEXT", SCOPE_HIGHLIGHT = "SCOPE_HIGHLIGHT", CLEAR = "CLEAR", HIGHLIGHT_SETUP = "HIGHLIGHT_SETUP", } ---@class ibl.hooks.options ---@field bufnr number? local default_opts = { bufnr = nil, } local hooks = { [M.type.ACTIVE] = {}, [M.type.SCOPE_ACTIVE] = {}, [M.type.SKIP_LINE] = {}, [M.type.WHITESPACE] = {}, [M.type.VIRTUAL_TEXT] = {}, [M.type.SCOPE_HIGHLIGHT] = {}, [M.type.CLEAR] = {}, [M.type.HIGHLIGHT_SETUP] = {}, buffer_scoped = {}, } local count = 0 ---@alias ibl.hooks.cb.active fun(bufnr: number): boolean ---@alias ibl.hooks.cb.scope_active fun(bufnr: number): boolean ---@alias ibl.hooks.cb.skip_line fun(tick: number, bufnr: number, row: number, line: string): boolean ---@alias ibl.hooks.cb.whitespace fun(tick: number, bufnr: number, row: number, whitespace: ibl.indent.whitespace[]): ibl.indent.whitespace[] ---@alias ibl.hooks.cb.virtual_text fun(tick: number, bufnr: number, row: number, virt_text: ibl.virtual_text): ibl.virtual_text ---@alias ibl.hooks.cb.scope_highlight fun(tick: number, bufnr: number, scope: TSNode, scope_index: number): number ---@alias ibl.hooks.cb.clear fun(bufnr: number) ---@alias ibl.hooks.cb.highlight_setup fun() --- Registers a hook --- --- Each hook type takes a callback a different function, and a configuration table ---@param type ibl.hooks.type ---@param cb function ---@param opts ibl.hooks.options? ---@overload fun(type: 'ACTIVE', cb: ibl.hooks.cb.active, opts: ibl.hooks.options?): string ---@overload fun(type: 'SCOPE_ACTIVE', cb: ibl.hooks.cb.scope_active, opts: ibl.hooks.options?): string ---@overload fun(type: 'SKIP_LINE', cb: ibl.hooks.cb.skip_line, opts: ibl.hooks.options?): string ---@overload fun(type: 'WHITESPACE', cb: ibl.hooks.cb.whitespace, opts: ibl.hooks.options?): string ---@overload fun(type: 'VIRTUAL_TEXT', cb: ibl.hooks.cb.virtual_text, opts: ibl.hooks.options?): string ---@overload fun(type: 'SCOPE_HIGHLIGHT', cb: ibl.hooks.cb.scope_highlight, opts: ibl.hooks.options?): string ---@overload fun(type: 'CLEAR', cb: ibl.hooks.cb.clear, opts: ibl.hooks.options?): string ---@overload fun(type: 'HIGHLIGHT_SETUP', cb: ibl.hooks.cb.highlight_setup, opts: ibl.hooks.options?): string M.register = function(type, cb, opts) utils.validate { type = { type, function(t) return M.type[t] == t end, "hooks type enum", }, cb = { cb, "function" }, opts = { opts, "table", true }, } opts = vim.tbl_deep_extend("keep", opts or {}, default_opts) utils.validate { bufnr = { opts.bufnr, "number", true }, } if opts.bufnr then opts.bufnr = utils.get_bufnr(opts.bufnr) end count = count + 1 local hook_id = type .. "_" .. tostring(count) if opts.bufnr then local bufnr = tostring(opts.bufnr) if not hooks.buffer_scoped[bufnr] then hooks.buffer_scoped[bufnr] = { [M.type.ACTIVE] = {}, [M.type.SCOPE_ACTIVE] = {}, [M.type.SKIP_LINE] = {}, [M.type.WHITESPACE] = {}, [M.type.VIRTUAL_TEXT] = {}, [M.type.SCOPE_HIGHLIGHT] = {}, [M.type.CLEAR] = {}, [M.type.HIGHLIGHT_SETUP] = {}, } end hooks.buffer_scoped[bufnr][type][hook_id] = cb else hooks[type][hook_id] = cb end return hook_id end --- Clears a hook by id --- ---@param id string M.clear = function(id) utils.validate { id = { id, "string" } } local type, hook_id = unpack(utils.split(id, "_")) if not type or not hook_id or not utils.tbl_contains(M.type, type) then return end hooks[type][hook_id] = nil end --- Clears all hooks --- M.clear_all = function() hooks = { [M.type.ACTIVE] = {}, [M.type.SCOPE_ACTIVE] = {}, [M.type.SKIP_LINE] = {}, [M.type.WHITESPACE] = {}, [M.type.VIRTUAL_TEXT] = {}, [M.type.SCOPE_HIGHLIGHT] = {}, [M.type.CLEAR] = {}, [M.type.HIGHLIGHT_SETUP] = {}, buffer_scoped = {}, } end --- Returns all hooks of the given type for a buffer --- ---@param bufnr number ---@param type ibl.hooks.type ---@overload fun(bufnr: number, type: 'ACTIVE'): ibl.hooks.cb.active[] ---@overload fun(bufnr: number, type: 'SCOPE_ACTIVE'): ibl.hooks.cb.scope_active[] ---@overload fun(bufnr: number, type: 'SKIP_LINE'): ibl.hooks.cb.skip_line[] ---@overload fun(bufnr: number, type: 'WHITESPACE'): ibl.hooks.cb.whitespace[] ---@overload fun(bufnr: number, type: 'VIRTUAL_TEXT'): ibl.hooks.cb.virtual_text[] ---@overload fun(bufnr: number, type: 'SCOPE_HIGHLIGHT'): ibl.hooks.cb.scope_highlight[] ---@overload fun(bufnr: number, type: 'CLEAR'): ibl.hooks.cb.clear[] ---@overload fun(bufnr: number, type: 'HIGHLIGHT_SETUP'): ibl.hooks.cb.highlight_setup[] M.get = function(bufnr, type) local bufnr_str = tostring(bufnr) local list = {} for _, hook in pairs(hooks[type]) do table.insert(list, hook) end if hooks.buffer_scoped[bufnr_str] then for _, hook in pairs(hooks.buffer_scoped[bufnr_str][type]) do table.insert(list, hook) end end return list end --- Built in hooks --- --- You can register them yourself using `hooks.register` --- --- --- hooks.register( --- hooks.type.SKIP_LINE, --- hooks.builtin.skip_preproc_lines, --- { bufnr = 0 } --- ) --- M.builtin = { ---@type ibl.hooks.cb.skip_line skip_preproc_lines = function(_, _, _, line) for _, pattern in ipairs { "^#%s*if", "^#%s*ifdef", "^#%s*ifndef", "^#%s*elif", "^#%s*elifdef", "^#%s*elifndef", "^#%s*else", "^#%s*endif", "^#%s*define", "^#%s*undef", "^#%s*warning", "^#%s*error", } do if line:match(pattern) then return true end end return false end, ---@type ibl.hooks.cb.scope_highlight scope_highlight_from_extmark = function(_, bufnr, scope, scope_index) local config = conf.get_config(bufnr) local highlight = config.scope.highlight if type(highlight) ~= "table" then return scope_index end local reverse_hls = {} for i, hl in ipairs(highlight) do reverse_hls[hl] = i end local start_row, start_col = scope:start() local end_row, end_col = scope:end_() local start_line = vim.api.nvim_buf_get_lines(bufnr, start_row, start_row + 1, false)[1] local end_line = vim.api.nvim_buf_get_lines(bufnr, end_row, end_row + 1, false)[1] local end_pos = {} local start_pos = {} local start_pos_scope = {} local end_pos_scope = {} local function inspect_pos(pos) return vim.api.nvim_buf_get_extmarks(bufnr, -1, pos, pos, { type = "highlight", details = true, }) end if end_line then end_pos = inspect_pos { end_row, (end_line:find "%S" or 0) - 1 } end_pos_scope = inspect_pos { end_row, end_col - 1 } end if start_line then start_pos = inspect_pos { start_row, #start_line - 1 } start_pos_scope = inspect_pos { start_row, start_col } end -- it is most accurate to get correct colors from rainbow-delimiters via -- the scope, since you can have something like: -- function() -- ... -- end, -- where the last symbol will give you rainbow-delimiters highlights -- from the comma (nothing) and the last parenthesis (the wrong color) for _, extmark in ipairs(end_pos_scope) do local i = reverse_hls[extmark[4].hl_group] if i ~= nil then return i end end for _, extmark in ipairs(start_pos_scope) do local i = reverse_hls[extmark[4].hl_group] if i ~= nil then return i end end -- For some languages the scope extends before or after the delimiters. Make an attempt to capture them anyway by looking at the first character of the last line, and the last character of the first line. for _, extmark in ipairs(end_pos) do local i = reverse_hls[extmark[4].hl_group] if i ~= nil then return i end end for _, extmark in ipairs(start_pos) do local i = reverse_hls[extmark[4].hl_group] if i ~= nil then return i end end return scope_index end, ---@type ibl.hooks.cb.whitespace hide_first_space_indent_level = function(_, _, _, whitespace_tbl) if whitespace_tbl[1] == indent.whitespace.INDENT then whitespace_tbl[1] = indent.whitespace.SPACE end return whitespace_tbl end, ---@type ibl.hooks.cb.whitespace hide_first_tab_indent_level = function(_, _, _, whitespace_tbl) if whitespace_tbl[1] == indent.whitespace.TAB_START or whitespace_tbl[1] == indent.whitespace.TAB_START_SINGLE then whitespace_tbl[1] = indent.whitespace.TAB_FILL end return whitespace_tbl end, } return M lukas-reineke-indent-blankline.nvim-005b560/lua/ibl/indent.lua000066400000000000000000000101201476603644500242240ustar00rootroot00000000000000local utils = require "ibl.utils" local M = {} ---@enum ibl.indent.whitespace M.whitespace = { TAB_START = 1, TAB_START_SINGLE = 2, TAB_FILL = 3, TAB_END = 4, SPACE = 5, INDENT = 6, } ---@class ibl.indent_state ---@field cap boolean ---@field stack table? ---@class ibl.indent_options ---@field smart_indent_cap boolean ---@field shiftwidth number ---@field tabstop number ---@field vartabstop string --- Takes the whitespace of a line and returns a list of ibl.indent.whitespace --- ---@param whitespace string ---@param opts ibl.indent_options ---@param whitespace_only boolean ---@param indent_state ibl.indent_state? ---@return ibl.indent.whitespace[], ibl.indent_state M.get = function(whitespace, opts, whitespace_only, indent_state) if not indent_state then indent_state = { cap = false, stack = {} } end local shiftwidth = opts.shiftwidth local tabstop = opts.tabstop local vartabstop = opts.vartabstop local spaces = 0 local spaces_since_last_tab = 0 local tabs = 0 local extra = 0 local indent_cap = indent_state.stack[#indent_state.stack] or 0 if indent_state.cap then indent_cap = indent_state.stack[1] or 0 indent_state.cap = false end local varts = utils.tbl_map(tonumber, utils.split(vartabstop, ",", { trimempty = true })) if shiftwidth == 0 then shiftwidth = tabstop end local whitespace_tbl = {} for ch in whitespace:gmatch "." do if ch == "\t" then local tab_width = tabstop - ((spaces_since_last_tab + extra - tabstop) % tabstop) local vart_padding = spaces while #varts > 0 do tabstop = table.remove(varts, 1) if tabstop > spaces then tab_width = tabstop - vart_padding break else vart_padding = vart_padding - tabstop end end tabs = tabs + tab_width if tab_width == 1 then table.insert(whitespace_tbl, M.whitespace.TAB_START_SINGLE) else table.insert(whitespace_tbl, M.whitespace.TAB_START) end for i = 2, tab_width do if i == tab_width then table.insert(whitespace_tbl, M.whitespace.TAB_END) else table.insert(whitespace_tbl, M.whitespace.TAB_FILL) end end spaces_since_last_tab = 0 else local mod = (spaces + tabs + extra) % shiftwidth if utils.tbl_contains(indent_state.stack, spaces + tabs) then table.insert(whitespace_tbl, M.whitespace.INDENT) extra = extra + mod elseif mod == 0 then if #whitespace_tbl < indent_cap or not opts.smart_indent_cap then table.insert(whitespace_tbl, M.whitespace.INDENT) extra = extra + mod else indent_state.cap = true table.insert(whitespace_tbl, M.whitespace.SPACE) end else table.insert(whitespace_tbl, M.whitespace.SPACE) end spaces = spaces + 1 spaces_since_last_tab = spaces_since_last_tab + 1 end end indent_state.stack = utils.tbl_filter(function(a) return a < spaces + tabs end, indent_state.stack) if not whitespace_only then table.insert(indent_state.stack, spaces + tabs) end return whitespace_tbl, indent_state end --- Returns true if the passed whitespace is an indent --- ---@param whitespace ibl.indent.whitespace M.is_indent = function(whitespace) return whitespace == M.whitespace.INDENT or whitespace == M.whitespace.TAB_START or whitespace == M.whitespace.TAB_START_SINGLE end --- Returns true if the passed whitespace belongs to space indent --- ---@param whitespace ibl.indent.whitespace M.is_space_indent = function(whitespace) return whitespace == M.whitespace.INDENT or whitespace == M.whitespace.SPACE end return M lukas-reineke-indent-blankline.nvim-005b560/lua/ibl/init.lua000066400000000000000000000424721476603644500237250ustar00rootroot00000000000000local highlights = require "ibl.highlights" local hooks = require "ibl.hooks" local autocmds = require "ibl.autocmds" local inlay_hints = require "ibl.inlay_hints" local indent = require "ibl.indent" local vt = require "ibl.virt_text" local scp = require "ibl.scope" local conf = require "ibl.config" local utils = require "ibl.utils" local namespace = vim.api.nvim_create_namespace "indent_blankline" local M = {} M.initialized = false ---@type table local global_buffer_state = {} ---@param bufnr number local clear_buffer = function(bufnr) vt.clear_buffer(bufnr) inlay_hints.clear_buffer(bufnr) for _, fn in pairs(hooks.get(bufnr, hooks.type.CLEAR)) do fn(bufnr) end global_buffer_state[bufnr] = nil end ---@param config ibl.config.full local setup = function(config) if not config.enabled then for bufnr, _ in pairs(global_buffer_state) do if not conf.get_config(bufnr).enabled then clear_buffer(bufnr) end end end inlay_hints.setup() highlights.setup() autocmds.setup() M.initialized = true M.refresh_all() end --- Initializes and configures indent-blankline. --- --- Optionally, the first parameter can be a configuration table. --- All values that are not passed in the table are set to the default value. --- List values get merged with the default list value. --- --- `setup` is idempotent, meaning you can call it multiple times, and each call will reset indent-blankline. --- If you want to only update the current configuration, use `update()`. ---@param config ibl.config? M.setup = function(config) setup(conf.set_config(config)) end --- Updates the indent-blankline configuration --- --- The first parameter is a configuration table. --- All values that are not passed in the table are kept as they are. --- List values get merged with the current list value. ---@param config ibl.config M.update = function(config) setup(conf.update_config(config)) end --- Overwrites the indent-blankline configuration --- --- The first parameter is a configuration table. --- All values that are not passed in the table are kept as they are. --- All values that are passed overwrite existing and default values. ---@param config ibl.config M.overwrite = function(config) setup(conf.overwrite_config(config)) end --- Configures indent-blankline for one buffer --- --- All values that are not passed are cleared, and will fall back to the global config ---@param bufnr number ---@param config ibl.config M.setup_buffer = function(bufnr, config) assert(M.initialized, "Tried to setup buffer without doing global setup") bufnr = utils.get_bufnr(bufnr) local c = conf.set_buffer_config(bufnr, config) if c.enabled then M.refresh(bufnr) else clear_buffer(bufnr) end end --- Refreshes indent-blankline in all buffers M.refresh_all = function() for _, win in ipairs(vim.api.nvim_list_wins()) do vim.api.nvim_win_call(win, function() M.refresh(vim.api.nvim_win_get_buf(win) --[[@as number]]) end) end end local debounced_refresh = setmetatable({ timers = {}, queued_buffers = {}, }, { ---@param bufnr number __call = function(self, bufnr) bufnr = utils.get_bufnr(bufnr) local uv = vim.uv or vim.loop if not self.timers[bufnr] then self.timers[bufnr] = uv.new_timer() end if uv.timer_get_due_in(self.timers[bufnr]) <= 50 then self.queued_buffers[bufnr] = nil local config = conf.get_config(bufnr) self.timers[bufnr]:start(config.debounce, 0, function() if self.queued_buffers[bufnr] then self.queued_buffers[bufnr] = nil vim.schedule_wrap(M.refresh)(bufnr) end end) M.refresh(bufnr) else self.queued_buffers[bufnr] = true end end, }) --- Refreshes indent-blankline in one buffer, debounced --- ---@param bufnr number M.debounced_refresh = function(bufnr) if vim.api.nvim_get_current_buf() == bufnr and vim.api.nvim_get_option_value("scrollbind", { scope = "local" }) then for _, b in ipairs(vim.fn.tabpagebuflist()) do debounced_refresh(b) end else debounced_refresh(bufnr) end end --- Refreshes indent-blankline in one buffer --- --- Only use this directly if you know what you are doing, consider `debounced_refresh` instead ---@param bufnr number M.refresh = function(bufnr) assert(M.initialized, "Tried to refresh without doing setup") bufnr = utils.get_bufnr(bufnr) local is_current_buffer = vim.api.nvim_get_current_buf() == bufnr local config = conf.get_config(bufnr) if not config.enabled or not vim.api.nvim_buf_is_loaded(bufnr) or not utils.is_buffer_active(bufnr, config) then clear_buffer(bufnr) return end for _, fn in pairs(hooks.get(bufnr, hooks.type.ACTIVE) --[=[@as ibl.hooks.cb.active[]]=]) do if not fn(bufnr) then clear_buffer(bufnr) return end end local left_offset, top_offset, win_end, win_height = utils.get_offset(bufnr) if top_offset > win_end then return end local offset = math.max(top_offset - 1 - config.viewport_buffer.min, 0) local scope_disabled = false for _, fn in pairs(hooks.get(bufnr, hooks.type.SCOPE_ACTIVE) --[=[@as ibl.hooks.cb.scope_active[]]=]) do if not fn(bufnr) then scope_disabled = true break end end local scope local scope_start_line, scope_end_line if not scope_disabled and config.scope.enabled then scope = scp.get(bufnr, config) if scope and scope:start() >= 0 then local scope_start = scope:start() local scope_end = scope:end_() scope_start_line = vim.api.nvim_buf_get_lines(bufnr, scope_start, scope_start + 1, false)[1] scope_end_line = vim.api.nvim_buf_get_lines(bufnr, scope_end, scope_end + 1, false)[1] end end local range = math.min(win_end + config.viewport_buffer.min, vim.api.nvim_buf_line_count(bufnr)) local lines = vim.api.nvim_buf_get_lines(bufnr, offset, range, false) ---@type ibl.indent_options local indent_opts = { tabstop = vim.api.nvim_get_option_value("tabstop", { buf = bufnr }), vartabstop = vim.api.nvim_get_option_value("vartabstop", { buf = bufnr }), shiftwidth = vim.api.nvim_get_option_value("shiftwidth", { buf = bufnr }), smart_indent_cap = config.indent.smart_indent_cap, } local listchars = utils.get_listchars(bufnr) if listchars.tabstop_overwrite then indent_opts.tabstop = 2 indent_opts.vartabstop = "" end local has_empty_foldtext = utils.has_empty_foldtext(bufnr) local indent_state local next_whitespace_tbl = {} local empty_line_counter = 0 local buffer_state = global_buffer_state[bufnr] or { scope = nil, left_offset = -1, top_offset = -1, tick = 0, } local same_scope = (scope and scope:id()) == (buffer_state.scope and buffer_state.scope:id()) if not same_scope then inlay_hints.clear_buffer(bufnr) end global_buffer_state[bufnr] = { left_offset = left_offset, top_offset = top_offset, scope = scope, tick = buffer_state.tick + 1, } local scope_col_start_single = -1 local scope_row_start, scope_col_start, scope_row_end, scope_col_end = -1, -1, -1, -1 local scope_index = -1 if scope then ---@diagnostic disable-next-line: cast-local-type scope_row_start, scope_col_start, scope_row_end, scope_col_end = scope:range() scope_row_start, scope_col_start, scope_row_end = scope_row_start + 1, scope_col_start + 1, scope_row_end + 1 end local exact_scope_col_start = scope_col_start ---@type ibl.indent.whitespace[] local last_whitespace_tbl = {} ---@type table local line_skipped = {} ---@type ibl.hooks.cb.skip_line[] local skip_line_hooks = hooks.get(bufnr, hooks.type.SKIP_LINE) for i, line in ipairs(lines) do local row = i + offset for _, fn in pairs(skip_line_hooks) do if fn(buffer_state.tick, bufnr, row - 1, line) then line_skipped[i] = true break end end end if scope and scope_start_line then -- find whitespace tables for the start and end lines of scope local whitespace_start = utils.get_whitespace(scope_start_line) local whitespace_end = utils.get_whitespace(scope_end_line) local whitespace_tbl_start = indent.get(whitespace_start, indent_opts, false, nil) local whitespace_tbl_end = indent.get(whitespace_end, indent_opts, false, nil) local whitespace, whitespace_tbl, scope_row -- use the smallest whitespace table of the two to determine the scope index if #whitespace_tbl_end < #whitespace_tbl_start then whitespace = whitespace_end whitespace_tbl = whitespace_tbl_end scope_row = scope_row_end else whitespace = whitespace_start whitespace_tbl = whitespace_tbl_start scope_row = scope_row_start end -- do the same calculations as in the main loop below, but note that the scope -- start and end will never be on a blankline, so these cases simplify a lot whitespace_tbl = utils.fix_horizontal_scroll(whitespace_tbl, left_offset) for _, fn in pairs(hooks.get(bufnr, hooks.type.WHITESPACE) --[=[@as ibl.hooks.cb.whitespace[]]=]) do whitespace_tbl = fn(buffer_state.tick, bufnr, scope_row - 1, whitespace_tbl) end -- calculate the scope index scope_col_start = #whitespace scope_col_start_single = #whitespace_tbl scope_index = #utils.tbl_filter(function(w) return indent.is_indent(w) end, whitespace_tbl) + 1 for _, fn in pairs(hooks.get(bufnr, hooks.type.SCOPE_HIGHLIGHT) --[=[@as ibl.hooks.cb.scope_highlight[]]=]) do scope_index = fn(buffer_state.tick, bufnr, scope, scope_index) end end -- Repeat indent virtual text on wrapped lines when 'breakindent' is set. local repeat_indent = utils.has_repeat_indent(bufnr, config) for i, line in ipairs(lines) do local row = i + offset if line_skipped[i] then vt.clear_buffer(bufnr, row) goto continue end local whitespace = utils.get_whitespace(line) local foldclosed = utils.get_foldclosed(bufnr, row) if is_current_buffer and foldclosed == row and not has_empty_foldtext then local foldtext = utils.get_foldtextresult(bufnr, row) local foldtext_whitespace = utils.get_whitespace(foldtext) if vim.fn.strdisplaywidth(foldtext_whitespace, 0) < vim.fn.strdisplaywidth(whitespace, 0) then vt.clear_buffer(bufnr, row) goto continue end end if is_current_buffer and foldclosed > -1 and foldclosed + win_height < row then vt.clear_buffer(bufnr, row) goto continue end ---@type ibl.indent.whitespace[] local whitespace_tbl local blankline = line:len() == 0 local whitespace_only = not blankline and line == whitespace -- #### calculate indent #### if not blankline then whitespace_tbl, indent_state = indent.get(whitespace, indent_opts, whitespace_only, indent_state) elseif empty_line_counter > 0 then empty_line_counter = empty_line_counter - 1 whitespace_tbl = next_whitespace_tbl else if i == #lines then whitespace_tbl = {} else local j = i + 1 while j < #lines and (lines[j]:len() == 0 or line_skipped[j]) do if not line_skipped[j] then empty_line_counter = empty_line_counter + 1 end j = j + 1 end local j_whitespace = utils.get_whitespace(lines[j]) whitespace_tbl, indent_state = indent.get(j_whitespace, indent_opts, whitespace_only, indent_state) if utils.has_end(lines[j]) then local trail = last_whitespace_tbl[indent_state.stack[#indent_state.stack] + 1] local trail_whitespace = last_whitespace_tbl[indent_state.stack[#indent_state.stack]] if trail then table.insert(whitespace_tbl, trail) elseif trail_whitespace then if indent.is_space_indent(trail_whitespace) then table.insert(whitespace_tbl, indent.whitespace.INDENT) else table.insert(whitespace_tbl, indent.whitespace.TAB_START) end end end end next_whitespace_tbl = whitespace_tbl end local scope_active = row >= scope_row_start and row <= scope_row_end if scope_active and scope_col_start_single > -1 and (whitespace_tbl[scope_col_start_single + 1] or blankline) and not indent.is_indent(whitespace_tbl[scope_col_start_single + 1]) then local ref = whitespace_tbl[scope_col_start_single + 1] or last_whitespace_tbl[scope_col_start_single + 1] if ref then if indent.is_space_indent(ref) then whitespace_tbl[scope_col_start_single + 1] = indent.whitespace.INDENT else whitespace_tbl[scope_col_start_single + 1] = indent.whitespace.TAB_START end local k = scope_col_start_single while not whitespace_tbl[k] and k >= 0 do whitespace_tbl[k] = indent.whitespace.SPACE k = k - 1 end end end -- remove blankline trail if blankline and config.whitespace.remove_blankline_trail then while #whitespace_tbl > 0 do if indent.is_indent(whitespace_tbl[#whitespace_tbl]) then break end table.remove(whitespace_tbl, #whitespace_tbl) end end -- Fix horizontal scroll whitespace_tbl = utils.fix_horizontal_scroll(whitespace_tbl, left_offset) for _, fn in pairs(hooks.get(bufnr, hooks.type.WHITESPACE) --[=[@as ibl.hooks.cb.whitespace[]]=]) do whitespace_tbl = fn(buffer_state.tick, bufnr, row - 1, whitespace_tbl) end last_whitespace_tbl = whitespace_tbl -- #### make virtual text #### local scope_start = row == scope_row_start local scope_end = row == scope_row_end local char_map = vt.get_char_map(config, listchars, whitespace_only, blankline) local virt_text, scope_hl = vt.get( config, char_map, whitespace_tbl, scope_active, scope_index, scope_start, scope_end, scope_col_start_single ) -- #### set virtual text #### vt.clear_buffer(bufnr, row) -- Show exact scope local scope_col_start_draw = #whitespace local scope_show_end_cond = #whitespace_tbl > scope_col_start_single if config.scope.show_exact_scope then scope_col_start_draw = exact_scope_col_start - 1 scope_show_end_cond = #whitespace_tbl >= scope_col_start_single end -- Scope start if config.scope.show_start and scope_start then vim.api.nvim_buf_set_extmark(bufnr, namespace, row - 1, scope_col_start_draw, { end_col = #line, hl_group = scope_hl.underline, priority = config.scope.priority, strict = false, }) inlay_hints.set(bufnr, row - 1, #whitespace, scope_hl.underline, scope_hl.underline) end -- Scope end if config.scope.show_end and scope_end and scope_show_end_cond then vim.api.nvim_buf_set_extmark(bufnr, namespace, row - 1, scope_col_start, { end_col = scope_col_end, hl_group = scope_hl.underline, priority = config.scope.priority, strict = false, }) inlay_hints.set(bufnr, row - 1, #whitespace, scope_hl.underline, scope_hl.underline) end for _, fn in pairs(hooks.get(bufnr, hooks.type.VIRTUAL_TEXT) --[=[@as ibl.hooks.cb.virtual_text[]]=]) do virt_text = fn(buffer_state.tick, bufnr, row - 1, virt_text) end -- Indent if #virt_text > 0 then vim.api.nvim_buf_set_extmark(bufnr, namespace, row - 1, 0, { virt_text = virt_text, virt_text_pos = "overlay", virt_text_repeat_linebreak = repeat_indent or nil, hl_mode = "combine", priority = config.indent.priority, strict = false, }) end ::continue:: end end return M lukas-reineke-indent-blankline.nvim-005b560/lua/ibl/inlay_hints.lua000066400000000000000000000055121476603644500252750ustar00rootroot00000000000000local namespace = "vim_lsp_inlayhint" if vim.fn.has "nvim-0.11" == 1 then namespace = "nvim.lsp.inlayhint" end local inlayhint_namespace = vim.api.nvim_create_namespace(namespace) local M = {} ---@type function? local handler = nil ---@type table local buffer_state = {} local inlay_hint_timer = vim.uv.new_timer() ---@param bufnr number ---@param row number ---@param col number ---@param hl string|string[] ---@param hl_empty string local set_extmark = function(bufnr, row, col, hl, hl_empty) if not vim.api.nvim_buf_is_loaded(bufnr) then return end local inlayhint_extmarks = vim.api.nvim_buf_get_extmarks( bufnr, inlayhint_namespace, { row, col }, { row, -1 }, { details = true, hl_name = false, type = "virt_text" } ) vim.api.nvim_buf_clear_namespace(bufnr, inlayhint_namespace, row, row + 1) for _, inlay in ipairs(inlayhint_extmarks or {}) do local _, inlay_row, inlay_col, inlay_opt = unpack(inlay) for _, virt_text in ipairs(inlay_opt.virt_text) do if vim.trim(virt_text[1]) == "" then virt_text[2] = hl_empty else virt_text[2] = hl end end inlay_opt.ns_id = nil pcall(vim.api.nvim_buf_set_extmark, bufnr, inlayhint_namespace, inlay_row, inlay_col, inlay_opt) end end M.setup = function() if not handler then handler = vim.lsp.handlers["textDocument/inlayHint"] vim.lsp.handlers["textDocument/inlayHint"] = function(err, result, ctx) local response if handler then response = handler(err, result, ctx) end if inlay_hint_timer then inlay_hint_timer:start( 0, -- refresh after the next nvim__redraw 0, vim.schedule_wrap(function() require("ibl").debounced_refresh(ctx.bufnr) end) ) end return response end end end M.clear = function() if handler then vim.lsp.handlers["textDocument/inlayHint"] = handler handler = nil end for bufnr, _ in pairs(buffer_state) do M.clear_buffer(bufnr) end end ---@param bufnr number M.clear_buffer = function(bufnr) for _, row in ipairs(buffer_state[bufnr] or {}) do pcall(set_extmark, bufnr, row, 0, "LspInlayHint", "") end buffer_state[bufnr] = nil end ---@param bufnr number ---@param row number ---@param col number ---@param hl string ---@param hl_empty string M.set = function(bufnr, row, col, hl, hl_empty) if not buffer_state[bufnr] then buffer_state[bufnr] = {} end table.insert(buffer_state[bufnr], row) set_extmark(bufnr, row, col, { "LspInlayHint", hl }, hl_empty) end return M lukas-reineke-indent-blankline.nvim-005b560/lua/ibl/scope.lua000066400000000000000000000051541476603644500240670ustar00rootroot00000000000000local utils = require "ibl.utils" local scope_lang = require "ibl.scope_languages" local M = {} ---@param win number ---@return table M.get_cursor_range = function(win) local pos = vim.api.nvim_win_get_cursor(win) local row, col = pos[1] - 1, pos[2] return { row, 0, row, col } end --- Takes a language tree and a range, and returns the child language tree for that range --- ---@param language_tree vim.treesitter.LanguageTree ---@param range table ---@param config ibl.config.full M.language_for_range = function(language_tree, range, config) if config.scope.injected_languages then for _, child in pairs(language_tree:children()) do if child:contains(range) then local lang_tree = M.language_for_range(child, range, config) if lang_tree then return lang_tree end end end end if not utils.tbl_contains(config.scope.exclude.language, language_tree:lang()) then return language_tree end end ---@param bufnr number ---@param config ibl.config.full ---@return TSNode? M.get = function(bufnr, config) local lang_tree_ok, lang_tree = pcall(vim.treesitter.get_parser, bufnr) if not lang_tree_ok or not lang_tree then return nil end local win if bufnr ~= vim.api.nvim_get_current_buf() then win = utils.get_win(bufnr) if not win then return nil end else win = 0 end local range = M.get_cursor_range(win) lang_tree = M.language_for_range(lang_tree, range, config) if not lang_tree then return nil end local lang = lang_tree:lang() if not scope_lang[lang] and not config.scope.include.node_type[lang] then return nil end local node = lang_tree:named_node_for_range(range, { bufnr = bufnr }) if not node then return nil end local excluded_node_types = utils.tbl_join(config.scope.exclude.node_type["*"] or {}, config.scope.exclude.node_type[lang] or {}) local include_node_types = utils.tbl_join(config.scope.include.node_type["*"] or {}, config.scope.include.node_type[lang] or {}) while node and node:byte_length() > 0 do local type = node:type() if ((scope_lang[lang] and scope_lang[lang][type]) and not utils.tbl_contains(excluded_node_types, type)) or utils.tbl_contains(include_node_types, type) or utils.tbl_contains(include_node_types, "*") then return node else node = node:parent() end end end return M lukas-reineke-indent-blankline.nvim-005b560/lua/ibl/scope_languages.lua000066400000000000000000000405531476603644500261170ustar00rootroot00000000000000-- from nvim-treesitter/queries/{lang}/locals local M = { ada = { compilation = true, package_declaration = true, package_body = true, subprogram_declaration = true, subprogram_body = true, block_statement = true, }, bash = { function_definition = true, }, bass = { list = true, scope = true, cons = true, }, bicep = { infrastructure = true, call_expression = true, lambda_expression = true, subscript_expression = true, if_statement = true, for_statement = true, array = true, object = true, interpolation = true, }, bitbake = { python_function_definition = true, dictionary_comprehension = true, list_comprehension = true, set_comprehension = true, }, c = { preproc_function_def = true, for_statement = true, if_statement = true, while_statement = true, function_definition = true, compound_statement = true, struct_specifier = true, }, c_sharp = { block = true, }, cairo = { block = true, function_definition = true, loop_expression = true, if_expression = true, match_expression = true, match_arm = true, struct_item = true, enum_item = true, impl_item = true, }, capnp = { message = true, annotation_targets = true, const_list = true, enum = true, interface = true, implicit_generics = true, generics = true, group = true, method_parameters = true, named_return_types = true, struct = true, struct_shorthand = true, union = true, }, commonlisp = { defun = true, sym_lit = true, loop_macro = true, list_lit = true, }, corn = { object = true, array = true, }, cpon = { meta_map = true, map = true, array = true, }, cue = { field = true, for_clause = true, }, dart = { body = true, block = true, if_statement = true, for_statement = true, while_statement = true, try_statement = true, catch_clause = true, finally_clause = true, }, devicetree = { node = true, integer_cells = true, }, ecma = { statement_block = true, ["function"] = true, arrow_function = true, function_declaration = true, method_definition = true, for_statement = true, for_in_statement = true, catch_clause = true, }, elixir = { call = true, stab_clause = true, }, elsa = { reduction = true, }, fennel = { fn = true, lambda = true, let = true, each = true, ["for"] = true, match = true, }, firrtl = { circuit = true, module = true, ["else"] = true, when = true, }, fish = { command = true, function_definition = true, if_statement = true, for_statement = true, begin_statement = true, while_statement = true, switch_statement = true, }, forth = { word_definition = true, }, fusion = { block = true, eel_arrow_function = true, eel_object = true, }, gdscript = { if_statement = true, elif_clause = true, else_clause = true, for_statement = true, while_statement = true, function_definition = true, constructor_definition = true, class_definition = true, match_statement = true, pattern_section = true, lambda = true, get_body = true, set_body = true, }, git_config = { section = true, }, gleam = { function_body = true, case_clause = true, }, glimmer = { element_node = true, block_statement = true, }, go = { func_literal = true, function_declaration = true, if_statement = true, block = true, expression_switch_statement = true, for_statement = true, method_declaration = true, }, godot_resource = { section = true, }, hare = { module = true, function_declaration = true, if_statement = true, for_statement = true, match_expression = true, switch_expression = true, }, heex = { component = true, slot = true, tag = true, }, html = { element = true, }, java = { body = true, lambda_expression = true, enhanced_for_statement = true, block = true, if_statement = true, consequence = true, alternative = true, try_statement = true, catch_clause = true, for_statement = true, constructor_declaration = true, method_declaration = true, }, json = { object = true, array = true, }, jsonnet = { parenthesis = true, anonymous_function = true, object = true, field = true, local_bind = true, }, julia = { function_definition = true, short_function_definition = true, macro_definition = true, for_statement = true, while_statement = true, try_statement = true, catch_clause = true, finally_clause = true, let_statement = true, quote_statement = true, do_clause = true, }, kconfig = { config = true, menuconfig = true, choice = true, comment_entry = true, menu = true, ["if"] = true, }, kdl = { node = true, node_children = true, }, kotlin = { if_expression = true, when_expression = true, when_entry = true, for_statement = true, while_statement = true, do_while_statement = true, lambda_literal = true, function_declaration = true, primary_constructor = true, secondary_constructor = true, anonymous_initializer = true, class_declaration = true, enum_class_body = true, enum_entry = true, interpolated_expression = true, }, lua = { do_statement = true, while_statement = true, repeat_statement = true, if_statement = true, for_statement = true, function_declaration = true, function_definition = true, }, matlab = { function_definition = true, }, mlir = { region = true, }, nim = { ["if"] = true, case = true, try = true, ["for"] = true, ["while"] = true, block = true, static_statement = true, proc_declaration = true, func_declaration = true, method_declaration = true, iterator_declaration = true, converter_declaration = true, template_declaration = true, macro_declaration = true, proc_expression = true, func_expression = true, iterator_expression = true, concept_declaration = true, }, nix = { let_expression = true, rec_attrset_expression = true, function_expression = true, }, ocaml = { compilation_unit = true, structure = true, signature = true, module_binding = true, functor = true, let_binding = true, match_case = true, class_binding = true, class_function = true, method_definition = true, let_expression = true, fun_expression = true, for_expression = true, let_class_expression = true, object_expression = true, attribute_payload = true, }, odin = { block = true, declaration = true, statement = true, }, pascal = { root = true, defProc = true, lambda = true, declProc = true, declProcRef = true, exceptionHandler = true, }, perl = { block = true, block_statement = true, }, php = { class_declaration = true, method_declaration = true, function_definition = true, anonymous_function = true, }, pony = { use_statement = true, actor_definition = true, class_definition = true, primitive_definition = true, interface_definition = true, trait_definition = true, struct_definition = true, constructor = true, method = true, behavior = true, if_statement = true, iftype_statement = true, elseif_block = true, elseiftype_block = true, else_block = true, for_statement = true, while_statement = true, try_statement = true, with_statement = true, repeat_statement = true, recover_statement = true, match_statement = true, case_statement = true, parenthesized_expression = true, tuple_expression = true, array_literal = true, object_literal = true, }, puppet = { block = true, defined_resource_type = true, parameter_list = true, attribute_type_entry = true, class_definition = true, node_definition = true, resource_declaration = true, selector = true, method_call = true, case_statement = true, hash = true, array = true, }, python = { module = true, class_definition = true, function_definition = true, dictionary_comprehension = true, list_comprehension = true, set_comprehension = true, }, ql = { module = true, dataclass = true, datatype = true, select = true, body = true, conjunction = true, }, query = { named_node = true, anonymous_node = true, grouping = true, }, r = { function_definition = true, }, rasi = { rule_set = true, }, re2c = { body = true, }, risor = { function_declaration = true, if_statement = true, block = true, switch_statement = true, for_statement = true, }, ron = { array = true, map = true, struct = true, tuple = true, }, rst = { directive = true, }, ruby = { method = true, class = true, block = true, do_block = true, }, rust = { block = true, function_item = true, closure_expression = true, while_expression = true, for_expression = true, loop_expression = true, if_expression = true, match_expression = true, match_arm = true, expression_statement = true, struct_item = true, enum_item = true, impl_item = true, }, scala = { template_body = true, lambda_expression = true, function_definition = true, block = true, }, smali = { class_directive = true, expression = true, annotation_directive = true, array_data_directive = true, method_definition = true, packed_switch_directive = true, sparse_switch_directive = true, subannotation_directive = true, }, sparql = { triples_block = true, }, squirrel = { script = true, class_declaration = true, enum_declaration = true, function_declaration = true, attribute_declaration = true, array = true, block = true, table = true, anonymous_function = true, parenthesized_expression = true, if_statement = true, else_statement = true, while_statement = true, do_while_statement = true, switch_statement = true, for_statement = true, foreach_statement = true, try_statement = true, catch_statement = true, }, starlark = { function_definition = true, dictionary_comprehension = true, list_comprehension = true, set_comprehension = true, }, supercollider = { function_call = true, code_block = true, function_block = true, control_structure = true, }, swift = { statements = true, for_statement = true, while_statement = true, repeat_while_statement = true, do_statement = true, if_statement = true, guard_statement = true, switch_statement = true, property_declaration = true, function_declaration = true, class_declaration = true, protocol_declaration = true, }, systemtap = { function_definition = true, statement_block = true, if_statement = true, while_statement = true, for_statement = true, foreach_statement = true, catch_clause = true, }, t32 = { block = true, }, tablegen = { class = true, multiclass = true, def = true, defm = true, defset = true, defvar = true, foreach = true, ["if"] = true, let = true, }, teal = { anon_function = true, function_statement = true, if_statement = true, for_body = true, repeat_statement = true, while_body = true, do_statement = true, }, thrift = { definition = true, }, tiger = { for_expression = true, let_expression = true, function_declaration = true, }, tlaplus = { bounded_quantification = true, choose = true, function_definition = true, function_literal = true, lambda = true, let_in = true, module = true, module_definition = true, operator_definition = true, set_filter = true, set_map = true, unbounded_quantification = true, non_terminal_proof = true, suffices_proof_step = true, theorem = true, pcal_algorithm = true, pcal_macro = true, pcal_procedure = true, pcal_with = true, }, toml = { table = true, table_array_element = true, }, turlte = { turtle_doc = true, }, ungrammar = { grammar = true, }, usd = { block = true, metadata = true, }, uxntal = { macro = true, memory_execution = true, subroutine = true, }, v = { function_declaration = true, if_expression = true, block = true, for_statement = true, }, verilog = { loop_generate_construct = true, loop_statement = true, conditional_statement = true, case_item = true, function_declaration = true, always_construct = true, module_declaration = true, }, vim = { function_definition = true, }, wing = { block = true, }, yaml = { block_node = true, }, yuck = { ast_block = true, list = true, array = true, expr = true, json_array = true, json_object = true, parenthesized_expression = true, }, } M.cpp = vim.tbl_extend("keep", M.c, { class_specifier = true, template_declaration = true, body = true, template_function = true, template_method = true, function_declarator = true, lambda_expression = true, catch_clause = true, requires_expression = true, }) M.angular = M.html M.arduino = M.cpp M.cuda = M.cpp M.astro = M.html M.glsl = M.c M.hjson = M.json M.hlsl = M.cpp M.ispc = vim.tbl_extend("keep", M.c, { template_declaration = true, foreach_statement = true, foreach_instance_statement = true, unmasked_statement = true, }) M.javascript = vim.tbl_extend("keep", M.ecma, { jsx_element = true }) M.jsonc = M.json M.luau = M.lua M.nqc = M.c M.objc = M.c M.ocaml_interface = M.ocaml M.svelte = M.html M.tsx = vim.tbl_extend("keep", M.ecma, { jsx_element = true }) M.typescript = M.ecma M.vue = vim.tbl_extend("keep", M.html, { template_element = true, }) return M lukas-reineke-indent-blankline.nvim-005b560/lua/ibl/utils.lua000066400000000000000000000353421476603644500241200ustar00rootroot00000000000000local M = {} local has_repeat = vim.fn.has "nvim-0.10" == 1 ---@param line string? M.get_whitespace = function(line) if not line then return "" end return string.match(line, "^%s+") or "" end --- Use the faster validate version if available. --- NOTE: We disable some Lua diagnostics here since lua_ls isn't smart enough to --- realize that we're using an overloaded function. ---@param spec table M.validate = function(spec) if vim.fn.has "nvim-0.11" == 1 then for key, key_spec in pairs(spec) do local message = type(key_spec[3]) == "string" and key_spec[3] or nil --[[@as string?]] local optional = type(key_spec[3]) == "boolean" and key_spec[3] or nil --[[@as boolean?]] ---@diagnostic disable-next-line:param-type-mismatch, redundant-parameter vim.validate(key, key_spec[1], key_spec[2], optional, message) end else ---@diagnostic disable-next-line:param-type-mismatch vim.validate(spec) end end ---@param opt table ---@param input table ---@param path string M.validate_config = function(opt, input, path) M.validate(opt) for key, _ in pairs(input) do if not opt[key] then error(string.format("'%s' is not a valid key of %s", key, path)) end end end --- copy of vim.spit without vim.validate --- ---@param s string String to split ---@param sep string Separator or pattern ---@param opts (table|nil) Keyword arguments |kwargs| accepted by |vim.gsplit()| ---@return string[] List of split components function M.split(s, sep, opts) local t = {} for c in M.gsplit(s, sep, opts) do table.insert(t, c) end return t end --- copy of vim.gsplit without vim.validate --- --- @param s string String to split --- @param sep string Separator or pattern --- @param opts (table|nil) Keyword arguments |kwargs|: --- - plain: (boolean) Use `sep` literally (as in string.find). --- - trimempty: (boolean) Discard empty segments at start and end of the sequence. ---@return fun():string|nil (function) Iterator over the split components function M.gsplit(s, sep, opts) local plain local trimempty = false if type(opts) == "boolean" then plain = opts -- For backwards compatibility. else opts = opts or {} plain, trimempty = opts.plain, opts.trimempty end local start = 1 local done = false -- For `trimempty`: queue of collected segments, to be emitted at next pass. local segs = {} local empty_start = true -- Only empty segments seen so far. local function _pass(i, j, ...) if i then assert(j + 1 > start, "Infinite loop detected") local seg = s:sub(start, i - 1) start = j + 1 return seg, ... else done = true return s:sub(start) end end return function() if trimempty and #segs > 0 then -- trimempty: Pop the collected segments. return table.remove(segs) elseif done or (s == "" and sep == "") then return nil elseif sep == "" then if start == #s then done = true end return _pass(start + 1, start) end local seg = _pass(s:find(sep, start, plain)) -- Trim empty segments from start/end. if trimempty and seg ~= "" then empty_start = false elseif trimempty and seg == "" then while not done and seg == "" do table.insert(segs, 1, "") seg = _pass(s:find(sep, start, plain)) end if done and seg == "" then return nil elseif empty_start then empty_start = false segs = {} return seg end if seg ~= "" then table.insert(segs, 1, seg) end return table.remove(segs) end return seg end end --- copy of vim.tbl_contains without vim.validate --- ---@param t table Table to check ---@param value any Value to compare or predicate function reference ---@param opts table? Keyword arguments |kwargs|: --- - predicate: (boolean) `value` is a function reference to be checked (default false) ---@return boolean `true` if `t` contains `value` M.tbl_contains = function(t, value, opts) local pred if opts and opts.predicate then pred = value else pred = function(v) return v == value end end for _, v in pairs(t) do if pred(v) then return true end end return false end --- copy of vim.tbl_count without vim.validate --- ---@param t table Table ---@return integer Number of non-nil values in table M.tbl_count = function(t) local count = 0 for _ in pairs(t) do count = count + 1 end return count end --- copy of vim.tbl_map without vim.validate --- ---@generic T ---@param func fun(value: T): any (function) Function ---@param t table (table) Table ---@return table Table of transformed values M.tbl_map = function(func, t) local rettab = {} for k, v in pairs(t) do rettab[k] = func(v) end return rettab end --- copy of vim.tbl_filter without vim.validate --- ---@generic T ---@param func fun(value: T): boolean (function) Function ---@param t table (table) Table ---@return T[] (table) Table of filtered values M.tbl_filter = function(func, t) local rettab = {} for _, entry in pairs(t) do if func(entry) then table.insert(rettab, entry) end end return rettab end ---@param codepoint integer M.utf8_encode = function(codepoint) if codepoint <= 0x7F then return string.char(codepoint) elseif codepoint <= 0x7FF then return string.char(0xC0 + math.floor(codepoint / 0x40), 0x80 + (codepoint % 0x40)) elseif codepoint <= 0xFFFF then return string.char( 0xE0 + math.floor(codepoint / 0x1000), 0x80 + math.floor((codepoint % 0x1000) / 0x40), 0x80 + (codepoint % 0x40) ) else return string.char( 0xF0 + math.floor(codepoint / 0x40000), 0x80 + math.floor((codepoint % 0x40000) / 0x1000), 0x80 + math.floor((codepoint % 0x1000) / 0x40), 0x80 + (codepoint % 0x40) ) end end ---@param input string? M.encode = function(input) return ( input and input :gsub("\\x%x%x", function(hex) return string.char(tonumber(hex:sub(3, 4), 16)) end) :gsub("\\u%x%x%x%x", function(hex) return M.utf8_encode(tonumber(hex:sub(3, 6), 16)) end) :gsub("\\U%x%x%x%x%x%x%x%x", function(hex) -- Note: This won't work for characters outside the range Lua's string can handle. return M.utf8_encode(tonumber(hex:sub(3, 10), 16)) end) ) end ---@param bufnr number ---@return number? M.get_win = function(bufnr) local win_list = vim.fn.win_findbuf(bufnr) local current_tab = vim.api.nvim_get_current_tabpage() for _, win in ipairs(win_list or {}) do if current_tab == vim.api.nvim_win_get_tabpage(win) then return win end end return win_list and win_list[1] end ---@class ibl.listchars ---@field tabstop_overwrite boolean ---@field space_char string ---@field trail_char string? ---@field lead_char string? ---@field multispace_chars string[]? ---@field leadmultispace_chars string[]? ---@field tab_char_start string? ---@field tab_char_fill string ---@field tab_char_end string? ---@param bufnr number ---@return ibl.listchars M.get_listchars = function(bufnr) local listchars ---@diagnostic disable-next-line local list = vim.opt.list:get() if list then listchars = vim.opt.listchars:get() end if bufnr ~= vim.api.nvim_get_current_buf() then local win = M.get_win(bufnr) if win then list = vim.api.nvim_get_option_value("list", { win = win }) if list then local raw_value = vim.api.nvim_get_option_value("listchars", { win = win }) listchars = {} for _, key_value_str in ipairs(M.split(raw_value, ",")) do local key, value = unpack(M.split(key_value_str, ":")) listchars[vim.trim(key)] = value end end end end if list then local tabstop_overwrite = false local tab_char local space_char = M.encode(listchars.space) or " " local multispace_chars local leadmultispace_chars if listchars.tab then tab_char = vim.fn.split(M.encode(listchars.tab), "\\zs") else tabstop_overwrite = true tab_char = { "^", "I" } end if listchars.multispace then multispace_chars = vim.fn.split(M.encode(listchars.multispace), "\\zs") end if listchars.leadmultispace then leadmultispace_chars = vim.fn.split(M.encode(listchars.leadmultispace), "\\zs") end return { tabstop_overwrite = tabstop_overwrite, space_char = space_char, trail_char = M.encode(listchars.trail), multispace_chars = multispace_chars, leadmultispace_chars = leadmultispace_chars, lead_char = M.encode(listchars.lead), tab_char_start = tab_char[1] or space_char, tab_char_fill = tab_char[2] or space_char, tab_char_end = tab_char[3], } end return { tabstop_overwrite = false, space_char = " ", trail_char = nil, lead_char = nil, multispace_chars = nil, leadmultispace_chars = nil, tab_char_start = nil, tab_char_fill = " ", tab_char_end = nil, } end ---@param bufnr number M.get_filetypes = function(bufnr) local filetype = vim.api.nvim_get_option_value("filetype", { buf = bufnr }) if filetype == "" then return { "" } end return M.split(filetype, ".", { plain = true, trimempty = true }) end local has_end_reg = vim.regex "^\\s*\\(}\\|]\\|)\\|end\\)" ---@param line string M.has_end = function(line) if has_end_reg and has_end_reg:match_str(line) ~= nil then return true end return false end ---@param bufnr number ---@return number, number, number, number M.get_offset = function(bufnr) local win local win_view local win_end if bufnr == vim.api.nvim_get_current_buf() then win = 0 win_view = vim.fn.winsaveview() win_end = vim.fn.line "w$" else win = M.get_win(bufnr) if not win then return 0, 0, 0, 0 end win_view = vim.api.nvim_win_call(win, vim.fn.winsaveview) end local win_height = vim.api.nvim_win_get_height(win) if not win_end then win_end = win_height + (win_view.topline or 0) end if win_view.lnum > win_end then win_view.topline = win_view.lnum win_end = win_view.lnum + win_height end return win_view.leftcol or 0, win_view.topline or 0, win_end, win_height end ---@param bufnr number ---@param row number ---@return number M.get_foldclosed = function(bufnr, row) if bufnr == vim.api.nvim_get_current_buf() then return vim.fn.foldclosed(row) or -1 else local win = M.get_win(bufnr) if not win then return -1 end return vim.api.nvim_win_call(win, function() ---@diagnostic disable-next-line: redundant-return-value return vim.fn.foldclosed(row) or -1 end) end end ---@param bufnr number ---@param row number ---@return string M.get_foldtextresult = function(bufnr, row) if bufnr == vim.api.nvim_get_current_buf() then return vim.fn.foldtextresult(row) or "" else local win = M.get_win(bufnr) if not win then return "" end return vim.api.nvim_win_call(win, function() ---@diagnostic disable-next-line: redundant-return-value return vim.fn.foldtextresult(row) or "" end) end end ---@param bufnr number ---@return boolean M.has_empty_foldtext = function(bufnr) if vim.fn.has "nvim-0.10" == 0 then return false end local win = M.get_win(bufnr) if not win then return false end return vim.api.nvim_get_option_value("foldtext", { win = win }) == "" end ---@param bufnr number ---@param config ibl.config M.is_buffer_active = function(bufnr, config) for _, filetype in ipairs(M.get_filetypes(bufnr)) do if M.tbl_contains(config.exclude.filetypes, filetype) then return false end end local buftype = vim.api.nvim_get_option_value("buftype", { buf = bufnr }) if M.tbl_contains(config.exclude.buftypes, buftype) then return false end return true end ---@param bufnr number ---@return number M.get_bufnr = function(bufnr) if not bufnr or bufnr == 0 then return vim.api.nvim_get_current_buf() --[[@as number]] end return bufnr end ---@generic T: table ---@vararg T ---@return T M.tbl_join = function(...) ---@diagnostic disable-next-line: deprecated return vim.iter and vim.iter({ ... }):flatten():totable() or vim.tbl_flatten { ... } end ---@generic T ---@param list T[] ---@param i number ---@return T M.tbl_get_index = function(list, i) return list[((i - 1) % #list) + 1] end ---@param whitespace_tbl ibl.indent.whitespace[] ---@param left_offset number ---@return ibl.indent.whitespace[] M.fix_horizontal_scroll = function(whitespace_tbl, left_offset) local current_left_offset = left_offset while #whitespace_tbl > 0 and current_left_offset > 0 do table.remove(whitespace_tbl, 1) current_left_offset = current_left_offset - 1 end return whitespace_tbl end ---@param bufnr number ---@param config ibl.config ---@return boolean M.has_repeat_indent = function(bufnr, config) if not config.indent.repeat_linebreak then return false end if not has_repeat then return false end local win = M.get_win(bufnr) if not vim.api.nvim_get_option_value("breakindent", { win = win }) then return false end local raw_value = vim.api.nvim_get_option_value("breakindentopt", { win = win }) for _, key_value_str in ipairs(M.split(raw_value, ",")) do local key, value = unpack(M.split(key_value_str, ":")) key = vim.trim(key) if key == "column" then return false end if key == "sbr" then return false end if key == "shift" and (tonumber(value) or 0) < 0 then return false end end return true end return M lukas-reineke-indent-blankline.nvim-005b560/lua/ibl/virt_text.lua000066400000000000000000000105711476603644500250050ustar00rootroot00000000000000local highlights = require "ibl.highlights" local utils = require "ibl.utils" local indent = require "ibl.indent" local whitespace = indent.whitespace local M = {} ---@alias ibl.virtual_text { [1]: string, [2]: string|string[] }[] ---@alias ibl.char_map { [ibl.indent.whitespace]: string|string[] } ---@param input string|string[] ---@param index number ---@return string local get_char = function(input, index) if type(input) == "string" then return input end return utils.tbl_get_index(input, index) end ---@param config ibl.config ---@param listchars ibl.listchars ---@param whitespace_only boolean ---@param blankline boolean ---@return ibl.char_map M.get_char_map = function(config, listchars, whitespace_only, blankline) return { [whitespace.TAB_START] = config.indent.tab_char or listchars.tab_char_start or config.indent.char, [whitespace.TAB_START_SINGLE] = config.indent.tab_char or listchars.tab_char_end or listchars.tab_char_start or config.indent.char, [whitespace.TAB_FILL] = (blankline and " ") or listchars.tab_char_fill, [whitespace.TAB_END] = (blankline and " ") or listchars.tab_char_end or listchars.tab_char_fill, [whitespace.SPACE] = (blankline and " ") or (whitespace_only and (listchars.trail_char or listchars.multispace_chars or listchars.space_char)) or listchars.leadmultispace_chars or listchars.lead_char or listchars.multispace_chars or listchars.space_char, [whitespace.INDENT] = config.indent.char, } end ---@param bufnr number ---@param row number? M.clear_buffer = function(bufnr, row) local namespace = vim.api.nvim_create_namespace "indent_blankline" local line_start = 0 local line_end = -1 if row then line_start = row - 1 line_end = row end pcall(vim.api.nvim_buf_clear_namespace, bufnr, namespace, line_start, line_end) end ---@param config ibl.config ---@param char_map ibl.char_map ---@param whitespace_tbl ibl.indent.whitespace[] ---@param scope_active boolean ---@param scope_index number ---@param scope_start boolean ---@param scope_end boolean ---@param scope_col_start_single number ---@return ibl.virtual_text, ibl.highlight M.get = function( config, char_map, whitespace_tbl, scope_active, scope_index, scope_start, scope_end, scope_col_start_single ) local scope_hl = utils.tbl_get_index(highlights.scope, scope_index) local indent_index = 1 local virt_text = {} local scope_edge = (config.scope.show_start and scope_start and not config.scope.show_exact_scope) or (config.scope.show_end and scope_end) for i, ws in ipairs(whitespace_tbl) do local whitespace_hl = utils.tbl_get_index(highlights.whitespace, indent_index - 1).char local indent_hl local underline_hl local sa = scope_active local char = get_char(char_map[ws], (ws == whitespace.SPACE and i) or indent_index) if indent.is_indent(ws) then whitespace_hl = utils.tbl_get_index(highlights.whitespace, indent_index).char if vim.fn.strwidth(char) == 0 then char = char_map[whitespace.SPACE] --[[@as string]] sa = false else indent_hl = utils.tbl_get_index(highlights.indent, indent_index).char end indent_index = indent_index + 1 end if sa and scope_edge and i - 1 > scope_col_start_single then scope_hl = utils.tbl_get_index(highlights.scope, scope_index) underline_hl = scope_hl.underline end if sa and i - 1 == scope_col_start_single then if not scope_start then indent_hl = scope_hl.char if config.scope.char then local scope_char = get_char(config.scope.char, scope_index) if vim.fn.strwidth(scope_char) == 1 then char = scope_char end end end if scope_edge then underline_hl = scope_hl.underline end end table.insert(virt_text, { char, utils.tbl_filter(function(v) return v ~= nil end, { whitespace_hl, indent_hl, underline_hl }), }) end return virt_text, scope_hl end return M lukas-reineke-indent-blankline.nvim-005b560/lua/indent_blankline.lua000066400000000000000000000005121476603644500255010ustar00rootroot00000000000000return { setup = function() vim.notify_once( "You are trying to call the setup function of indent-blankline version 2, but you have version 3 installed.\nTake a look at the GitHub wiki for instructions on how to migrate, or revert back to version 2.", vim.log.levels.ERROR ) end, } lukas-reineke-indent-blankline.nvim-005b560/specs/000077500000000000000000000000001476603644500220345ustar00rootroot00000000000000lukas-reineke-indent-blankline.nvim-005b560/specs/features/000077500000000000000000000000001476603644500236525ustar00rootroot00000000000000lukas-reineke-indent-blankline.nvim-005b560/specs/features/config_spec.lua000066400000000000000000000120111476603644500266270ustar00rootroot00000000000000assert = require "luassert" local conf = require "ibl.config" describe("set_config", function() before_each(function() conf.set_config() end) it("fills in values with the default config", function() local config = conf.set_config() assert.are.same(config, conf.default_config) end) it("uses the passed config", function() local config = conf.set_config { enabled = false } assert.are_not.equal(config.enabled, conf.default_config.enabled) end) it("validates the passed config", function() ---@diagnostic disable-next-line local ok = pcall(conf.set_config, { enabled = "string" }) assert.are.equal(ok, false) end) it("does not allow extra keys", function() ---@diagnostic disable-next-line local ok = pcall(conf.set_config, { this_does_not_exist = "string" }) assert.are.equal(ok, false) end) it("resets the config every time", function() conf.set_config { enabled = false } local config = conf.set_config { debounce = 100 } assert.are.equal(config.enabled, true) assert.are.equal(config.debounce, 100) end) it("merges passed in lists", function() local config = conf.set_config { exclude = { buftypes = { "foo" } } } assert.are.equal(vim.tbl_contains(config.exclude.buftypes, "foo"), true) assert.are.equal(vim.tbl_contains(config.exclude.buftypes, "terminal"), true) end) it("merges node_type", function() local config = conf.set_config { scope = { exclude = { node_type = { foo = { "a", "b" }, lua = { "c" }, }, }, }, } assert.are.equal(vim.tbl_contains(config.scope.exclude.node_type.foo, "a"), true) assert.are.equal(vim.tbl_contains(config.scope.exclude.node_type.foo, "b"), true) assert.are.equal(vim.tbl_contains(config.scope.exclude.node_type.lua, "c"), true) assert.are.equal(vim.tbl_contains(config.scope.exclude.node_type.lua, "chunk"), true) end) end) describe("update_config", function() before_each(function() conf.set_config() end) it("updates the existing config", function() conf.set_config { enabled = false } local config = conf.update_config { debounce = 100 } assert.are.equal(config.enabled, false) assert.are.equal(config.debounce, 100) end) end) describe("overwrite_config", function() before_each(function() conf.set_config() end) it("overwrites passed in lists", function() local config = conf.overwrite_config { exclude = { buftypes = { "foo" } } } assert.are.equal(vim.tbl_contains(config.exclude.buftypes, "foo"), true) assert.are.equal(vim.tbl_contains(config.exclude.buftypes, "terminal"), false) end) end) describe("set_buffer_config", function() local bufnr = 99 before_each(function() conf.set_config() conf.clear_buffer_config(bufnr) end) it("uses the passed config", function() local config = conf.set_buffer_config(bufnr, { enabled = false }) assert.are_not.equal(config.enabled, conf.default_config.enabled) end) it("uses uses the current global config as the default", function() conf.set_config { debounce = 100 } local config = conf.set_buffer_config(bufnr, { enabled = false }) assert.are.equal(config.debounce, 100) assert.are.equal(config.enabled, false) end) it("validates the passed config", function() ---@diagnostic disable-next-line local ok = pcall(conf.set_buffer_config, bufnr, { enabled = "string" }) assert.are.equal(ok, false) end) it("resets the config every time", function() conf.set_buffer_config(bufnr, { enabled = false }) local config = conf.set_buffer_config(bufnr, { debounce = 100 }) assert.are.equal(config.enabled, true) assert.are.equal(config.debounce, 100) end) end) describe("get_config", function() local bufnr = 99 before_each(function() conf.set_config() conf.clear_buffer_config(bufnr) end) it("gets the global config by default", function() local config = conf.get_config(bufnr) assert.are.same(config, conf.default_config) end) it("gets the buffer config if available", function() conf.set_buffer_config(bufnr, { enabled = false }) local config = conf.get_config(bufnr) assert.are.equal(config.enabled, false) end) it( "falls back to the global config if a value is not in the buffer config, even if it changed after the buffer config was set", function() conf.set_buffer_config(bufnr, { enabled = false }) conf.set_config { debounce = 100 } local config = conf.get_config(bufnr) assert.are.equal(config.enabled, false) assert.are.equal(config.debounce, 100) end ) end) lukas-reineke-indent-blankline.nvim-005b560/specs/features/hooks_spec.lua000066400000000000000000000045531476603644500265210ustar00rootroot00000000000000assert = require "luassert" local hooks = require "ibl.hooks" describe("hooks", function() before_each(function() hooks.clear_all() end) it("registers a new hook", function() local hook_id = hooks.register(hooks.type.ACTIVE, function() return true end) assert.is.equal(type(hook_id), "string") end) it("does not allow invalid types", function() local ok, _ = pcall(hooks.register, "invalid", function() return true end) assert.is.False(ok) end) it("does not allow nil types", function() local ok, _ = pcall(hooks.register, nil, function() return true end) assert.is.False(ok) end) it("does not allow invalid function", function() local ok, _ = pcall(hooks.register, hooks.type.ACTIVE, nil) assert.is.False(ok) end) it("registers hooks globally by default", function() hooks.register(hooks.type.ACTIVE, function() return true end) assert.equal(#hooks.get(9999, hooks.type.ACTIVE), 1) end) it("registers hooks to buffer when bufnr ~= nil", function() hooks.register(hooks.type.ACTIVE, function() return true end, { bufnr = 1 }) assert.equal(#hooks.get(1, hooks.type.ACTIVE), 1) assert.equal(#hooks.get(9999, hooks.type.ACTIVE), 0) end) it("registers hooks to the current buffer when bufnr == 0", function() local bufnr = vim.api.nvim_get_current_buf() hooks.register(hooks.type.ACTIVE, function() return true end, { bufnr = 0 }) assert.equal(#hooks.get(bufnr, hooks.type.ACTIVE), 1) end) end) describe("default hooks", function() describe("skip_preproc_lines", function() local skip_preproc_lines = hooks.builtin.skip_preproc_lines it("does not match 'foo'", function() assert.is.False(skip_preproc_lines(0, 0, 0, "foo")) end) it("does match '#if'", function() assert.is.True(skip_preproc_lines(0, 0, 0, "#if")) end) it("does match with trailing whitespace", function() assert.is.False(skip_preproc_lines(0, 0, 0, " #if")) end) it("does match '#if something'", function() assert.is.True(skip_preproc_lines(0, 0, 0, "#if something")) end) end) end) lukas-reineke-indent-blankline.nvim-005b560/specs/features/indent_spec.lua000066400000000000000000000101071476603644500266470ustar00rootroot00000000000000assert = require "luassert" local indent = require "ibl.indent" local TAB_START = indent.whitespace.TAB_START local TAB_START_SINGLE = indent.whitespace.TAB_START_SINGLE local TAB_FILL = indent.whitespace.TAB_FILL local TAB_END = indent.whitespace.TAB_END local SPACE = indent.whitespace.SPACE local INDENT = indent.whitespace.INDENT describe("indent", function() local opts before_each(function() opts = { tabstop = 4, vartabstop = "", shiftwidth = 2, smart_indent_cap = true, } end) it("no whitespace", function() local whitespace_tbl, _ = indent.get("", opts, false) assert.are.same({}, whitespace_tbl) end) it("normal space indentation", function() local whitespace_tbl, _ = indent.get(" ", opts, false, { cap = false, stack = { 0 } }) assert.are.same({ INDENT, SPACE }, whitespace_tbl) end) it("normal tab", function() local whitespace_tbl, _ = indent.get(" ", opts, false, { cap = false, stack = { 0 } }) assert.are.same({ TAB_START, TAB_FILL, TAB_FILL, TAB_END }, whitespace_tbl) end) it("single width tab", function() opts.tabstop = 1 local whitespace_tbl, _ = indent.get(" ", opts, false, { cap = false, stack = { 0 } }) assert.are.same({ TAB_START_SINGLE }, whitespace_tbl) end) it("double width tab", function() opts.tabstop = 2 local whitespace_tbl, _ = indent.get(" ", opts, false, { cap = false, stack = { 0 } }) assert.are.same({ TAB_START, TAB_END }, whitespace_tbl) end) it("vartabstop", function() opts.vartabstop = "1,3" local whitespace_tbl, _ = indent.get(" ", opts, false, { cap = false, stack = { 0 } }) assert.are.same( { TAB_START_SINGLE, TAB_START, TAB_FILL, TAB_END, TAB_START, TAB_FILL, TAB_END }, whitespace_tbl ) end) it("vartabstop with mixed indentation", function() opts.vartabstop = "2,8" local whitespace_tbl, _ = indent.get(" ", opts, false, { cap = false, stack = { 0 } }) assert.are.same( { INDENT, SPACE, TAB_START, TAB_FILL, TAB_FILL, TAB_FILL, TAB_FILL, TAB_FILL, TAB_FILL, TAB_END }, whitespace_tbl ) end) it("vartabstop with mixed indentation and stack", function() opts.vartabstop = "2,8" local whitespace_tbl, _ = indent.get(" ", opts, false, { cap = false, stack = { 1 } }) assert.are.same( { INDENT, INDENT, TAB_START, TAB_FILL, TAB_FILL, TAB_FILL, TAB_FILL, TAB_FILL, TAB_FILL, TAB_END }, whitespace_tbl ) end) it("mix of tabs and spaces", function() local whitespace_tbl, _ = indent.get(" ", opts, false, { cap = false, stack = { 0 } }) assert.are.same({ INDENT, SPACE, TAB_START, TAB_END, SPACE, TAB_START, TAB_FILL, TAB_END }, whitespace_tbl) end) it("mix of tabs and spaces with vartabstop", function() opts.vartabstop = "1,3" local whitespace_tbl, _ = indent.get(" ", opts, false, { cap = false, stack = { 0 } }) assert.are.same({ TAB_START_SINGLE, SPACE, TAB_START, TAB_END, SPACE }, whitespace_tbl) end) it("caps after first indent after last item in stack", function() local whitespace_tbl, _ = indent.get(" ", opts, false, { cap = false, stack = { 0, 4 } }) assert.are.same({ INDENT, SPACE, INDENT, SPACE, INDENT, SPACE, SPACE, SPACE }, whitespace_tbl) end) it("caps after first indent of first item in stack when cap is true", function() local whitespace_tbl, _ = indent.get(" ", opts, false, { cap = true, stack = { 0, 4 } }) assert.are.same({ INDENT, SPACE, SPACE, SPACE, INDENT, SPACE, SPACE, SPACE }, whitespace_tbl) end) it("doesn't cap with smart_indent_cap off", function() opts.smart_indent_cap = false local whitespace_tbl, _ = indent.get(" ", opts, false, { cap = false, stack = { 0, 4 } }) assert.are.same({ INDENT, SPACE, INDENT, SPACE, INDENT, SPACE, INDENT, SPACE }, whitespace_tbl) end) end) lukas-reineke-indent-blankline.nvim-005b560/specs/features/utils_spec.lua000066400000000000000000000077011476603644500265340ustar00rootroot00000000000000assert = require "luassert" local utils = require "ibl.utils" local conf = require "ibl.config" describe("get_listchars", function() local listchars = vim.opt.listchars:get() after_each(function() vim.opt.listchars = listchars vim.opt.list = false end) it("returns fallback listchars if list is off", function() assert.are.same(utils.get_listchars(0), { tabstop_overwrite = false, space_char = " ", tab_char_fill = " ", }) end) it("returns default listchars", function() vim.opt.list = true assert.are.same(utils.get_listchars(0), { tabstop_overwrite = false, space_char = " ", tab_char_start = ">", tab_char_fill = " ", trail_char = "-", }) end) it("sets tabstop_overwrite to true when there is are tab chars", function() vim.opt.list = true vim.opt.listchars = {} assert.are.same(utils.get_listchars(0), { tabstop_overwrite = true, space_char = " ", tab_char_start = "^", tab_char_fill = "I", }) end) it("splits utf-8 chars correctly", function() vim.opt.list = true vim.opt.listchars = { tab = "󱢗󰗲" } assert.are.same(utils.get_listchars(0), { tabstop_overwrite = false, space_char = " ", tab_char_start = "󱢗", tab_char_fill = "󰗲", }) end) it("supports hex values", function() vim.opt.list = true vim.opt.listchars = { tab = "\\x24\\u21b5\\U000021b5", space = "\\u00B7" } assert.are.same(utils.get_listchars(0), { tabstop_overwrite = false, space_char = "·", tab_char_start = "$", tab_char_fill = "↵", tab_char_end = "↵", }) end) end) describe("has_repeat_indent", function() local config = conf.get_config(0) after_each(function() vim.opt.breakindent = false vim.opt.breakindentopt = "" config.indent.repeat_linebreak = true end) -- test for old Neovim versions if vim.fn.has "nvim-0.10" ~= 1 then it("does not use repeat indent on old Neovim versions", function() vim.opt.breakindent = true assert.are.equal(utils.has_repeat_indent(0, config), false) end) return end it("does not use repeat indent with breakindent off", function() assert.are.equal(utils.has_repeat_indent(0, config), false) end) it("does not use repeat indent with breakindent on", function() vim.opt.breakindent = true assert.are.equal(utils.has_repeat_indent(0, config), true) end) it("does not use repeat indent when disabled in the config", function() config.indent.repeat_linebreak = false vim.opt.breakindent = true assert.are.equal(utils.has_repeat_indent(0, config), false) end) it("does not use repeat indent when breakindentopt includes sbr", function() vim.opt.breakindent = true vim.opt.breakindentopt = "min:5,sbr" assert.are.equal(utils.has_repeat_indent(0, config), false) end) it("does not use repeat indent when breakindentopt includes column", function() vim.opt.breakindent = true vim.opt.breakindentopt = "min:5,column:9" assert.are.equal(utils.has_repeat_indent(0, config), false) end) it("does not use repeat indent when breakindentopt includes a negative value for shift", function() vim.opt.breakindent = true vim.opt.breakindentopt = "min:5,shift:-5" assert.are.equal(utils.has_repeat_indent(0, config), false) end) it("does use repeat indent when breakindentopt includes a positive value for shift", function() vim.opt.breakindent = true vim.opt.breakindentopt = "min:5,shift:5" assert.are.equal(utils.has_repeat_indent(0, config), true) end) end) lukas-reineke-indent-blankline.nvim-005b560/specs/features/virt_text_spec.lua000066400000000000000000000567541476603644500274400ustar00rootroot00000000000000assert = require "luassert" local conf = require "ibl.config" local indent = require "ibl.indent" local whitespace = indent.whitespace local highlights = require "ibl.highlights" local vt = require "ibl.virt_text" local TAB_START = whitespace.TAB_START local TAB_START_SINGLE = whitespace.TAB_START_SINGLE local TAB_FILL = whitespace.TAB_FILL local TAB_END = whitespace.TAB_END local SPACE = whitespace.SPACE local INDENT = whitespace.INDENT describe("get_char_map", function() before_each(function() conf.set_config() end) it("makes a basic char map", function() local config = conf.set_config { indent = { char = "i" } } local listchars = { tabstop_overwrite = false, space_char = " ", trail_char = nil, lead_char = nil, multispace_chars = nil, leadmultispace_chars = nil, tab_char_start = ">", tab_char_fill = " ", tab_char_end = nil, } local whitespace_only = false local blankline = false local char_map = vt.get_char_map(config, listchars, whitespace_only, blankline) assert.are.same(char_map, { [TAB_START] = ">", [TAB_START_SINGLE] = ">", [TAB_FILL] = " ", [TAB_END] = " ", [SPACE] = " ", [INDENT] = "i", }) end) it("uses tab_char for tabs", function() local config = conf.set_config { indent = { char = "i", tab_char = "t" } } local listchars = { tabstop_overwrite = false, space_char = " ", trail_char = nil, lead_char = nil, multispace_chars = nil, leadmultispace_chars = nil, tab_char_start = nil, tab_char_fill = " ", tab_char_end = nil, } local whitespace_only = false local blankline = false local char_map = vt.get_char_map(config, listchars, whitespace_only, blankline) assert.are.same(char_map, { [TAB_START] = "t", [TAB_START_SINGLE] = "t", [TAB_FILL] = " ", [TAB_END] = " ", [SPACE] = " ", [INDENT] = "i", }) end) it("uses char for tabs if everything else is nil", function() local config = conf.set_config { indent = { char = "i" } } local listchars = { tabstop_overwrite = false, space_char = " ", trail_char = nil, lead_char = nil, multispace_chars = nil, leadmultispace_chars = nil, tab_char_start = nil, tab_char_fill = " ", tab_char_end = nil, } local whitespace_only = false local blankline = false local char_map = vt.get_char_map(config, listchars, whitespace_only, blankline) assert.are.same(char_map, { [TAB_START] = "i", [TAB_START_SINGLE] = "i", [TAB_FILL] = " ", [TAB_END] = " ", [SPACE] = " ", [INDENT] = "i", }) end) it("parses basic listchars", function() local config = conf.set_config { indent = { char = "i" } } local listchars = { tabstop_overwrite = false, space_char = " ", trail_char = "w", lead_char = "a", multispace_chars = nil, leadmultispace_chars = nil, tab_char_start = "b", tab_char_fill = "c", tab_char_end = "d", } local whitespace_only = false local blankline = false local char_map = vt.get_char_map(config, listchars, whitespace_only, blankline) assert.are.same(char_map, { [TAB_START] = "b", [TAB_START_SINGLE] = "d", [TAB_FILL] = "c", [TAB_END] = "d", [SPACE] = "a", [INDENT] = "i", }) end) it("parses uses multispace listchars", function() local config = conf.set_config { indent = { char = "i" } } local listchars = { tabstop_overwrite = false, space_char = " ", trail_char = "w", lead_char = nil, multispace_chars = { "x", "y" }, leadmultispace_chars = nil, tab_char_start = "b", tab_char_fill = "c", tab_char_end = "d", } local whitespace_only = false local blankline = false local char_map = vt.get_char_map(config, listchars, whitespace_only, blankline) assert.are.same(char_map, { [TAB_START] = "b", [TAB_START_SINGLE] = "d", [TAB_FILL] = "c", [TAB_END] = "d", [SPACE] = { "x", "y" }, [INDENT] = "i", }) end) it("uses lead over multispace listchars", function() local config = conf.set_config { indent = { char = "i" } } local listchars = { tabstop_overwrite = false, space_char = " ", trail_char = "w", lead_char = "a", multispace_chars = { "x", "y" }, leadmultispace_chars = nil, tab_char_start = "b", tab_char_fill = "c", tab_char_end = "d", } local whitespace_only = false local blankline = false local char_map = vt.get_char_map(config, listchars, whitespace_only, blankline) assert.are.same(char_map, { [TAB_START] = "b", [TAB_START_SINGLE] = "d", [TAB_FILL] = "c", [TAB_END] = "d", [SPACE] = "a", [INDENT] = "i", }) end) it("uses leadmultispace over lead listchars", function() local config = conf.set_config { indent = { char = "i" } } local listchars = { tabstop_overwrite = false, space_char = " ", trail_char = "w", lead_char = "a", multispace_chars = { "x", "y" }, leadmultispace_chars = { "o", "i" }, tab_char_start = "b", tab_char_fill = "c", tab_char_end = "d", } local whitespace_only = false local blankline = false local char_map = vt.get_char_map(config, listchars, whitespace_only, blankline) assert.are.same(char_map, { [TAB_START] = "b", [TAB_START_SINGLE] = "d", [TAB_FILL] = "c", [TAB_END] = "d", [SPACE] = { "o", "i" }, [INDENT] = "i", }) end) it("uses trail listchars on whitspace only lines", function() local config = conf.set_config { indent = { char = "i" } } local listchars = { tabstop_overwrite = false, space_char = " ", trail_char = "w", lead_char = "a", multispace_chars = { "x", "y" }, leadmultispace_chars = { "o", "i" }, tab_char_start = "b", tab_char_fill = "c", tab_char_end = "d", } local whitespace_only = true local blankline = false local char_map = vt.get_char_map(config, listchars, whitespace_only, blankline) assert.are.same(char_map, { [TAB_START] = "b", [TAB_START_SINGLE] = "d", [TAB_FILL] = "c", [TAB_END] = "d", [SPACE] = "w", [INDENT] = "i", }) end) it("uses spaces on blanklines", function() local config = conf.set_config { indent = { char = "i" } } local listchars = { tabstop_overwrite = false, space_char = "s", trail_char = "w", lead_char = "a", multispace_chars = { "x", "y" }, leadmultispace_chars = { "o", "i" }, tab_char_start = "b", tab_char_fill = "c", tab_char_end = "d", } local whitespace_only = false local blankline = true local char_map = vt.get_char_map(config, listchars, whitespace_only, blankline) assert.are.same(char_map, { [TAB_START] = "b", [TAB_START_SINGLE] = "d", [TAB_FILL] = " ", [TAB_END] = " ", [SPACE] = " ", [INDENT] = "i", }) end) end) describe("virt_text", function() before_each(function() conf.set_config() end) it("handles empty whitespace table", function() local config = conf.set_config() highlights.setup() local char_map = { [TAB_START] = "a", [TAB_START_SINGLE] = "b", [TAB_FILL] = "c", [TAB_END] = "d", [SPACE] = "e", [INDENT] = "f", } local whitespace_tbl = {} local scope_active = false local scope_index = -1 local scope_start = false local scope_end = false local scope_col_start_single = 0 local virt_text = vt.get( config, char_map, whitespace_tbl, scope_active, scope_index, scope_start, scope_end, scope_col_start_single ) assert.are.same(virt_text, {}) end) it("handles simple indentation", function() local config = conf.set_config() highlights.setup() local char_map = { [TAB_START] = "a", [TAB_START_SINGLE] = "b", [TAB_FILL] = "c", [TAB_END] = "d", [SPACE] = "e", [INDENT] = "f", } local whitespace_tbl = { INDENT, SPACE, INDENT, SPACE } local scope_active = false local scope_index = -1 local scope_start = false local scope_end = false local scope_col_start_single = 0 local virt_text = vt.get( config, char_map, whitespace_tbl, scope_active, scope_index, scope_start, scope_end, scope_col_start_single ) assert.are.same(virt_text, { { "f", { "@ibl.whitespace.char.1", "@ibl.indent.char.1" } }, { "e", { "@ibl.whitespace.char.1" } }, { "f", { "@ibl.whitespace.char.1", "@ibl.indent.char.1" } }, { "e", { "@ibl.whitespace.char.1" } }, }) end) it("handles a list of indent chars", function() local config = conf.set_config() highlights.setup() local char_map = { [TAB_START] = "o", [TAB_START_SINGLE] = "b", [TAB_FILL] = "c", [TAB_END] = "d", [SPACE] = "e", [INDENT] = { "a", "b", "c" }, } local whitespace_tbl = { INDENT, SPACE, INDENT, SPACE, INDENT, SPACE } local scope_active = false local scope_index = -1 local scope_start = false local scope_end = false local scope_col_start_single = 0 local virt_text = vt.get( config, char_map, whitespace_tbl, scope_active, scope_index, scope_start, scope_end, scope_col_start_single ) assert.are.same(virt_text, { { "a", { "@ibl.whitespace.char.1", "@ibl.indent.char.1" } }, { "e", { "@ibl.whitespace.char.1" } }, { "b", { "@ibl.whitespace.char.1", "@ibl.indent.char.1" } }, { "e", { "@ibl.whitespace.char.1" } }, { "c", { "@ibl.whitespace.char.1", "@ibl.indent.char.1" } }, { "e", { "@ibl.whitespace.char.1" } }, }) end) it("handles a list of tab chars", function() local config = conf.set_config() highlights.setup() local char_map = { [TAB_START] = { "a", "b", "c" }, [TAB_START_SINGLE] = "o", [TAB_FILL] = "c", [TAB_END] = "d", [SPACE] = "e", [INDENT] = "f", } local whitespace_tbl = { TAB_START, TAB_END, TAB_START, TAB_END, TAB_START, TAB_END } local scope_active = false local scope_index = -1 local scope_start = false local scope_end = false local scope_col_start_single = 0 local virt_text = vt.get( config, char_map, whitespace_tbl, scope_active, scope_index, scope_start, scope_end, scope_col_start_single ) assert.are.same(virt_text, { { "a", { "@ibl.whitespace.char.1", "@ibl.indent.char.1" } }, { "d", { "@ibl.whitespace.char.1" } }, { "b", { "@ibl.whitespace.char.1", "@ibl.indent.char.1" } }, { "d", { "@ibl.whitespace.char.1" } }, { "c", { "@ibl.whitespace.char.1", "@ibl.indent.char.1" } }, { "d", { "@ibl.whitespace.char.1" } }, }) end) it("handles indent with no display width", function() local config = conf.set_config() highlights.setup() local char_map = { [TAB_START] = "a", [TAB_START_SINGLE] = "b", [TAB_FILL] = "c", [TAB_END] = "d", [SPACE] = "e", [INDENT] = "", } local whitespace_tbl = { INDENT, SPACE, INDENT, SPACE } local scope_active = false local scope_index = -1 local scope_start = false local scope_end = false local scope_col_start_single = 0 local virt_text = vt.get( config, char_map, whitespace_tbl, scope_active, scope_index, scope_start, scope_end, scope_col_start_single ) assert.are.same(virt_text, { { "e", { "@ibl.whitespace.char.1" } }, { "e", { "@ibl.whitespace.char.1" } }, { "e", { "@ibl.whitespace.char.1" } }, { "e", { "@ibl.whitespace.char.1" } }, }) end) it("handles scope", function() local config = conf.set_config() highlights.setup() local char_map = { [TAB_START] = "a", [TAB_START_SINGLE] = "b", [TAB_FILL] = "c", [TAB_END] = "d", [SPACE] = "e", [INDENT] = "f", } local whitespace_tbl = { INDENT, SPACE, INDENT, SPACE } local scope_active = true local scope_index = 1 local scope_start = false local scope_end = false local scope_col_start_single = 2 local virt_text = vt.get( config, char_map, whitespace_tbl, scope_active, scope_index, scope_start, scope_end, scope_col_start_single ) assert.are.same(virt_text, { { "f", { "@ibl.whitespace.char.1", "@ibl.indent.char.1" } }, { "e", { "@ibl.whitespace.char.1" } }, { "f", { "@ibl.whitespace.char.1", "@ibl.scope.char.1" } }, { "e", { "@ibl.whitespace.char.1" } }, }) end) it("handles tabs", function() local config = conf.set_config() highlights.setup() local char_map = { [TAB_START] = "a", [TAB_START_SINGLE] = "b", [TAB_FILL] = "c", [TAB_END] = "d", [SPACE] = "e", [INDENT] = "f", } local whitespace_tbl = { TAB_START, TAB_FILL, TAB_FILL, TAB_END, TAB_START_SINGLE } local scope_active = false local scope_index = -1 local scope_start = false local scope_end = false local scope_col_start_single = 0 local virt_text = vt.get( config, char_map, whitespace_tbl, scope_active, scope_index, scope_start, scope_end, scope_col_start_single ) assert.are.same(virt_text, { { "a", { "@ibl.whitespace.char.1", "@ibl.indent.char.1" } }, { "c", { "@ibl.whitespace.char.1" } }, { "c", { "@ibl.whitespace.char.1" } }, { "d", { "@ibl.whitespace.char.1" } }, { "b", { "@ibl.whitespace.char.1", "@ibl.indent.char.1" } }, }) end) it("handles multiple highlight groups", function() local config = conf.set_config { whitespace = { highlight = { "Error", "Function", "Label" } }, indent = { highlight = { "Error", "Function", "Label" } }, } highlights.setup() local char_map = { [TAB_START] = "a", [TAB_START_SINGLE] = "b", [TAB_FILL] = "c", [TAB_END] = "d", [SPACE] = "e", [INDENT] = "f", } local whitespace_tbl = { INDENT, SPACE, INDENT, SPACE, INDENT, SPACE } local scope_active = false local scope_index = -1 local scope_start = false local scope_end = false local scope_col_start_single = 0 local virt_text = vt.get( config, char_map, whitespace_tbl, scope_active, scope_index, scope_start, scope_end, scope_col_start_single ) assert.are.same(virt_text, { { "f", { "@ibl.whitespace.char.1", "@ibl.indent.char.1" } }, { "e", { "@ibl.whitespace.char.1" } }, { "f", { "@ibl.whitespace.char.2", "@ibl.indent.char.2" } }, { "e", { "@ibl.whitespace.char.2" } }, { "f", { "@ibl.whitespace.char.3", "@ibl.indent.char.3" } }, { "e", { "@ibl.whitespace.char.3" } }, }) end) it("handles multiple highlight groups with scope", function() local config = conf.set_config { whitespace = { highlight = { "Error", "Function", "Label" } }, indent = { highlight = { "Error", "Function", "Label" } }, scope = { highlight = { "Error", "Function", "Label" } }, } highlights.setup() local char_map = { [TAB_START] = "a", [TAB_START_SINGLE] = "b", [TAB_FILL] = "c", [TAB_END] = "d", [SPACE] = "e", [INDENT] = "f", } local whitespace_tbl = { INDENT, SPACE, INDENT, SPACE, INDENT, SPACE } local scope_active = true local scope_index = 2 local scope_start = false local scope_end = false local scope_col_start_single = 2 local virt_text = vt.get( config, char_map, whitespace_tbl, scope_active, scope_index, scope_start, scope_end, scope_col_start_single ) assert.are.same(virt_text, { { "f", { "@ibl.whitespace.char.1", "@ibl.indent.char.1" } }, { "e", { "@ibl.whitespace.char.1" } }, { "f", { "@ibl.whitespace.char.2", "@ibl.scope.char.2" } }, { "e", { "@ibl.whitespace.char.2" } }, { "f", { "@ibl.whitespace.char.3", "@ibl.indent.char.3" } }, { "e", { "@ibl.whitespace.char.3" } }, }) end) it("handles multiple highlight groups with scope on scope end", function() local config = conf.set_config { whitespace = { highlight = { "Error", "Function", "Label" } }, indent = { highlight = { "Error", "Function", "Label" } }, scope = { highlight = { "Error", "Function", "Label" } }, } highlights.setup() local char_map = { [TAB_START] = "a", [TAB_START_SINGLE] = "b", [TAB_FILL] = "c", [TAB_END] = "d", [SPACE] = "e", [INDENT] = "f", } local whitespace_tbl = { INDENT, SPACE, INDENT, SPACE, INDENT, SPACE } local scope_active = true local scope_index = 2 local scope_start = false local scope_end = true local scope_col_start_single = 2 local virt_text = vt.get( config, char_map, whitespace_tbl, scope_active, scope_index, scope_start, scope_end, scope_col_start_single ) assert.are.same(virt_text, { { "f", { "@ibl.whitespace.char.1", "@ibl.indent.char.1" } }, { "e", { "@ibl.whitespace.char.1" } }, { "f", { "@ibl.whitespace.char.2", "@ibl.scope.char.2", "@ibl.scope.underline.2" } }, { "e", { "@ibl.whitespace.char.2", "@ibl.scope.underline.2" } }, { "f", { "@ibl.whitespace.char.3", "@ibl.indent.char.3", "@ibl.scope.underline.2" } }, { "e", { "@ibl.whitespace.char.3", "@ibl.scope.underline.2" } }, }) end) it("handles multiple highlight groups with scope on scope start", function() local config = conf.set_config { whitespace = { highlight = { "Error", "Function", "Label" } }, indent = { highlight = { "Error", "Function", "Label" } }, scope = { highlight = { "Error", "Function", "Label" } }, } highlights.setup() local char_map = { [TAB_START] = "a", [TAB_START_SINGLE] = "b", [TAB_FILL] = "c", [TAB_END] = "d", [SPACE] = "e", [INDENT] = "f", } local whitespace_tbl = { INDENT, SPACE, INDENT, SPACE, INDENT, SPACE } local scope_active = true local scope_index = 2 local scope_start = true local scope_end = false local scope_col_start_single = 2 local virt_text = vt.get( config, char_map, whitespace_tbl, scope_active, scope_index, scope_start, scope_end, scope_col_start_single ) assert.are.same(virt_text, { { "f", { "@ibl.whitespace.char.1", "@ibl.indent.char.1" } }, { "e", { "@ibl.whitespace.char.1" } }, { "f", { "@ibl.whitespace.char.2", "@ibl.indent.char.2", "@ibl.scope.underline.2" } }, { "e", { "@ibl.whitespace.char.2", "@ibl.scope.underline.2" } }, { "f", { "@ibl.whitespace.char.3", "@ibl.indent.char.3", "@ibl.scope.underline.2" } }, { "e", { "@ibl.whitespace.char.3", "@ibl.scope.underline.2" } }, }) end) it("renders lead and multi lead spaces correctly", function() local config = conf.set_config() highlights.setup() local char_map = { [TAB_START] = "a", [TAB_START_SINGLE] = "b", [TAB_FILL] = "c", [TAB_END] = "d", [SPACE] = { "e", "f", "g" }, [INDENT] = "h", } local whitespace_tbl = { INDENT, SPACE, SPACE, SPACE, INDENT, SPACE, SPACE, SPACE } local scope_active = false local scope_index = -1 local scope_start = false local scope_end = false local scope_col_start_single = 0 local virt_text = vt.get( config, char_map, whitespace_tbl, scope_active, scope_index, scope_start, scope_end, scope_col_start_single ) assert.are.same(virt_text, { { "h", { "@ibl.whitespace.char.1", "@ibl.indent.char.1" } }, { "f", { "@ibl.whitespace.char.1" } }, { "g", { "@ibl.whitespace.char.1" } }, { "e", { "@ibl.whitespace.char.1" } }, { "h", { "@ibl.whitespace.char.1", "@ibl.indent.char.1" } }, { "g", { "@ibl.whitespace.char.1" } }, { "e", { "@ibl.whitespace.char.1" } }, { "f", { "@ibl.whitespace.char.1" } }, }) end) end) lukas-reineke-indent-blankline.nvim-005b560/specs/spec.lua000066400000000000000000000004211476603644500234660ustar00rootroot00000000000000vim.api.nvim_command [[set rtp+=.]] vim.opt.swapfile = false local cwd = vim.fn.getcwd() vim.api.nvim_command(string.format([[set rtp+=%s,%s/sepcs]], cwd, cwd)) vim.api.nvim_command(string.format([[set packpath=%s/.ci/vendor]], cwd)) vim.api.nvim_command [[packloadall]] lukas-reineke-indent-blankline.nvim-005b560/stylua.toml000066400000000000000000000001311476603644500231300ustar00rootroot00000000000000line_endings = "Unix" indent_type = "Spaces" indent_width = 4 no_call_parentheses = true