pax_global_header 0000666 0000000 0000000 00000000064 14766036445 0014531 g ustar 00root root 0000000 0000000 52 comment=005b56001b2cb30bfa61b7986bc50657816ba4ba
lukas-reineke-indent-blankline.nvim-005b560/ 0000775 0000000 0000000 00000000000 14766036445 0020717 5 ustar 00root root 0000000 0000000 lukas-reineke-indent-blankline.nvim-005b560/.cbfmt.toml 0000664 0000000 0000000 00000000042 14766036445 0022761 0 ustar 00root root 0000000 0000000 [languages]
lua = ["stylua -s -"]
lukas-reineke-indent-blankline.nvim-005b560/.github/ 0000775 0000000 0000000 00000000000 14766036445 0022257 5 ustar 00root root 0000000 0000000 lukas-reineke-indent-blankline.nvim-005b560/.github/FUNDING.yml 0000664 0000000 0000000 00000000030 14766036445 0024065 0 ustar 00root root 0000000 0000000 github: [lukas-reineke]
lukas-reineke-indent-blankline.nvim-005b560/.github/ISSUE_TEMPLATE/ 0000775 0000000 0000000 00000000000 14766036445 0024442 5 ustar 00root root 0000000 0000000 lukas-reineke-indent-blankline.nvim-005b560/.github/ISSUE_TEMPLATE/bug_report.yml 0000664 0000000 0000000 00000002605 14766036445 0027340 0 ustar 00root root 0000000 0000000 name: 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.yml 0000664 0000000 0000000 00000001433 14766036445 0030371 0 ustar 00root root 0000000 0000000 name: 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/ 0000775 0000000 0000000 00000000000 14766036445 0024314 5 ustar 00root root 0000000 0000000 lukas-reineke-indent-blankline.nvim-005b560/.github/workflows/lua_language_server.yml 0000664 0000000 0000000 00000003531 14766036445 0031053 0 ustar 00root root 0000000 0000000 name: 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.yml 0000664 0000000 0000000 00000001043 14766036445 0027650 0 ustar 00root root 0000000 0000000 name: 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.yml 0000664 0000000 0000000 00000004011 14766036445 0026611 0 ustar 00root root 0000000 0000000 name: 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.yml 0000664 0000000 0000000 00000002000 14766036445 0026171 0 ustar 00root root 0000000 0000000 name: 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/.gitignore 0000664 0000000 0000000 00000000020 14766036445 0022677 0 ustar 00root root 0000000 0000000 .ci/*
/doc/tags
lukas-reineke-indent-blankline.nvim-005b560/.luacheckrc 0000664 0000000 0000000 00000000221 14766036445 0023017 0 ustar 00root root 0000000 0000000 globals = { "vim", "_", "describe", "it", "after_each", "before_each", "assert" }
max_line_length = false
exclude_files = {
".ci/vendor",
}
lukas-reineke-indent-blankline.nvim-005b560/.luarc.nightly.json 0000664 0000000 0000000 00000000716 14766036445 0024457 0 ustar 00root root 0000000 0000000 {
"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.json 0000664 0000000 0000000 00000000715 14766036445 0024252 0 ustar 00root root 0000000 0000000 {
"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/.styluaignore 0000664 0000000 0000000 00000000006 14766036445 0023441 0 ustar 00root root 0000000 0000000 .ci/*
lukas-reineke-indent-blankline.nvim-005b560/LICENSE.md 0000664 0000000 0000000 00000002062 14766036445 0022323 0 ustar 00root root 0000000 0000000 The 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/Makefile 0000664 0000000 0000000 00000001646 14766036445 0022366 0 ustar 00root root 0000000 0000000 ifndef 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.md 0000664 0000000 0000000 00000011511 14766036445 0022175 0 ustar 00root root 0000000 0000000 # 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()
```
### Scope
Scope requires treesitter to be set up.
```lua
require("ibl").setup()
```
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()
```
### 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 } }
```
### 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 },
}
```
### 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)
```
lukas-reineke-indent-blankline.nvim-005b560/after/ 0000775 0000000 0000000 00000000000 14766036445 0022020 5 ustar 00root root 0000000 0000000 lukas-reineke-indent-blankline.nvim-005b560/after/ftplugin/ 0000775 0000000 0000000 00000000000 14766036445 0023650 5 ustar 00root root 0000000 0000000 lukas-reineke-indent-blankline.nvim-005b560/after/ftplugin/c.lua 0000664 0000000 0000000 00000000171 14766036445 0024574 0 ustar 00root root 0000000 0000000 local 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.lua 0000664 0000000 0000000 00000000171 14766036445 0025134 0 ustar 00root root 0000000 0000000 local 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/ 0000775 0000000 0000000 00000000000 14766036445 0023316 5 ustar 00root root 0000000 0000000 lukas-reineke-indent-blankline.nvim-005b560/after/plugin/commands.lua 0000664 0000000 0000000 00000002426 14766036445 0025626 0 ustar 00root root 0000000 0000000 local 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/ 0000775 0000000 0000000 00000000000 14766036445 0021464 5 ustar 00root root 0000000 0000000 lukas-reineke-indent-blankline.nvim-005b560/doc/indent_blankline.txt 0000664 0000000 0000000 00000073103 14766036445 0025531 0 ustar 00root root 0000000 0000000 *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/ 0000775 0000000 0000000 00000000000 14766036445 0021500 5 ustar 00root root 0000000 0000000 lukas-reineke-indent-blankline.nvim-005b560/lua/ibl/ 0000775 0000000 0000000 00000000000 14766036445 0022246 5 ustar 00root root 0000000 0000000 lukas-reineke-indent-blankline.nvim-005b560/lua/ibl/autocmds.lua 0000664 0000000 0000000 00000003307 14766036445 0024573 0 ustar 00root root 0000000 0000000 local 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.lua 0000664 0000000 0000000 00000024644 14766036445 0024230 0 ustar 00root root 0000000 0000000 local 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.lua 0000664 0000000 0000000 00000021720 14766036445 0025363 0 ustar 00root root 0000000 0000000
---@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.lua 0000664 0000000 0000000 00000006327 14766036445 0025113 0 ustar 00root root 0000000 0000000 local 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.lua 0000664 0000000 0000000 00000023045 14766036445 0024100 0 ustar 00root root 0000000 0000000 local 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.lua 0000664 0000000 0000000 00000010120 14766036445 0024224 0 ustar 00root root 0000000 0000000 local 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.lua 0000664 0000000 0000000 00000042472 14766036445 0023725 0 ustar 00root root 0000000 0000000 local 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.lua 0000664 0000000 0000000 00000005512 14766036445 0025275 0 ustar 00root root 0000000 0000000 local 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.lua 0000664 0000000 0000000 00000005154 14766036445 0024067 0 ustar 00root root 0000000 0000000 local 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.lua 0000664 0000000 0000000 00000040553 14766036445 0026117 0 ustar 00root root 0000000 0000000 -- 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.lua 0000664 0000000 0000000 00000035342 14766036445 0024120 0 ustar 00root root 0000000 0000000 local 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.lua 0000664 0000000 0000000 00000010571 14766036445 0025005 0 ustar 00root root 0000000 0000000 local 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.lua 0000664 0000000 0000000 00000000512 14766036445 0025501 0 ustar 00root root 0000000 0000000 return {
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/ 0000775 0000000 0000000 00000000000 14766036445 0022034 5 ustar 00root root 0000000 0000000 lukas-reineke-indent-blankline.nvim-005b560/specs/features/ 0000775 0000000 0000000 00000000000 14766036445 0023652 5 ustar 00root root 0000000 0000000 lukas-reineke-indent-blankline.nvim-005b560/specs/features/config_spec.lua 0000664 0000000 0000000 00000012011 14766036445 0026627 0 ustar 00root root 0000000 0000000 assert = 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.lua 0000664 0000000 0000000 00000004553 14766036445 0026521 0 ustar 00root root 0000000 0000000 assert = 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.lua 0000664 0000000 0000000 00000010107 14766036445 0026647 0 ustar 00root root 0000000 0000000 assert = 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.lua 0000664 0000000 0000000 00000007701 14766036445 0026534 0 ustar 00root root 0000000 0000000 assert = 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.lua 0000664 0000000 0000000 00000056754 14766036445 0027440 0 ustar 00root root 0000000 0000000 assert = 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.lua 0000664 0000000 0000000 00000000421 14766036445 0023466 0 ustar 00root root 0000000 0000000 vim.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.toml 0000664 0000000 0000000 00000000131 14766036445 0023130 0 ustar 00root root 0000000 0000000 line_endings = "Unix"
indent_type = "Spaces"
indent_width = 4
no_call_parentheses = true