1

Regenerate nvim config

This commit is contained in:
2024-06-02 03:29:20 +02:00
parent 75eea0c030
commit ef2e28883d
5576 changed files with 604886 additions and 503 deletions

View File

@ -0,0 +1,19 @@
root = true
[*]
indent_style = space
indent_size = 4
[*.lua]
indent_style = space
indent_size = 4
[*.vim]
indent_style = space
indent_size = 4
[*.snippets]
indent_style = tab
indent_size = 4

View File

@ -0,0 +1,2 @@
custom: https://paypal.me/trieule1vn
patreon: windwp

View File

@ -0,0 +1,71 @@
name: Bug report
description: Report a problem with nvim-autopairs
labels: [bug]
body:
- type: markdown
attributes:
value: |
Before reporting: search [existing issues](https://github.com/windwp/nvim-autopairs/issues) and make sure that both nvim-autopairs and its dependencies are updated to the latest version.
- type: textarea
attributes:
label: "Description"
description: "A short description of the problem you are reporting."
validations:
required: true
- type: textarea
attributes:
label: "Mapping <cr> bug"
description: "report a bug about mapping `<cr>` key and completion plugin"
value: |
1.If you report a bug about indent. Please remember that plugin doesn't do anything about indent.
It just trigger the indent of your vim config so if you have wrong indent config then it will do wrong indent.
You can check by select a block of code and press `==`
2. provide result of command `:verbose imap <cr>`.
validations:
required: false
- type: textarea
attributes:
label: "Steps to reproduce"
description: "Steps to reproduce using the minimal config provided below."
placeholder: |
- It will beter if you can provide a video of gif
validations:
required: false
- type: textarea
attributes:
label: "Minimal config"
description: "Minimal(!) configuration necessary to reproduce the issue. Save this as `minimal.lua`. If _absolutely_ necessary, add plugins and config options from your `init.lua` at the indicated lines."
render: Lua
value: |
vim.cmd [[set runtimepath=$VIMRUNTIME]]
vim.cmd [[set packpath=/tmp/nvim/site]]
local package_root = '/tmp/nvim/site/pack'
local install_path = package_root .. '/packer/start/packer.nvim'
local function load_plugins()
require('packer').startup {
{
'wbthomason/packer.nvim',
{
'windwp/nvim-autopairs',
},
-- ADD PLUGINS THAT ARE _NECESSARY_ FOR REPRODUCING THE ISSUE
},
config = {
package_root = package_root,
compile_path = install_path .. '/plugin/packer_compiled.lua',
display = { non_interactive = true },
},
}
end
_G.load_config = function()
require('nvim-autopairs').setup()
end
if vim.fn.isdirectory(install_path) == 0 then
print("Installing nvim-autopairs and dependencies.")
vim.fn.system { 'git', 'clone', '--depth=1', 'https://github.com/wbthomason/packer.nvim', install_path }
end
load_plugins()
require('packer').sync()
vim.cmd [[autocmd User PackerComplete ++once echo "Ready!" | lua load_config()]]
validations:
required: true

View File

@ -0,0 +1,2 @@
blank_issues_enabled: false

View File

@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@ -0,0 +1,7 @@
generatedFiles:
- path: "doc/nvim-autopairs.txt"
- path: "doc/nvim-autopairs-rules.txt"
message: "`nvim-autopairs.txt` is generated from README.md. Make changes there instead."
ignoreAuthors:
- 'github-actions[bot]'
- 'windwp'

View File

@ -0,0 +1,19 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 60
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
- pinned
- security
# Label to use when marking an issue as stale
staleLabel: wontfix
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false
only: issues

View File

@ -0,0 +1,44 @@
name: Tests
on: [push, pull_request]
jobs:
x64-ubuntu:
name: X64-ubuntu
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-20.04
url: https://github.com/neovim/neovim/releases/latest/download/nvim-linux64.tar.gz
manager: sudo apt-get
packages: -y fd-find
steps:
- uses: actions/checkout@v2
- run: date +%F > todays-date
- name: Restore from todays cache
uses: actions/cache@v2
with:
path: _neovim
key: ${{ runner.os }}-${{ matrix.url }}-${{ hashFiles('todays-date') }}
- name: Prepare
run: |
${{ matrix.manager }} update
${{ matrix.manager }} install ${{ matrix.packages }}
test -d _neovim || {
mkdir -p _neovim
curl -sL ${{ matrix.url }} | tar xzf - --strip-components=1 -C "${PWD}/_neovim"
}
mkdir -p ~/.local/share/nvim/site/pack/vendor/start
git clone --depth 1 https://github.com/nvim-lua/plenary.nvim ~/.local/share/nvim/site/pack/vendor/start/plenary.nvim
git clone --depth 1 https://github.com/nvim-treesitter/nvim-treesitter ~/.local/share/nvim/site/pack/vendor/start/nvim-treesitter
git clone --depth 1 https://github.com/nvim-treesitter/playground ~/.local/share/nvim/site/pack/vendor/start/playground
ln -s $(pwd) ~/.local/share/nvim/site/pack/vendor/start
- name: Run tests
run: |
export PATH="${PWD}/_neovim/bin:${PATH}"
export VIM="${PWD}/_neovim/share/nvim/runtime"
nvim --headless -u tests/minimal.vim -c "TSInstallSync all" -c "q"
make test

View File

@ -0,0 +1,51 @@
name: panvimdoc
on:
push:
paths:
- README.md
branches:
- master
jobs:
docs:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
name: markdown to vimdoc
steps:
- uses: actions/checkout@v2
- name: Setup git
run: |
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
printf 'VIMDOC_BRANCH=bot/vimdoc/%s\n' ${GITHUB_REF#refs/heads/} >> $GITHUB_ENV
- name: Checkout to vimdoc branch
run: git checkout -b ${VIMDOC_BRANCH}
- name: panvimdoc
uses: kdheepak/panvimdoc@v2.7.1
with:
vimdoc: nvim-autopairs
description: A super powerful autopair for Neovim.
- name: clone rules api docs
run: |
git clone --depth 1 https://github.com/windwp/nvim-autopairs.wiki.git ../nvim-autopairs.wiki
cp ../nvim-autopairs.wiki/Rules-API.md ./Rules-API.md
- name: panvimdoc
uses: kdheepak/panvimdoc@v2.7.1
with:
vimdoc: nvim-autopairs-rules
description: nvim-autopairs rules
pandoc: "Rules-API.md"
toc: true
- name: Create PR
run: |
if ! [[ -z $(git status -s) ]]; then
git add doc/nvim-autopairs.txt
git add doc/nvim-autopairs-rules.txt
git commit -m "chore: generated vimdoc"
git push --force https://${GITHUB_ACTOR}:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY} ${VIMDOC_BRANCH}
gh pr create --fill --base ${GITHUB_REF#refs/heads/} --head ${VIMDOC_BRANCH} || true
fi

View File

@ -0,0 +1 @@
doc/tags

View File

@ -0,0 +1,6 @@
{
"$schema": "https://raw.githubusercontent.com/sumneko/vscode-lua/master/setting/schema.json",
"Lua.diagnostics.disable": [
"undefined-field"
]
}

View File

@ -0,0 +1,22 @@
MIT License
Copyright (c) 2021 windwp
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.

View File

@ -0,0 +1,5 @@
test:
nvim --headless --noplugin -u tests/minimal.vim -c "PlenaryBustedDirectory tests/ {minimal_init = 'tests/minimal.vim'}"
test-file:
nvim --headless --noplugin -u tests/minimal.vim -c "lua require(\"plenary.busted\").run(\"$(FILE)\")"

View File

@ -0,0 +1,422 @@
## nvim-autopairs
A super powerful autopair plugin for Neovim that supports multiple characters.
Requires neovim 0.7
## Installation
Install the plugin with your preferred package manager:
### [lazy.nvim](https://github.com/folke/lazy.nvim)
```lua
{
'windwp/nvim-autopairs',
event = "InsertEnter",
config = true
-- use opts = {} for passing setup options
-- this is equalent to setup({}) function
}
```
### [vim-plug](https://github.com/junegunn/vim-plug)
```vim
Plug 'windwp/nvim-autopairs'
lua << EOF
require("nvim-autopairs").setup {}
EOF
```
### [packer](https://github.com/wbthomason/packer.nvim)
```lua
use {
"windwp/nvim-autopairs",
event = "InsertEnter",
config = function()
require("nvim-autopairs").setup {}
end
}
```
## Default values
``` lua
{
disable_filetype = { "TelescopePrompt", "spectre_panel" }
disable_in_macro = true -- disable when recording or executing a macro
disable_in_visualblock = false -- disable when insert after visual block mode
disable_in_replace_mode = true
ignored_next_char = [=[[%w%%%'%[%"%.%`%$]]=]
enable_moveright = true
enable_afterquote = true -- add bracket pairs after quote
enable_check_bracket_line = true --- check bracket in same line
enable_bracket_in_quote = true --
enable_abbr = false -- trigger abbreviation
break_undo = true -- switch for basic rule break undo sequence
check_ts = false
map_cr = true
map_bs = true -- map the <BS> key
map_c_h = false -- Map the <C-h> key to delete a pair
map_c_w = false -- map <c-w> to delete a pair if possible
}
```
### Override default values
``` lua
require('nvim-autopairs').setup({
disable_filetype = { "TelescopePrompt" , "vim" },
})
```
#### Mapping `<CR>`
```
Before Input After
------------------------------------
{|} <CR> {
|
}
------------------------------------
```
<details>
<summary><b>nvim-cmp</b></summary>
<h3>
You need to add mapping `CR` on nvim-cmp setup.
Check readme.md on nvim-cmp repo.
</h3>
``` lua
-- If you want insert `(` after select function or method item
local cmp_autopairs = require('nvim-autopairs.completion.cmp')
local cmp = require('cmp')
cmp.event:on(
'confirm_done',
cmp_autopairs.on_confirm_done()
)
```
You can customize the kind of completion to add `(` or any character.
```lua
local handlers = require('nvim-autopairs.completion.handlers')
cmp.event:on(
'confirm_done',
cmp_autopairs.on_confirm_done({
filetypes = {
-- "*" is a alias to all filetypes
["*"] = {
["("] = {
kind = {
cmp.lsp.CompletionItemKind.Function,
cmp.lsp.CompletionItemKind.Method,
},
handler = handlers["*"]
}
},
lua = {
["("] = {
kind = {
cmp.lsp.CompletionItemKind.Function,
cmp.lsp.CompletionItemKind.Method
},
---@param char string
---@param item table item completion
---@param bufnr number buffer number
---@param rules table
---@param commit_character table<string>
handler = function(char, item, bufnr, rules, commit_character)
-- Your handler function. Inspect with print(vim.inspect{char, item, bufnr, rules, commit_character})
end
}
},
-- Disable for tex
tex = false
}
})
)
```
Don't use `nil` to disable a filetype. If a filetype is `nil` then `*` is used as fallback.
</details>
<details>
<summary><b>coq_nvim</b></summary>
``` lua
local remap = vim.api.nvim_set_keymap
local npairs = require('nvim-autopairs')
npairs.setup({ map_bs = false, map_cr = false })
vim.g.coq_settings = { keymap = { recommended = false } }
-- these mappings are coq recommended mappings unrelated to nvim-autopairs
remap('i', '<esc>', [[pumvisible() ? "<c-e><esc>" : "<esc>"]], { expr = true, noremap = true })
remap('i', '<c-c>', [[pumvisible() ? "<c-e><c-c>" : "<c-c>"]], { expr = true, noremap = true })
remap('i', '<tab>', [[pumvisible() ? "<c-n>" : "<tab>"]], { expr = true, noremap = true })
remap('i', '<s-tab>', [[pumvisible() ? "<c-p>" : "<bs>"]], { expr = true, noremap = true })
-- skip it, if you use another global object
_G.MUtils= {}
MUtils.CR = function()
if vim.fn.pumvisible() ~= 0 then
if vim.fn.complete_info({ 'selected' }).selected ~= -1 then
return npairs.esc('<c-y>')
else
return npairs.esc('<c-e>') .. npairs.autopairs_cr()
end
else
return npairs.autopairs_cr()
end
end
remap('i', '<cr>', 'v:lua.MUtils.CR()', { expr = true, noremap = true })
MUtils.BS = function()
if vim.fn.pumvisible() ~= 0 and vim.fn.complete_info({ 'mode' }).mode == 'eval' then
return npairs.esc('<c-e>') .. npairs.autopairs_bs()
else
return npairs.autopairs_bs()
end
end
remap('i', '<bs>', 'v:lua.MUtils.BS()', { expr = true, noremap = true })
```
</details>
<details>
<summary><b>without completion plugin</b></summary>
```lua
-- add option map_cr
npairs.setup({ map_cr = true })
```
</details>
[another completion plugin](https://github.com/windwp/nvim-autopairs/wiki/Completion-plugin)
If you have a problem with indent after you press ` <CR> `
please check the settings of treesitter indent or install a plugin that has indent support for your filetype.
### Rule
nvim-autopairs uses rules with conditions to check pairs.
``` lua
local Rule = require('nvim-autopairs.rule')
local npairs = require('nvim-autopairs')
npairs.add_rule(Rule("$$","$$","tex"))
-- you can use some built-in conditions
local cond = require('nvim-autopairs.conds')
print(vim.inspect(cond))
npairs.add_rules({
Rule("$", "$",{"tex", "latex"})
-- don't add a pair if the next character is %
:with_pair(cond.not_after_regex("%%"))
-- don't add a pair if the previous character is xxx
:with_pair(cond.not_before_regex("xxx", 3))
-- don't move right when repeat character
:with_move(cond.none())
-- don't delete if the next character is xx
:with_del(cond.not_after_regex("xx"))
-- disable adding a newline when you press <cr>
:with_cr(cond.none())
},
-- disable for .vim files, but it work for another filetypes
Rule("a","a","-vim")
)
npairs.add_rules({
Rule("$$","$$","tex")
:with_pair(function(opts)
print(vim.inspect(opts))
if opts.line=="aa $$" then
-- don't add pair on that line
return false
end
end)
}
)
-- you can use regex
-- press u1234 => u1234number
npairs.add_rules({
Rule("u%d%d%d%d$", "number", "lua")
:use_regex(true)
})
-- press x1234 => x12341234
npairs.add_rules({
Rule("x%d%d%d%d$", "number", "lua")
:use_regex(true)
:replace_endpair(function(opts)
-- print(vim.inspect(opts))
return opts.prev_char:sub(#opts.prev_char - 3,#opts.prev_char)
end)
})
-- you can do anything with regex +special key
-- example press tab to uppercase text:
-- press b1234s<tab> => B1234S1234S
npairs.add_rules({
Rule("b%d%d%d%d%w$", "", "vim")
:use_regex(true,"<tab>")
:replace_endpair(function(opts)
return
opts.prev_char:sub(#opts.prev_char - 4,#opts.prev_char)
.."<esc>viwU"
end)
})
-- you can exclude filetypes
npairs.add_rule(
Rule("$$","$$")
:with_pair(cond.not_filetypes({"lua"}))
)
--- check ./lua/nvim-autopairs/rules/basic.lua
```
[Rules API](https://github.com/windwp/nvim-autopairs/wiki/Rules-API)
### Treesitter
You can use treesitter to check for a pair.
```lua
local npairs = require("nvim-autopairs")
local Rule = require('nvim-autopairs.rule')
npairs.setup({
check_ts = true,
ts_config = {
lua = {'string'},-- it will not add a pair on that treesitter node
javascript = {'template_string'},
java = false,-- don't check treesitter on java
}
})
local ts_conds = require('nvim-autopairs.ts-conds')
-- press % => %% only while inside a comment or string
npairs.add_rules({
Rule("%", "%", "lua")
:with_pair(ts_conds.is_ts_node({'string','comment'})),
Rule("$", "$", "lua")
:with_pair(ts_conds.is_not_ts_node({'function'}))
})
```
### Don't add pairs if it already has a close pair in the same line
if **next character** is a close pair and it doesn't have an open pair in same line, then it will not add a close pair
``` text
Before Input After
------------------------------------
( |)) ( ( (|))
```
``` lua
require('nvim-autopairs').setup({
enable_check_bracket_line = false
})
```
### Don't add pairs if the next char is alphanumeric
You can customize how nvim-autopairs will behave if it encounters a specific
character
``` lua
require('nvim-autopairs').setup({
ignored_next_char = "[%w%.]" -- will ignore alphanumeric and `.` symbol
})
```
``` text
Before Input After
------------------------------------
|foobar ( (|foobar
|.foobar ( (|.foobar
```
### Plugin Integration
``` lua
require('nvim-autopairs').disable()
require('nvim-autopairs').enable()
require('nvim-autopairs').remove_rule('(') -- remove rule (
require('nvim-autopairs').clear_rules() -- clear all rules
require('nvim-autopairs').get_rules('"')
```
* Sample
```lua
-- remove add single quote on filetype scheme or lisp
require("nvim-autopairs").get_rules("'")[1].not_filetypes = { "scheme", "lisp" }
require("nvim-autopairs").get_rules("'")[1]:with_pair(cond.not_after_text("["))
```
### FastWrap
``` text
Before Input After Note
-----------------------------------------------------------------
(|foobar <M-e> then press $ (|foobar)
(|)(foobar) <M-e> then press q (|(foobar))
(|foo bar <M-e> then press qh (|foo) bar
(|foo bar <M-e> then press qH (foo|) bar
(|foo bar <M-e> then press qH (foo)| bar if cursor_pos_before = false
```
```lua
-- put this to setup function and press <a-e> to use fast_wrap
npairs.setup({
fast_wrap = {},
})
-- change default fast_wrap
npairs.setup({
fast_wrap = {
map = '<M-e>',
chars = { '{', '[', '(', '"', "'" },
pattern = [=[[%'%"%>%]%)%}%,]]=],
end_key = '$',
before_key = 'h',
after_key = 'l',
cursor_pos_before = true,
keys = 'qwertyuiopzxcvbnmasdfghjkl',
manual_position = true,
highlight = 'Search',
highlight_grey='Comment'
},
})
```
### autotag html and tsx
[autotag](https://github.com/windwp/nvim-ts-autotag)
### Endwise
[endwise](https://github.com/windwp/nvim-autopairs/wiki/Endwise)
### Custom rules
[rules](https://github.com/windwp/nvim-autopairs/wiki/Custom-rules)
## Sponsors
Thanks to everyone who sponsors my projects and makes continued development maintenance possible!
<!-- patreon --><a href="https://github.com/t4t5"><img src="https://github.com/t4t5.png" width="60px" alt="" /></a><!-- patreon-->

View File

@ -0,0 +1,349 @@
*nvim-autopairs-rules.txt* nvim-autopairs rules
==============================================================================
Table of Contents *nvim-autopairs-rules-table-of-contents*
1. Rule Basics |nvim-autopairs-rules-rule-basics|
2. Controlling rule behavior |nvim-autopairs-rules-controlling-rule-behavior|
- Method Overview |nvim-autopairs-rules-method-overview|
- Conditions |nvim-autopairs-rules-conditions|
3. Method Explanations |nvim-autopairs-rules-method-explanations|
- The `with_*` methods |nvim-autopairs-rules-the-`with_*`-methods|
- The `use_*` methods |nvim-autopairs-rules-the-`use_*`-methods|
- Shorthand methods |nvim-autopairs-rules-shorthand-methods|
- Advanced methods |nvim-autopairs-rules-advanced-methods|
==============================================================================
1. Rule Basics *nvim-autopairs-rules-rule-basics*
At its core, a rule consists of two things: a **pair definition** and an
optional **declaration of filetypes** where the rule is in effect. A pair
definition has an opening part and a closing part. Each of these parts can be
as simple as a single character like a pair of parenthesis, or multiple
characters like Markdown code fences. Defining a rule is straightforward:
>
Rule(begin_pair, end_pair, filetypes)
<
Where `begin_pair` is the opening part of the pair and `end_pair` is the
closing part. `filetypes` may be specified in multiple ways:
>
Rule("(", ")") -- Enabled for all filetypes
Rule("(", ")", "markdown") -- As a string
Rule("(", ")", {"markdown", "vim"}) -- As a table
<
Additionally, it is possible to specify filetypes where the rule should **not**
be enabled by prefixing it with a `-` character:
>
Rule("(", ")", "-markdown") -- All filetypes *except* markdown
<
==============================================================================
2. Controlling rule behavior *nvim-autopairs-rules-controlling-rule-behavior*
By default, rules are very simple and will always complete a pair the moment
the opening part is typed. This is fine and in some cases desirable, but the
rules API allows you to control the manner and context in which pairs are
completed; this is done by attaching **conditions** (predicates) to **events**
and adding **modifiers** to the rule. `Rule` objects expose a variety of
methods to add these predicates and modifiers to the rule.
METHOD OVERVIEW *nvim-autopairs-rules-method-overview*
These methods allow control over if, when, and how rules perform completion of
pairs. Each method returns the `Rule` object so that they may be chained
together to easily define complex rules.
│ method │ usage │
│with_pair(cond) │add condition to check during pair event │
│with_move(cond) │add condition to check during move right event │
│with_cr(cond) │add condition to check during line break event │
│with_del(cond) │add condition to check during delete pair event │
│only_cr(cond) │enable _only_ the line break event; disable everything │
│ │else │
│use_regex(bool, "<k│interpret begin_pair as regex; optionally set trigger k│
│ey>") │ey │
│use_key("<key>") │set trigger key │
│replace_endpair(fun│define ending part with a function; optionally add with│
│c, check_pair) │_pair │
│set_end_pair_length│override offset used to position the cursor between the│
│(number) │ pair when replace_endpair is used │
│replace_map_cr(func│change the mapping for used for <CR> during the line br│
│) │eak event │
│end_wise(cond) │make the rule an end-wise rule │
AIDING UNDERSTANDING: "WHEN" INSTEAD OF "WITH" ~
It may be helpful to think of the `with_<event>` functions as reading more like
`when_<event>` instead, as the condition is checked **when** `<event>` happens
(or wants to happen). This naming scheme more accurately describes how the
`Rule` is affected and reads more intuitively when reading a rule definition.
For example, given a rule definition `Rule("(", ")")`, each method has a
certain effect on how and when the ending part of the pair, the closing
parenthesis, is completed. The ending part is only completed **when**
associated conditions are met upon typing the opening part of the pair.
CONDITIONS *nvim-autopairs-rules-conditions*
nvim-autopairs comes with a variety of common predicates ready to use simply by
including:
>
local cond = require('nvim-autopairs.conds')
<
│ function │ Usage │
│none() │always false │
│done() │always true │
│before_text(text) │text exists before opening part │
│after_text(text) │text exists after opening part │
│before_regex(regex, length│regex matches before opening part │
│) │ │
│after_regex(regex, length)│regex matches after opening part │
│ │ │
│not_before_text(text) │text is not before opening part │
│not_after_text(text) │text is not after opening part │
│not_before_regex(regex, le│regex doesnt match before opening part │
│ngth) │ │
│not_after_regex(regex, len│regex doesnt match after opening part │
│gth) │ │
│not_inside_quote() │not currently within quotation marks │
│is_inside_quote() │currently within quotation marks │
│not_filetypes({table}) │current filetype is not inside table │
│is_bracket_in_quote() │check the next char is quote and cursor is insi│
│ │de quote │
**N.B.** While `cond.not_filetypes` is available, its better to use the
minus syntax on the desired filetype in the initial rule declaration, since
then the rule is completely removed from the buffer.
TREESITTER CONDITIONS ~
Predicates based on the state of the Treesitter graph can be used by including:
>
local ts_conds = require('nvim-autopairs.ts-conds')
<
│ function │ Usage │
│is_ts_node({node_table}) │check current treesitter node│
│is_not_ts_node({node_table})│check not in treesitter node │
==============================================================================
3. Method Explanations *nvim-autopairs-rules-method-explanations*
This section explains each method in more detail: their signatures and how they
modify the rules behavior are all outlined here.
THE `WITH_*` METHODS *nvim-autopairs-rules-the-`with_*`-methods*
Calling these methods on a `Rule` will add predicate functions to their
corresponding event, which determines whether the effect of the event actually
takes place. There are no predicates if you dont define any, and so any
events without predicates behave as if they had a single predicate that always
returns true.
A `Rule` may have more than one predicate defined for a given event, and the
order that they are defined will be the order that they are checked. However,
the **first** non-`nil` value returned by a predicate is used and the remaining
predicates (if any) are **not** executed. In other words, predicates defined
earlier have priority over predicates defined later.
`WITH_PAIR(COND, POS)` ~
After typing the opening part, `cond` will fire and the ending part will only
be added if `cond` returned true. `with_pair` may be called more than once, and
by default, each predicate is appended to a list. When the "pair" event fires,
the _first_ predicate to return non-nil is used as the condition result.
Specifying `pos` allows explicit control over the order of the predicates.
`WITH_MOVE(COND)` ~
If `cond` is true, the cursor is simply moved right when typing the ending part
of the pair and the next character is also the ending part, e.g. `|" -> "|`
when typing `"`. If `cond` returns false, the ending part is inserted as normal
instead.
`WITH_CR(COND)` ~
If `cond` is true, then move the ending part of the pair to a new line below
the cursor after pressing `<CR>` while the cursor is between the pair (think
curly braces opening a block). Otherwise `<CR>` behaves as normal. For example:
>
{|}
<
Typing `<CR>` produces the following when `cond` is true:
>
{
|
}
<
`WITH_DEL(COND)` ~
If `cond` is true, when the cursor is between the pair, pressing `<BS>` to
delete the opening part of the pair will delete the ending part as well.
THE `USE_*` METHODS *nvim-autopairs-rules-the-`use_*`-methods*
The `use_*` functions alter how auto-pairing is triggered. Normally, the first
argument to `Rule` is taken literally as the opening part of the pair and as
soon as it is typed the "pair" event fires.
`USE_KEY(KEY)` ~
The pair is only completed when `key` is pressed, instead of the moment that
the opening part is typed. This is particularly useful in `use_regex`.
`USE_REGEX(BOOL, KEY)` ~
Causes the opening part to be interpreted as a lua pattern that triggers
insertion of the ending part when matched. If `key` is specified, then it acts
as an implicit `use_key`.
SHORTHAND METHODS *nvim-autopairs-rules-shorthand-methods*
These methods exist as convenient shortcuts for defining certain behaviors.
`END_WISE(FUNC)` ~
This method is used to make "end-wise" rules, which is terminology that should
be familiar to users of other auto-pair plugins, e.g. Lexima. Specifically,
this method makes it so that the ending part of the pair will be completed
_only upon pressing `<CR>` after the opening part_, in which case the "newline"
event is fired as usual.
This behavior is useful for languages with statement constructs like Lua and
Bash. For example, defining the following `Rule`:
>
Rule('then', 'end'):end_wise(function(opts))
-- Add any context checks here, e.g. line starts with "if"
return string.match(opts.line, '^%s*if') ~= nil
end)
<
And then pressing `<CR>` at the following cursor position:
>
if foo == bar then|
<
Would be completed as this (assuming some kind of automatic indent is enabled):
>
if foo == bar then
|
end
<
`ONLY_CR(COND)` ~
This shortcut method disables the "pair", "del", and "move" events by setting a
single predicate for each that is always false. Additionally, the effect of any
`use_key` modifiers are removed as well. If `cond` is specified, a "newline"
predicate is set as if `with_cr` were called.
This method is convenient for defining _simple_ end-wise rules. As an example,
a default rule is defined with `only_cr` for Markdown code blocks with an
explicit language; the closing triple-backtick is not completed until you press
`<CR>` after specifying the language:
>
```lua <-- <CR> pressed here
|
```
<
ADVANCED METHODS *nvim-autopairs-rules-advanced-methods*
These methods allow you to define more complex and dynamic rules. When combined
with `with_*` and `use_*` methods, it is possible to create very powerful
auto-pairs.
`REPLACE_ENDPAIR(FUNC, CHECK_PAIR)` ~
Facilitates the creation of dynamic ending parts. When the "pair" event fires
and the ending part is to be completed, `func` is called with a single `opts`
argument and should return a string. The returned string will be sent to
`nvim_feedkeys` to insert the ending part of the pair.
The `opts` parameter is a table that provides context for the current pair
completion, and can be useful for determining what to return. Note that because
`nvim_feedkeys` is used, arbitrary Vim functionality can be leveraged, such as
including `<Esc>` to be able to send normal mode commands.
*nvim-autopairs-rules-Optional-`check_pair`-parameter*
Optional `check_pair` parameter The `check_pair` parameter is optional,
and can either be a boolean or function.
If `check_pair` is a function, it is
passed as-is to `with_pair` to create a
"pair" predicate. If `check_pair` is
true, then an implicit
`with_pair(cond.after_text(rule.end_pair))`
predicate is added, where
`rule.end_pair` is the second argument
to the `Rule` constructor. If
`check_pair` is false, an "always false"
`with_pair` predicate is added.
As an example, these two rule definitions are equivalent:
>
-- This...
Rule("(", ")")
:use_key("<C-h>")
:replace_endpair(function() return "<BS><Del>" end, true)
-- ...is shorthand for this
Rule("(", "")
:use_key("<C-h>")
:with_pair(cond.after_text(")")) -- check that text after cursor is `)`
:replace_endpair(function() return "<BS><Del>" end)
<
`SET_END_PAIR_LENGTH(LEN)` ~
When completing the ending part of a pair, the cursor necessarily moves
backward so that is in between the opening part and the closing part. In order
to do this, the `Rule` must know the length of the ending part, which by
default is trivially determined. However, if you would like to override where
the cursor is placed after completion, i.e. using `replace_endpair`, you can
explicitly set the ending part length with this method.
`REPLACE_MAP_CR(FUNC)` ~
This method allows you to set a custom mapping for the "newline" (`<CR>`) event
that will be used instead of the normal behavior. This can be helpful for
enforcing certain styles or or adding additional edits. `func` is called with a
single `opts` argument and should return a string specifying the mapping for
`<CR>`. The default mapping is: `<C-g>u<CR><C-c>O`.
Generated by panvimdoc <https://github.com/kdheepak/panvimdoc>
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -0,0 +1,444 @@
*nvim-autopairs.txt* A super powerful autopair for Neovim.
==============================================================================
Table of Contents *nvim-autopairs-table-of-contents*
- nvim-autopairs |nvim-autopairs-nvim-autopairs|
- Installation |nvim-autopairs-installation|
- Default values |nvim-autopairs-default-values|
- Sponsors |nvim-autopairs-sponsors|
NVIM-AUTOPAIRS *nvim-autopairs-nvim-autopairs*
A super powerful autopair plugin for Neovim that supports multiple characters.
Requires neovim 0.7
INSTALLATION *nvim-autopairs-installation*
Install the plugin with your preferred package manager:
VIM-PLUG <HTTPS://GITHUB.COM/JUNEGUNN/VIM-PLUG> ~
>
Plug 'windwp/nvim-autopairs'
lua << EOF
require("nvim-autopairs").setup {}
EOF
<
PACKER <HTTPS://GITHUB.COM/WBTHOMASON/PACKER.NVIM> ~
>
use {
"windwp/nvim-autopairs",
config = function() require("nvim-autopairs").setup {} end
}
<
DEFAULT VALUES *nvim-autopairs-default-values*
>
local disable_filetype = { "TelescopePrompt" }
local disable_in_macro = false -- disable when recording or executing a macro
local disable_in_visualblock = false -- disable when insert after visual block mode
local disable_in_replace_mode = true
local ignored_next_char = [=[[%w%%%'%[%"%.%`%$]]=]
local enable_moveright = true
local enable_afterquote = true -- add bracket pairs after quote
local enable_check_bracket_line = true --- check bracket in same line
local enable_bracket_in_quote = true --
local enable_abbr = false -- trigger abbreviation
local break_undo = true -- switch for basic rule break undo sequence
local check_ts = false
local map_cr = true
local map_bs = true -- map the <BS> key
local map_c_h = false -- Map the <C-h> key to delete a pair
local map_c_w = false -- map <c-w> to delete a pair if possible
<
OVERRIDE DEFAULT VALUES ~
>
require('nvim-autopairs').setup({
disable_filetype = { "TelescopePrompt" , "vim" },
})
<
*nvim-autopairs-Mapping-`<CR>`*
>
Before Input After
------------------------------------
{|} <CR> {
|
}
------------------------------------
<
nvim-cmp ~
<h3>
You need to add mapping `CR` on nvim-cmp setup.
Check readme.md on nvim-cmp repo.
</h3>
>
-- If you want insert `(` after select function or method item
local cmp_autopairs = require('nvim-autopairs.completion.cmp')
local cmp = require('cmp')
cmp.event:on(
'confirm_done',
cmp_autopairs.on_confirm_done()
)
<
Mapping `<CR>` You can customize the kind of completion
to add `(` or any character.
>
local handlers = require('nvim-autopairs.completion.handlers')
cmp.event:on(
'confirm_done',
cmp_autopairs.on_confirm_done({
filetypes = {
-- "*" is a alias to all filetypes
["*"] = {
["("] = {
kind = {
cmp.lsp.CompletionItemKind.Function,
cmp.lsp.CompletionItemKind.Method,
},
handler = handlers["*"]
}
},
lua = {
["("] = {
kind = {
cmp.lsp.CompletionItemKind.Function,
cmp.lsp.CompletionItemKind.Method
},
---@param char string
---@param item table item completion
---@param bufnr number buffer number
---@param rules table
---@param commit_character table<string>
handler = function(char, item, bufnr, rules, commit_character)
-- Your handler function. Inpect with print(vim.inspect{char, item, bufnr, rules, commit_character})
end
}
},
-- Disable for tex
tex = false
}
})
)
<
Dont use `nil` to disable a filetype. If a filetype is `nil` then `*` is
used as fallback.
coq_nvim ~
>
local remap = vim.api.nvim_set_keymap
local npairs = require('nvim-autopairs')
npairs.setup({ map_bs = false, map_cr = false })
vim.g.coq_settings = { keymap = { recommended = false } }
-- these mappings are coq recommended mappings unrelated to nvim-autopairs
remap('i', '<esc>', [[pumvisible() ? "<c-e><esc>" : "<esc>"]], { expr = true, noremap = true })
remap('i', '<c-c>', [[pumvisible() ? "<c-e><c-c>" : "<c-c>"]], { expr = true, noremap = true })
remap('i', '<tab>', [[pumvisible() ? "<c-n>" : "<tab>"]], { expr = true, noremap = true })
remap('i', '<s-tab>', [[pumvisible() ? "<c-p>" : "<bs>"]], { expr = true, noremap = true })
-- skip it, if you use another global object
_G.MUtils= {}
MUtils.CR = function()
if vim.fn.pumvisible() ~= 0 then
if vim.fn.complete_info({ 'selected' }).selected ~= -1 then
return npairs.esc('<c-y>')
else
return npairs.esc('<c-e>') .. npairs.autopairs_cr()
end
else
return npairs.autopairs_cr()
end
end
remap('i', '<cr>', 'v:lua.MUtils.CR()', { expr = true, noremap = true })
MUtils.BS = function()
if vim.fn.pumvisible() ~= 0 and vim.fn.complete_info({ 'mode' }).mode == 'eval' then
return npairs.esc('<c-e>') .. npairs.autopairs_bs()
else
return npairs.autopairs_bs()
end
end
remap('i', '<bs>', 'v:lua.MUtils.BS()', { expr = true, noremap = true })
<
without completion plugin ~
>
-- add option map_cr
npairs.setup({ map_cr = true })
<
another completion plugin
<https://github.com/windwp/nvim-autopairs/wiki/Completion-plugin>
If you have a problem with indent after you press `<CR>` please check the
settings of treesitter indent or install a plugin that has indent support for
your filetype.
RULE ~
nvim-autopairs uses rules with conditions to check pairs.
>
local Rule = require('nvim-autopairs.rule')
local npairs = require('nvim-autopairs')
npairs.add_rule(Rule("$$","$$","tex"))
-- you can use some built-in conditions
local cond = require('nvim-autopairs.conds')
print(vim.inspect(cond))
npairs.add_rules({
Rule("$", "$",{"tex", "latex"})
-- don't add a pair if the next character is %
:with_pair(cond.not_after_regex("%%"))
-- don't add a pair if the previous character is xxx
:with_pair(cond.not_before_regex("xxx", 3))
-- don't move right when repeat character
:with_move(cond.none())
-- don't delete if the next character is xx
:with_del(cond.not_after_regex("xx"))
-- disable adding a newline when you press <cr>
:with_cr(cond.none())
},
-- disable for .vim files, but it work for another filetypes
Rule("a","a","-vim")
)
npairs.add_rules({
Rule("$$","$$","tex")
:with_pair(function(opts)
print(vim.inspect(opts))
if opts.line=="aa $$" then
-- don't add pair on that line
return false
end
end)
}
)
-- you can use regex
-- press u1234 => u1234number
npairs.add_rules({
Rule("u%d%d%d%d$", "number", "lua")
:use_regex(true)
})
-- press x1234 => x12341234
npairs.add_rules({
Rule("x%d%d%d%d$", "number", "lua")
:use_regex(true)
:replace_endpair(function(opts)
-- print(vim.inspect(opts))
return opts.prev_char:sub(#opts.prev_char - 3,#opts.prev_char)
end)
})
-- you can do anything with regex +special key
-- example press tab to uppercase text:
-- press b1234s<tab> => B1234S1234S
npairs.add_rules({
Rule("b%d%d%d%d%w$", "", "vim")
:use_regex(true,"<tab>")
:replace_endpair(function(opts)
return
opts.prev_char:sub(#opts.prev_char - 4,#opts.prev_char)
.."<esc>viwU"
end)
})
-- you can exclude filetypes
npairs.add_rule(
Rule("$$","$$")
:with_pair(cond.not_filetypes({"lua"}))
)
--- check ./lua/nvim-autopairs/rules/basic.lua
<
Rules API <https://github.com/windwp/nvim-autopairs/wiki/Rules-API>
TREESITTER ~
You can use treesitter to check for a pair.
>
local npairs = require("nvim-autopairs")
local Rule = require('nvim-autopairs.rule')
npairs.setup({
check_ts = true,
ts_config = {
lua = {'string'},-- it will not add a pair on that treesitter node
javascript = {'template_string'},
java = false,-- don't check treesitter on java
}
})
local ts_conds = require('nvim-autopairs.ts-conds')
-- press % => %% only while inside a comment or string
npairs.add_rules({
Rule("%", "%", "lua")
:with_pair(ts_conds.is_ts_node({'string','comment'})),
Rule("$", "$", "lua")
:with_pair(ts_conds.is_not_ts_node({'function'}))
})
<
DONT ADD PAIRS IF IT ALREADY HAS A CLOSE PAIR IN THE SAME LINE ~
if **next character** is a close pair and it doesnt have an open pair in
same line, then it will not add a close pair
>
Before Input After
------------------------------------
( |)) ( ( (|))
<
>
require('nvim-autopairs').setup({
enable_check_bracket_line = false
})
<
DONT ADD PAIRS IF THE NEXT CHAR IS ALPHANUMERIC ~
You can customize how nvim-autopairs will behave if it encounters a specific
character
>
require('nvim-autopairs').setup({
ignored_next_char = "[%w%.]" -- will ignore alphanumeric and `.` symbol
})
<
>
Before Input After
------------------------------------
|foobar ( (|foobar
|.foobar ( (|.foobar
<
PLUGIN INTEGRATION ~
>
require('nvim-autopairs').disable()
require('nvim-autopairs').enable()
require('nvim-autopairs').remove_rule('(') -- remove rule (
require('nvim-autopairs').clear_rules() -- clear all rules
-- get rule " then modify it. It can return a list of rule or just a rule
require('nvim-autopairs').get_rule('"')
<
- Sample
>
-- remove add single quote on filetype scheme or lisp
require("nvim-autopairs").get_rule("'")[1].not_filetypes = { "scheme", "lisp" }
require("nvim-autopairs").get_rule("'")[1]:with_pair(cond.not_after_text("["}))
<
FASTWRAP ~
>
Before Input After
--------------------------------------------------
(|foobar <M-e> then press $ (|foobar)
(|)(foobar) <M-e> then press q (|(foobar))
<
>
-- put this to setup function and press <a-e> to use fast_wrap
npairs.setup({
fast_wrap = {},
})
-- change default fast_wrap
npairs.setup({
fast_wrap = {
map = '<M-e>',
chars = { '{', '[', '(', '"', "'" },
pattern = [=[[%'%"%>%]%)%}%,]]=],
end_key = '$',
keys = 'qwertyuiopzxcvbnmasdfghjkl',
check_comma = true,
highlight = 'Search',
highlight_grey='Comment'
},
})
<
AUTOTAG HTML AND TSX ~
autotag <https://github.com/windwp/nvim-ts-autotag>
ENDWISE ~
endwise <https://github.com/windwp/nvim-autopairs/wiki/Endwise>
CUSTOM RULES ~
rules <https://github.com/windwp/nvim-autopairs/wiki/Custom-rules>
SPONSORS *nvim-autopairs-sponsors*
Thanks to everyone who sponsors my projects and makes continued development
maintenance possible! <!-- patreon --><a href="https://github.com/t4t5"><img
src="https://github.com/t4t5.png" width="60px" alt="" /></a><!-- patreon-->
Generated by panvimdoc <https://github.com/kdheepak/panvimdoc>
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -0,0 +1,664 @@
local log = require('nvim-autopairs._log')
local utils = require('nvim-autopairs.utils')
local basic_rule = require('nvim-autopairs.rules.basic')
local api = vim.api
local highlighter = nil
local M = {}
M.state = {
disabled = false,
rules = {},
buf_ts = {},
}
local default = {
map_bs = true,
map_c_h = false,
map_c_w = false,
map_cr = true,
disable_filetype = { 'TelescopePrompt', 'spectre_panel' },
disable_in_macro = true,
disable_in_visualblock = false,
disable_in_replace_mode = true,
ignored_next_char = [=[[%w%%%'%[%"%.%`%$]]=],
break_undo = true,
check_ts = false,
enable_moveright = true,
enable_afterquote = true,
enable_check_bracket_line = true,
enable_bracket_in_quote = true,
enable_abbr = false,
ts_config = {
lua = { 'string', 'source', 'string_content' },
javascript = { 'string', 'template_string' },
},
}
M.setup = function(opt)
M.config = vim.tbl_deep_extend('force', default, opt or {})
if M.config.fast_wrap then
require('nvim-autopairs.fastwrap').setup(M.config.fast_wrap)
end
M.config.rules = basic_rule.setup(M.config)
if M.config.check_ts then
local ok, ts_rule = pcall(require, 'nvim-autopairs.rules.ts_basic')
if ok then
highlighter = require "vim.treesitter.highlighter"
M.config.rules = ts_rule.setup(M.config)
end
end
if M.config.map_cr then
M.map_cr()
end
M.force_attach()
local group = api.nvim_create_augroup('autopairs_buf', { clear = true })
api.nvim_create_autocmd({ 'BufEnter', 'BufWinEnter' }, {
group = group, pattern = '*',
callback = function() M.on_attach() end
})
api.nvim_create_autocmd('BufDelete', {
group = group, pattern = '*',
callback = function(data)
local cur = api.nvim_get_current_buf()
local bufnr = tonumber(data.buf) or 0
if bufnr ~= cur then
M.set_buf_rule(nil, bufnr)
end
end,
})
api.nvim_create_autocmd('FileType', {
group = group, pattern = '*',
callback = function() M.force_attach() end
})
end
M.add_rule = function(rule)
M.add_rules({ rule })
end
M.get_rule = function(start_pair)
local tbl = M.get_rules(start_pair)
if #tbl == 1 then
return tbl[1]
end
return tbl
end
M.get_rules = function(start_pair)
local tbl = {}
for _, r in pairs(M.config.rules) do
if r.start_pair == start_pair then
table.insert(tbl, r)
end
end
return tbl
end
M.remove_rule = function(pair)
local tbl = {}
for _, r in pairs(M.config.rules) do
if r.start_pair ~= pair then
table.insert(tbl, r)
end
end
M.config.rules = tbl
if M.state.rules then
local state_tbl = {}
local rules = M.get_buf_rules()
for _, r in pairs(rules) do
if r.start_pair ~= pair then
table.insert(state_tbl, r)
elseif r.key_map and r.key_map ~= '' then
api.nvim_buf_del_keymap(0, 'i', r.key_map)
end
end
M.set_buf_rule(state_tbl, 0)
end
M.force_attach()
end
M.add_rules = function(rules)
for _, rule in pairs(rules) do
table.insert(M.config.rules, rule)
end
M.force_attach()
end
M.clear_rules = function()
M.state.rules = {}
M.config.rules = {}
end
M.disable = function()
M.state.disabled = true
end
M.enable = function()
M.state.disabled = false
end
--- force remap key to buffer
M.force_attach = function(bufnr)
utils.set_attach(bufnr, 0)
M.on_attach(bufnr)
end
local del_keymaps = function()
local status, autopairs_keymaps = pcall(api.nvim_buf_get_var, 0, 'autopairs_keymaps')
if status and autopairs_keymaps and #autopairs_keymaps > 0 then
for _, key in pairs(autopairs_keymaps) do
pcall(api.nvim_buf_del_keymap, 0, 'i', key)
end
end
end
local function is_disable()
if M.state.disabled then
return true
end
if vim.bo.filetype == '' and api.nvim_win_get_config(0).relative ~= '' then
-- disable for any floating window without filetype
return true
end
if vim.bo.modifiable == false then
return true
end
if M.config.disable_in_macro
and (vim.fn.reg_recording() ~= '' or vim.fn.reg_executing() ~= '')
then
return true
end
if M.config.disable_in_replace_mode and vim.api.nvim_get_mode().mode == "R" then
return true
end
if M.config.disable_in_visualblock and utils.is_block_wise_mode() then
return true
end
if utils.check_filetype(M.config.disable_filetype, vim.bo.filetype) then
del_keymaps()
M.set_buf_rule({}, 0)
return true
end
return false
end
---@return table <number, Rule>
M.get_buf_rules = function(bufnr)
return M.state.rules[bufnr or api.nvim_get_current_buf()] or {}
end
---@param rules nil|table list or rule
---@param bufnr number buffer number
M.set_buf_rule = function(rules, bufnr)
if bufnr == 0 or bufnr == nil then
bufnr = api.nvim_get_current_buf()
end
M.state.rules[bufnr] = rules
end
M.on_attach = function(bufnr)
-- log.debug('on_attach' .. vim.bo.filetype)
if is_disable() then
return
end
bufnr = bufnr or api.nvim_get_current_buf()
local rules = {}
for _, rule in pairs(M.config.rules) do
if utils.check_filetype(rule.filetypes, vim.bo.filetype)
and utils.check_not_filetype(rule.not_filetypes, vim.bo.filetype)
then
table.insert(rules, rule)
end
end
-- sort by pair and keymap
table.sort(rules, function(a, b)
if a.start_pair == b.start_pair then
if not b.key_map then
return a.key_map
end
if not a.key_map then
return b.key_map
end
return #a.key_map < #b.key_map
end
if #a.start_pair == #b.start_pair then
return string.byte(a.start_pair) > string.byte(b.start_pair)
end
return #a.start_pair > #b.start_pair
end)
M.set_buf_rule(rules, bufnr)
if M.config.check_ts then
if highlighter and highlighter.active[bufnr] then
M.state.ts_node = M.config.ts_config[vim.bo.filetype]
else
M.state.ts_node = nil
end
end
if utils.is_attached(bufnr) then
return
end
del_keymaps()
local enable_insert_auto = false
local autopairs_keymaps = {}
local expr_map = function(key)
api.nvim_buf_set_keymap(bufnr, 'i', key, '', {
expr = true,
noremap = true,
desc = "autopairs map key",
callback = function() return M.autopairs_map(bufnr, key) end,
})
table.insert(autopairs_keymaps, key)
end
for _, rule in pairs(rules) do
if rule.key_map ~= nil then
if rule.is_regex == false then
if rule.key_map == '' then
rule.key_map = rule.start_pair:sub(#rule.start_pair)
end
expr_map(rule.key_map)
local key_end = rule.key_end or rule.end_pair:sub(1, 1)
if #key_end >= 1 and key_end ~= rule.key_map and rule.move_cond ~= nil then
expr_map(key_end)
end
else
if rule.key_map ~= '' then
expr_map(rule.key_map)
elseif rule.is_endwise == false then
enable_insert_auto = true
end
end
end
end
api.nvim_buf_set_var(bufnr, 'autopairs_keymaps', autopairs_keymaps)
if enable_insert_auto then
-- capture all key use it to trigger regex pairs
-- it can make an issue with paste from register
api.nvim_create_autocmd('InsertCharPre', {
group = api.nvim_create_augroup(string.format("autopairs_insert_%d", bufnr), { clear = true }),
buffer = bufnr,
callback = function()
M.autopairs_insert(bufnr, vim.v.char)
end
})
end
if M.config.fast_wrap and M.config.fast_wrap.map then
api.nvim_buf_set_keymap(
bufnr,
'i',
M.config.fast_wrap.map,
"<esc>l<cmd>lua require('nvim-autopairs.fastwrap').show()<cr>",
{ noremap = true }
)
end
if M.config.map_bs then
api.nvim_buf_set_keymap(
bufnr,
'i',
'<bs>',
'',
{ callback = M.autopairs_bs, expr = true, noremap = true }
)
end
if M.config.map_c_h then
api.nvim_buf_set_keymap(
bufnr,
"i",
utils.key.c_h,
'',
{ callback = M.autopairs_c_h, expr = true, noremap = true }
)
end
if M.config.map_c_w then
api.nvim_buf_set_keymap(
bufnr,
'i',
'<c-w>',
'',
{ callback = M.autopairs_c_w, expr = true, noremap = true }
)
end
api.nvim_buf_set_var(bufnr, 'nvim-autopairs', 1)
end
local autopairs_delete = function(bufnr, key)
if is_disable() then
return utils.esc(key)
end
bufnr = bufnr or api.nvim_get_current_buf()
local line = utils.text_get_current_line(bufnr)
local _, col = utils.get_cursor()
local rules = M.get_buf_rules(bufnr)
for _, rule in pairs(rules) do
if rule.start_pair then
local prev_char, next_char = utils.text_cusor_line(
line,
col,
#rule.start_pair,
#rule.end_pair,
rule.is_regex
)
if utils.compare(rule.start_pair, prev_char, rule.is_regex)
and utils.compare(rule.end_pair, next_char, rule.is_regex)
and rule:can_del({
ts_node = M.state.ts_node,
rule = rule,
bufnr = bufnr,
prev_char = prev_char,
next_char = next_char,
line = line,
col = col,
})
then
local input = ''
for _ = 1, api.nvim_strwidth(rule.start_pair), 1 do
input = input .. utils.key.bs
end
for _ = 1, api.nvim_strwidth(rule.end_pair), 1 do
input = input .. utils.key.del
end
return utils.esc('<c-g>U' .. input)
end
end
end
return utils.esc(key)
end
M.autopairs_c_w = function(bufnr)
return autopairs_delete(bufnr, '<c-g>U<c-w>')
end
M.autopairs_c_h = function(bufnr)
return autopairs_delete(bufnr, utils.key.c_h)
end
M.autopairs_bs = function(bufnr)
return autopairs_delete(bufnr, utils.key.bs)
end
M.autopairs_map = function(bufnr, char)
if is_disable() then
return char
end
local line = utils.text_get_current_line(bufnr)
local _, col = utils.get_cursor()
local new_text = ''
local add_char = 1
local rules = M.get_buf_rules(bufnr)
for _, rule in pairs(rules) do
if rule.start_pair then
if char:match('<.*>') then
new_text = line
add_char = 0
else
new_text = line:sub(1, col) .. char .. line:sub(col + 1, #line)
add_char = rule.key_map and #rule.key_map or 1
end
-- log.debug("new_text:[" .. new_text .. "]")
local prev_char, next_char = utils.text_cusor_line(
new_text,
col + add_char,
#rule.start_pair,
#rule.end_pair,
rule.is_regex
)
local cond_opt = {
ts_node = M.state.ts_node,
text = new_text,
rule = rule,
bufnr = bufnr,
col = col + 1,
char = char,
line = line,
prev_char = prev_char,
next_char = next_char,
}
-- log.debug("start_pair" .. rule.start_pair)
-- log.debug('prev_char' .. prev_char)
-- log.debug('next_char' .. next_char)
local char_matches_rule = (rule.end_pair == char or rule.key_map == char)
-- for simple pairs, char will match end_pair
-- for more complex pairs, user should map the wanted end char with `use_key`
-- on a dedicated rule
if char_matches_rule
and utils.compare(rule.end_pair, next_char, rule.is_regex)
and rule:can_move(cond_opt)
then
local end_pair = rule:get_end_pair(cond_opt)
local end_pair_length = rule:get_end_pair_length(end_pair)
return utils.esc(utils.repeat_key(utils.key.join_right, end_pair_length))
end
if rule.key_map == char
and utils.compare(rule.start_pair, prev_char, rule.is_regex)
and rule:can_pair(cond_opt)
then
local end_pair = rule:get_end_pair(cond_opt)
local end_pair_length = rule:get_end_pair_length(end_pair)
local move_text = utils.repeat_key(utils.key.join_left, end_pair_length)
if add_char == 0 then
move_text = ''
char = ''
end
if end_pair:match('<.*>') then
end_pair = utils.esc(end_pair)
end
local result = char .. end_pair .. utils.esc(move_text)
if rule.is_undo then
result = utils.esc(utils.key.undo_sequence) .. result .. utils.esc(utils.key.undo_sequence)
end
if M.config.enable_abbr then
result = utils.esc(utils.key.abbr) .. result
end
log.debug("key_map :" .. result)
return result
end
end
end
return M.autopairs_afterquote(new_text, utils.esc(char))
end
M.autopairs_insert = function(bufnr, char)
if is_disable() then
return char
end
local line = utils.text_get_current_line(bufnr)
local _, col = utils.get_cursor()
local new_text = line:sub(1, col) .. char .. line:sub(col + 1, #line)
local rules = M.get_buf_rules(bufnr)
for _, rule in pairs(rules) do
if rule.start_pair and rule.is_regex and rule.key_map == '' then
local prev_char, next_char = utils.text_cusor_line(
new_text,
col + 1,
#rule.start_pair,
#rule.end_pair,
rule.is_regex
)
local cond_opt = {
ts_node = M.state.ts_node,
text = new_text,
rule = rule,
bufnr = bufnr,
col = col + 1,
char = char,
line = line,
prev_char = prev_char,
next_char = next_char,
}
-- log.debug("start_pair" .. rule.start_pair)
-- log.debug('prev_char' .. prev_char)
-- log.debug('next_char' .. next_char)
if next_char == rule.end_pair and rule:can_move(cond_opt) then
utils.set_vchar('')
vim.schedule(function()
utils.feed(utils.key.right, -1)
end)
return false
end
if utils.compare(rule.start_pair, prev_char, rule.is_regex)
and rule:can_pair(cond_opt)
then
local end_pair = rule:get_end_pair(cond_opt)
utils.set_vchar(char .. end_pair)
vim.schedule(function()
utils.feed(utils.key.left, rule:get_end_pair_length(end_pair))
end)
return
end
end
end
return char
end
M.autopairs_cr = function(bufnr)
if is_disable() then
return utils.esc('<cr>')
end
bufnr = bufnr or api.nvim_get_current_buf()
local line = utils.text_get_current_line(bufnr)
local _, col = utils.get_cursor()
-- log.debug("on_cr")
local rules = M.get_buf_rules(bufnr)
for _, rule in pairs(rules) do
if rule.start_pair then
local prev_char, next_char = utils.text_cusor_line(
line,
col,
#rule.start_pair,
#rule.end_pair,
rule.is_regex
)
local cond_opt = {
ts_node = M.state.ts_node,
check_endwise_ts = true,
rule = rule,
bufnr = bufnr,
col = col,
line = line,
prev_char = prev_char,
next_char = next_char,
}
-- log.debug('prev_char' .. rule.start_pair)
-- log.debug('prev_char' .. prev_char)
-- log.debug('next_char' .. next_char)
if rule.is_endwise
and utils.compare(rule.start_pair, prev_char, rule.is_regex)
and rule:can_cr(cond_opt)
then
local end_pair = rule:get_end_pair(cond_opt)
return utils.esc(
'<CR>' .. end_pair
-- FIXME do i need to re indent twice #118
.. '<CMD>normal! ====<CR><up><end><CR>'
)
end
cond_opt.check_endwise_ts = false
if utils.compare(rule.start_pair, prev_char, rule.is_regex)
and utils.compare(rule.end_pair, next_char, rule.is_regex)
and rule:can_cr(cond_opt)
then
log.debug('do_cr')
return utils.esc(rule:get_map_cr({ rule = rule, line = line, color = col, bufnr = bufnr }))
end
end
end
return utils.esc('<cr>')
end
--- add bracket pairs after quote (|"aaaaa" => (|"aaaaaa")
M.autopairs_afterquote = function(line, key_char)
if M.config.enable_afterquote and not utils.is_block_wise_mode() then
line = line or utils.text_get_current_line(0)
local _, col = utils.get_cursor()
local prev_char, next_char = utils.text_cusor_line(line, col + 1, 1, 1, false)
if utils.is_bracket(prev_char)
and utils.is_quote(next_char)
and not utils.is_in_quotes(line, col, next_char)
then
local count = 0
local index = 0
local is_prev_slash = false
local char_end = ''
for i = col, #line, 1 do
local char = line:sub(i, i + #next_char - 1)
if not is_prev_slash and char == next_char then
count = count + 1
char_end = line:sub(i + 1, i + #next_char)
index = i
end
is_prev_slash = char == '\\'
end
if count == 2 and index >= (#line - 2) then
local rules = M.get_buf_rules(api.nvim_get_current_buf())
for _, rule in pairs(rules) do
if rule.start_pair == prev_char and char_end ~= rule.end_pair then
local new_text = line:sub(0, index)
.. rule.end_pair
.. line:sub(index + 1, #line)
M.state.expr_quote = new_text
local append = 'a'
if col > 0 then
append = 'la'
end
return utils.esc(
"<esc><cmd>lua require'nvim-autopairs'.autopairs_closequote_expr()<cr>" .. append
)
end
end
end
end
end
return key_char
end
M.autopairs_closequote_expr = function()
---@diagnostic disable-next-line: param-type-mismatch
vim.fn.setline('.', M.state.expr_quote)
end
M.check_break_line_char = function()
return M.autopairs_cr()
end
M.completion_confirm =function ()
if vim.fn.pumvisible() ~= 0 then
return M.esc("<cr>")
else
return M.autopairs_cr()
end
end
M.map_cr = function()
api.nvim_set_keymap(
'i',
'<CR>',
"v:lua.require'nvim-autopairs'.completion_confirm()",
{ expr = true, noremap = true }
)
end
M.esc = utils.esc
return M

View File

@ -0,0 +1,13 @@
---@diagnostic disable: undefined-field
local empty = {
debug = function(_) end,
info = function(_) end,
error = function(_) end,
}
if _G.__is_log then
return require('plenary.log').new {
plugin = 'nvim-autopairs',
level = (_G.__is_log == true and 'debug') or 'warn',
} or empty
end
return empty

View File

@ -0,0 +1,97 @@
local autopairs = require('nvim-autopairs')
local handlers = require('nvim-autopairs.completion.handlers')
local cmp = require('cmp')
local Kind = cmp.lsp.CompletionItemKind
local M = {}
M.filetypes = {
-- Alias to all filetypes
["*"] = {
["("] = {
kind = { Kind.Function, Kind.Method },
handler = handlers["*"]
}
},
python = {
["("] = {
kind = { Kind.Function, Kind.Method },
handler = handlers.python
}
},
clojure = {
["("] = {
kind = { Kind.Function, Kind.Method },
handler = handlers.lisp
}
},
clojurescript = {
["("] = {
kind = { Kind.Function, Kind.Method },
handler = handlers.lisp
}
},
fennel = {
["("] = {
kind = { Kind.Function, Kind.Method },
handler = handlers.lisp
}
},
janet = {
["("] = {
kind = { Kind.Function, Kind.Method },
handler = handlers.lisp
}
},
tex = false,
plaintex = false,
context = false,
haskell = false,
purescript = false,
sh = false,
bash = false,
nix = false
}
M.on_confirm_done = function(opts)
opts = vim.tbl_deep_extend('force', {
filetypes = M.filetypes
}, opts or {})
return function(evt)
if evt.commit_character then
return
end
local entry = evt.entry
local commit_character = entry:get_commit_characters()
local bufnr = vim.api.nvim_get_current_buf()
local filetype = vim.api.nvim_buf_get_option(bufnr, 'filetype')
local item = entry:get_completion_item()
-- Without options and fallback
if not opts.filetypes[filetype] and not opts.filetypes["*"] then
return
end
if opts.filetypes[filetype] == false then
return
end
-- If filetype is nil then use *
local completion_options = opts.filetypes[filetype] or opts.filetypes["*"]
local rules = vim.tbl_filter(function(rule)
return completion_options[rule.key_map]
end, autopairs.get_buf_rules(bufnr))
for char, value in pairs(completion_options) do
if vim.tbl_contains(value.kind, item.kind) then
value.handler(char, item, bufnr, rules, commit_character)
end
end
end
end
return M

View File

@ -0,0 +1,86 @@
local npairs = require('nvim-autopairs')
local Completion = require('compe.completion')
local utils = require('nvim-autopairs.utils')
local method_kind = nil
local function_kind = nil
local options = {}
local M = {}
M.completion_done = function()
local line = utils.text_get_current_line(0)
local _, col = utils.get_cursor()
local prev_char, next_char = utils.text_cusor_line(line, col, 1, 1, false)
local filetype = vim.bo.filetype
local char = options.map_char[filetype] or options.map_char["all"] or '('
if char == '' then return end
if prev_char ~= char and next_char ~= char then
if method_kind == nil then
method_kind = require('vim.lsp.protocol').CompletionItemKind[2]
function_kind = require('vim.lsp.protocol').CompletionItemKind[3]
end
local item = Completion._confirm_item
if item.kind == method_kind or item.kind == function_kind then
-- check insert text have ( from snippet
local completion_item = item.user_data.compe.completion_item
if
(
completion_item.textEdit
and completion_item.textEdit.newText
and completion_item.textEdit.newText:match('[%(%[%$]')
)
or (completion_item.insertText and completion_item.insertText:match('[%(%[%$]'))
then
return
end
vim.api.nvim_feedkeys(char, 'i', true)
end
end
end
M.setup = function(opt)
opt = opt or { map_cr = true, map_complete = true, auto_select = false, map_char = {all = '('}}
if not opt.map_char then opt.map_char = {} end
options = opt
local map_cr = opt.map_cr
local map_complete = opt.map_complete
vim.g.completion_confirm_key = ''
if map_cr then
vim.api.nvim_set_keymap(
'i',
'<CR>',
'',
{ callback = M.completion_confirm, expr = true, noremap = true }
)
end
if opt.auto_select then
M.completion_confirm = function()
if vim.fn.pumvisible() ~= 0 then
return vim.fn['compe#confirm']({ keys = '<CR>', select = true })
else
return npairs.autopairs_cr()
end
end
else
M.completion_confirm = function()
if vim.fn.pumvisible() ~= 0 and vim.fn.complete_info()['selected'] ~= -1 then
return vim.fn['compe#confirm'](npairs.esc('<cr>'))
else
return npairs.autopairs_cr()
end
end
end
if map_complete then
vim.cmd([[
augroup autopairs_compe
autocmd!
autocmd User CompeConfirmDone lua require'nvim-autopairs.completion.compe'.completion_done()
augroup end
]])
end
end
return M

View File

@ -0,0 +1,91 @@
local autopairs = require('nvim-autopairs')
local utils = require('nvim-autopairs.utils')
local M = {}
---@param char string
---@param item table
---@param bufnr number
---@param rules table
---@param commit_character table<string>
M["*"] = function(char, item, bufnr, rules, _)
local line = utils.text_get_current_line(bufnr)
local _, col = utils.get_cursor()
local char_before, char_after = utils.text_cusor_line(line, col, 1, 1, false)
if char == '' or char_before == char or char_after == char
or (item.data and type(item.data) == 'table' and item.data.funcParensDisabled)
or (item.textEdit and item.textEdit.newText and item.textEdit.newText:match "[%(%[%$]")
or (item.insertText and item.insertText:match "[%(%[%$]")
then
return
end
if vim.tbl_isempty(rules) then
return
end
local new_text = ''
local add_char = 1
for _, rule in pairs(rules) do
if rule.start_pair then
local prev_char, next_char = utils.text_cusor_line(
new_text,
col + add_char,
#rule.start_pair,
#rule.end_pair,
rule.is_regex
)
local cond_opt = {
ts_node = autopairs.state.ts_node,
text = new_text,
rule = rule,
bufnr = bufnr,
col = col + 1,
char = char,
line = line,
prev_char = prev_char,
next_char = next_char,
}
if rule.key_map and rule:can_pair(cond_opt) then
vim.api.nvim_feedkeys(rule.key_map, "i", true)
return
end
end
end
end
---Handler with "clojure", "clojurescript", "fennel", "janet
M.lisp = function (char, item, bufnr, _, _)
local line = utils.text_get_current_line(bufnr)
local _, col = utils.get_cursor()
local char_before, char_after = utils.text_cusor_line(line, col, 1, 1, false)
local length = #item.label
if char == '' or char_before == char or char_after == char
or (item.data and item.data.funcParensDisabled)
or (item.textEdit and item.textEdit.newText and item.textEdit.newText:match "[%(%[%$]")
or (item.insertText and item.insertText:match "[%(%[%$]")
then
return
end
if utils.text_sub_char(line, col - length, 1) == "(" then
utils.feed("<Space>")
return
end
utils.feed(utils.key.left, length)
utils.feed(char)
utils.feed(utils.key.right, length)
utils.feed("<Space>")
end
M.python = function(char, item, bufnr, rules, _)
if item.data then
item.data.funcParensDisabled = false
end
M["*"](char,item,bufnr,rules)
end
return M

View File

@ -0,0 +1,350 @@
local utils = require('nvim-autopairs.utils')
local log = require('nvim-autopairs._log')
---@class CondOpts
---@field ts_node table
---@field text string
---@field rule table
---@field bufnr number
---@field col number
---@field char string
---@field line string
---@field prev_char string
---@field next_char string
---@field is_endwise string
local cond = {}
-- cond
-- @return false when it is not correct
-- true when it is correct
-- nil when it is not determine
-- stylua: ignore
cond.none = function()
return function() return false end
end
-- stylua: ignore
cond.done = function()
return function() return true end
end
cond.invert = function(func)
return function(...)
local result = func(...)
if result ~= nil then
return not result
end
return nil
end
end
cond.before_regex = function(regex, length)
length = length or 1
if length < 0 then length = nil end
length = length and -length
---@param opts CondOpts
return function(opts)
log.debug('before_regex')
local str = utils.text_sub_char(opts.line, opts.col - 1, length or -opts.col)
if str:match(regex) then
return true
end
return false
end
end
cond.before_text = function(text)
local length = #text
---@param opts CondOpts
return function(opts)
log.debug('before_text')
local str = utils.text_sub_char(opts.line, opts.col - 1, -length)
if str == text then
return true
end
return false
end
end
cond.after_text = function(text)
local length = #text
---@param opts CondOpts
return function(opts)
log.debug('after_text')
local str = utils.text_sub_char(opts.line, opts.col, length)
if str == text then
return true
end
return false
end
end
cond.after_regex = function(regex, length)
length = length or 1
if length < 0 then length = nil end
---@param opts CondOpts
return function(opts)
log.debug('after_regex')
local str = utils.text_sub_char(opts.line, opts.col, length or #opts.line)
if str:match(regex) then
return true
end
return false
end
end
cond.not_before_text = function(text)
local length = #text
return function(opts)
log.debug('not_before_text')
local str = utils.text_sub_char(opts.line, opts.col - 1, -length)
if str == text then
return false
end
end
end
cond.not_after_text = function(text)
local length = #text
---@param opts CondOpts
return function(opts)
log.debug('not_after_text')
local str = utils.text_sub_char(opts.line, opts.col, length)
if str == text then
return false
end
end
end
cond.not_before_regex = function(regex, length)
length = length or 1
if length < 0 then length = nil end
length = length and -length
---@param opts CondOpts
return function(opts)
log.debug('not_before_regex')
log.debug(length)
local str = utils.text_sub_char(opts.line, opts.col - 1, length or -opts.col)
if str:match(regex) then
return false
end
end
end
cond.not_after_regex = function(regex, length)
length = length or 1
if length < 0 then length = nil end
---@param opts CondOpts
return function(opts)
log.debug('not_after_regex')
local str = utils.text_sub_char(opts.line, opts.col, length or #opts.line)
if str:match(regex) then
return false
end
end
end
local function count_bracket_char(line, prev_char, next_char)
local count_prev_char = 0
local count_next_char = 0
for i = 1, #line, 1 do
local c = line:sub(i, i)
if c == prev_char then
count_prev_char = count_prev_char + 1
elseif c == next_char then
count_next_char = count_next_char + 1
end
end
return count_prev_char, count_next_char
end
-- Checks if bracket chars are balanced around specific postion.
---@param line string
---@param open_char string
---@param close_char string
---@param col integer position
local function is_brackets_balanced_around_position(line, open_char, close_char, col)
local balance = 0
for i = 1, #line, 1 do
local c = line:sub(i, i)
if c == open_char then
balance = balance + 1
elseif balance > 0 and c == close_char then
balance = balance - 1
if col <= i and balance == 0 then
break
end
end
end
return balance == 0
end
cond.is_bracket_line = function()
---@param opts CondOpts
return function(opts)
log.debug('is_bracket_line')
if utils.is_bracket(opts.char) and
(opts.next_char == opts.rule.end_pair
or opts.next_char == opts.rule.start_pair)
then
-- (( many char |)) => add
-- ( many char |)) => not add
local count_prev_char, count_next_char = count_bracket_char(
opts.line,
opts.rule.start_pair,
opts.rule.end_pair
)
if count_prev_char ~= count_next_char then
return false
end
end
end
end
cond.is_bracket_line_move = function()
---@param opts CondOpts
return function(opts)
log.debug('is_bracket_line_move')
if utils.is_close_bracket(opts.char)
and opts.char == opts.rule.end_pair
then
-- (( many char |)) => move
-- (( many char |) => not move
local is_balanced = is_brackets_balanced_around_position(
opts.line,
opts.rule.start_pair,
opts.char,
opts.col
)
return is_balanced
end
end
end
cond.not_inside_quote = function()
---@param opts CondOpts
return function(opts)
log.debug('not_inside_quote')
if utils.is_in_quotes(opts.text, opts.col - 1) then
return false
end
end
end
cond.is_inside_quote = function()
---@param opts CondOpts
return function(opts)
log.debug('is_inside_quote')
if utils.is_in_quotes(opts.text, opts.col - 1) then
return true
end
end
end
cond.not_add_quote_inside_quote = function()
---@param opts CondOpts
return function(opts)
log.debug('not_add_quote_inside_quote')
if utils.is_quote(opts.char)
and utils.is_in_quotes(opts.text, opts.col - 1)
then
return false
end
end
end
cond.move_right = function()
---@param opts CondOpts
return function(opts)
log.debug('move_right')
if opts.next_char == opts.char then
if utils.is_close_bracket(opts.char) then
return
end
-- move right when have quote on end line or in quote
-- situtaion |" => "|
if utils.is_quote(opts.char) then
if opts.col == string.len(opts.line) then
return
end
-- ("|") => (""|)
-- "" |" " => "" "| "
if utils.is_in_quotes(opts.line, opts.col - 1, opts.char) then
return
end
end
end
return false
end
end
cond.is_end_line = function()
---@param opts CondOpts
return function(opts)
log.debug('is_end_line')
local end_text = opts.line:sub(opts.col + 1)
-- end text is blank
if end_text ~= '' and end_text:match('^%s+$') == nil then
return false
end
end
end
--- Check the next char is quote and cursor is inside quote
cond.is_bracket_in_quote = function()
---@param opts CondOpts
return function(opts)
log.debug("is_bracket_in_quote")
if utils.is_bracket(opts.char)
and utils.is_quote(opts.next_char)
and utils.is_in_quotes(opts.line, opts.col - 1, opts.next_char)
then
return true
end
end
end
cond.not_filetypes = function(filetypes)
return function()
log.debug('not_filetypes')
for _, filetype in pairs(filetypes) do
if vim.bo.filetype == filetype then
return false
end
end
end
end
--- Check the character before the cursor is not equal
---@param char string character to compare
---@param index number the position of character before current curosr
cond.not_before_char = function(char, index)
index = index or 1
---@param opts CondOpts
return function(opts)
log.debug('not_before_char')
local match_char = #opts.line > index
and opts.line:sub(#opts.line - index, #opts.line - index) or ''
if match_char == char and match_char ~= "" then
return false
end
end
end
---@deprecated
cond.not_after_regex_check = cond.not_after_regex
---@deprecated
cond.after_regex_check = cond.after_regex
---@deprecated
cond.before_regex_check = cond.before_regex
---@deprecated
cond.not_before_regex_check = cond.not_before_regex
---@deprecated
cond.after_text_check = cond.after_text
---@deprecated
cond.not_after_text_check = cond.not_after_text
---@deprecated
cond.before_text_check = cond.before_text
---@deprecated
cond.not_before_text_check = cond.not_before_text
return cond

View File

@ -0,0 +1,222 @@
local utils = require('nvim-autopairs.utils')
local log = require('nvim-autopairs._log')
local npairs = require('nvim-autopairs')
local M = {}
local default_config = {
map = '<M-e>',
chars = { '{', '[', '(', '"', "'" },
pattern = [=[[%'%"%>%]%)%}%,%`]]=],
end_key = '$',
before_key = 'h',
after_key = 'l',
cursor_pos_before = true,
keys = 'qwertyuiopzxcvbnmasdfghjkl',
highlight = 'Search',
highlight_grey = 'Comment',
manual_position = true,
use_virt_lines = true
}
M.ns_fast_wrap = vim.api.nvim_create_namespace('autopairs_fastwrap')
local config = {}
M.setup = function(cfg)
if config.chars == nil then
config = vim.tbl_extend('force', default_config, cfg or {}) or {}
npairs.config.fast_wrap = config
end
end
function M.getchar_handler()
local ok, key = pcall(vim.fn.getchar)
if not ok then
return nil
end
if key ~= 27 and type(key) == 'number' then
local key_str = vim.fn.nr2char(key)
return key_str
end
return nil
end
M.show = function(line)
line = line or utils.text_get_current_line(0)
log.debug(line)
local row, col = utils.get_cursor()
local prev_char = utils.text_cusor_line(line, col, 1, 1, false)
local end_pair = ''
if utils.is_in_table(config.chars, prev_char) then
local rules = npairs.get_buf_rules()
for _, rule in pairs(rules) do
if rule.start_pair == prev_char then
end_pair = rule.end_pair
end
end
if end_pair == '' then
return
end
local list_pos = {}
local index = 1
local str_length = #line
local offset = -1
for i = col + 2, #line, 1 do
local char = line:sub(i, i)
local char2 = line:sub(i - 1, i)
if string.match(char, config.pattern)
or (char == ' ' and string.match(char2, '%w'))
then
local key = config.keys:sub(index, index)
index = index + 1
if not config.manual_position and (
utils.is_quote(char)
or (
utils.is_close_bracket(char)
and utils.is_in_quotes(line, col, prev_char)
)
)
then
offset = 0
end
if config.manual_position and i == str_length then
key = config.end_key
end
table.insert(
list_pos,
{ col = i + offset, key = key, char = char, pos = i }
)
end
end
local end_col, end_pos
if config.manual_position then
end_col = str_length + offset
end_pos = str_length
else
end_col = str_length + 1
end_pos = str_length + 1
end
-- add end_key to list extmark
if #list_pos == 0 or list_pos[#list_pos].key ~= config.end_key then
table.insert(
list_pos,
{ col = end_col, key = config.end_key, pos = end_pos, char = config.end_key }
)
end
-- Create a whitespace string for the current line which replaces every non whitespace
-- character with a space and preserves tabs, so we can use it for highlighting with
-- virtual lines so that highlighting lines up correctly.
-- The string is limited to the last position in list_pos
local whitespace_line = line:sub(1, list_pos[#list_pos].end_pos):gsub("[^ \t]", " ")
M.highlight_wrap(list_pos, row, col, #line, whitespace_line)
vim.defer_fn(function()
-- get the first char
local char = #list_pos == 1 and config.end_key or M.getchar_handler()
vim.api.nvim_buf_clear_namespace(0, M.ns_fast_wrap, row, row + 1)
for _, pos in pairs(list_pos) do
local hl_mark = {
{ pos = pos.pos - 1, key = config.before_key },
{ pos = pos.pos + 1, key = config.after_key },
}
if config.manual_position and (char == pos.key or char == string.upper(pos.key)) then
M.highlight_wrap(hl_mark, row, col, #line, whitespace_line)
M.choose_pos(row, line, pos, end_pair)
break
end
if char == pos.key then
M.move_bracket(line, pos.col, end_pair, false)
break
end
if char == string.upper(pos.key) then
M.move_bracket(line, pos.col, end_pair, true)
break
end
end
vim.cmd('startinsert')
end, 10)
return
end
vim.cmd('startinsert')
end
M.choose_pos = function(row, line, pos, end_pair)
vim.defer_fn(function()
-- select a second key
local char =
pos.char == nil and config.before_key
or pos.char == config.end_key and config.after_key
or M.getchar_handler()
vim.api.nvim_buf_clear_namespace(0, M.ns_fast_wrap, row, row + 1)
if not char then return end
local change_pos = false
local col = pos.col
if char == string.upper(config.before_key) or char == string.upper(config.after_key) then
change_pos = true
end
if char == config.after_key or char == string.upper(config.after_key) then
col = pos.col + 1
end
M.move_bracket(line, col, end_pair, change_pos)
vim.cmd('startinsert')
end, 10)
end
M.move_bracket = function(line, target_pos, end_pair, change_pos)
log.debug(target_pos)
line = line or utils.text_get_current_line(0)
local row, col = utils.get_cursor()
local _, next_char = utils.text_cusor_line(line, col, 1, 1, false)
-- remove an autopairs if that exist
if next_char == end_pair then
line = line:sub(1, col) .. line:sub(col + 2, #line)
target_pos = target_pos - 1
end
line = line:sub(1, target_pos) .. end_pair .. line:sub(target_pos + 1, #line)
vim.api.nvim_set_current_line(line)
if change_pos then
vim.api.nvim_win_set_cursor(0, { row + 1, target_pos + (config.cursor_pos_before and 0 or 1) })
end
end
M.highlight_wrap = function(tbl_pos, row, col, end_col, whitespace_line)
local bufnr = vim.api.nvim_win_get_buf(0)
if config.use_virt_lines then
local virt_lines = {}
local start = 0
for _, pos in ipairs(tbl_pos) do
virt_lines[#virt_lines + 1] = { whitespace_line:sub(start + 1, pos.pos - 1), 'Normal' }
virt_lines[#virt_lines + 1] = { pos.key, config.highlight }
start = pos.pos
end
vim.api.nvim_buf_set_extmark(bufnr, M.ns_fast_wrap, row, 0, {
virt_lines = { virt_lines },
hl_mode = 'blend',
})
else
if config.highlight_grey then
vim.highlight.range(
bufnr,
M.ns_fast_wrap,
config.highlight_grey,
{ row, col },
{ row, end_col },
{}
)
end
for _, pos in ipairs(tbl_pos) do
vim.api.nvim_buf_set_extmark(bufnr, M.ns_fast_wrap, row, pos.pos - 1, {
virt_text = { { pos.key, config.highlight } },
virt_text_pos = 'overlay',
hl_mode = 'blend',
})
end
end
end
return M

View File

@ -0,0 +1,224 @@
local Cond = require('nvim-autopairs.conds')
--- @class Rule
--- @field start_pair string
--- @field end_pair string
--- @field end_pair_func function dynamic change end_pair
--- @field map_cr_func function dynamic change mapping_cr
--- @field end_pair_length number change end_pair length for key map like <left>
--- @field key_map string|nil equal nil mean it will skip on autopairs map
--- @field filetypes table|nil
--- @field not_filetypes table|nil
--- @field is_regex boolean use regex to compare
--- @field is_multibyte boolean
--- @field is_endwise boolean only use on end_wise
--- @field is_undo boolean add break undo sequence
local Rule = setmetatable({}, {
__call = function(self, ...)
return self.new(...)
end,
})
---@return Rule
function Rule.new(...)
local params = { ... }
local opt = {}
if type(params[1]) == 'table' then
opt = params[1]
else
opt.start_pair = params[1]
opt.end_pair = params[2]
if type(params[3]) == 'string' then
opt.filetypes = { params[3] }
else
opt.filetypes = params[3]
end
end
opt = vim.tbl_extend('force', {
key_map = "",
start_pair = nil,
end_pair = nil,
end_pair_func = false,
filetypes = nil,
not_filetypes = nil,
move_cond = nil,
del_cond = {},
cr_cond = {},
pair_cond = {},
is_endwise = false,
is_regex = false,
is_multibyte = false,
end_pair_length = nil,
}, opt) or {}
---@param rule Rule
local function constructor(rule)
-- check multibyte
if #rule.start_pair ~= vim.api.nvim_strwidth(rule.start_pair) then
rule:use_multibyte()
end
-- check filetypes and not_filetypes
-- if have something like "-vim" it will add to not_filetypes
if rule.filetypes then
local ft, not_ft = {}, {}
for _, value in pairs(rule.filetypes) do
if value:sub(1, 1) == '-' then
table.insert(not_ft, value:sub(2, #value))
else
table.insert(ft, value)
end
end
rule.filetypes = #ft > 0 and ft or nil
rule.not_filetypes = #not_ft > 0 and not_ft or nil
end
return rule
end
local r = setmetatable(opt, { __index = Rule })
return constructor(r)
end
function Rule:use_regex(value, key_map)
self.is_regex = value
self.key_map = key_map or ''
return self
end
function Rule:use_key(key_map)
self.key_map = key_map or ''
return self
end
function Rule:use_undo(value)
self.is_undo = value
return self
end
function Rule:use_multibyte()
self.is_multibyte = true
self.end_pair_length = vim.fn.strdisplaywidth(self.end_pair)
self.key_map = string.match(self.start_pair, "[^\128-\191][\128-\191]*$")
self.key_end = string.match(self.end_pair, "[%z\1-\127\194-\244][\128-\191]*")
return self
end
function Rule:get_end_pair(opts)
if self.end_pair_func then
return self.end_pair_func(opts)
end
return self.end_pair
end
function Rule:get_map_cr(opts)
if self.map_cr_func then
return self.map_cr_func(opts)
end
return '<c-g>u<CR><CMD>normal! ====<CR><up><end><CR>'
end
function Rule:replace_map_cr(value)
self.map_cr_func = value
return self
end
function Rule:get_end_pair_length(opts)
if self.end_pair_length then
return self.end_pair_length
end
if type(opts) == 'string' then
return #opts
end
return #self.get_end_pair(opts)
end
function Rule:replace_endpair(value, check_pair)
self.end_pair_func = value
if check_pair ~= nil then
if check_pair == true then
self:with_pair(Cond.after_text(self.end_pair))
else
self:with_pair(check_pair)
end
end
return self
end
function Rule:set_end_pair_length(length)
self.end_pair_length = length
return self
end
function Rule:with_move(cond)
if self.move_cond == nil then self.move_cond = {} end
table.insert(self.move_cond, cond)
return self
end
function Rule:with_del(cond)
if self.del_cond == nil then self.del_cond = {} end
table.insert(self.del_cond, cond)
return self
end
function Rule:with_cr(cond)
if self.cr_cond == nil then self.cr_cond = {} end
table.insert(self.cr_cond, cond)
return self
end
---add condition to rule
---@param cond any
---@param pos number|nil = 1. It have higher priority to another condition
---@return Rule
function Rule:with_pair(cond, pos)
if self.pair_cond == nil then self.pair_cond = {} end
self.pair_cond[pos or (#self.pair_cond + 1)] = cond
return self
end
function Rule:only_cr(cond)
self.key_map = nil
self.pair_cond = false
self.move_cond = false
self.del_cond = false
if cond then return self:with_cr(cond) end
return self
end
function Rule:end_wise(cond)
self.is_endwise = true
return self:only_cr(cond)
end
local function can_do(conds, opt)
if type(conds) == 'table' then
for _, cond in pairs(conds) do
local result = cond(opt)
if result ~= nil then
return result
end
end
return true
elseif type(conds) == 'function' then
return conds(opt) == true
end
return false
end
function Rule:can_pair(opt)
return can_do(self.pair_cond, opt)
end
function Rule:can_move(opt)
return can_do(self.move_cond, opt)
end
function Rule:can_del(opt)
return can_do(self.del_cond, opt)
end
function Rule:can_cr(opt)
return can_do(self.cr_cond, opt)
end
return Rule

View File

@ -0,0 +1,86 @@
local Rule = require("nvim-autopairs.rule")
local cond = require("nvim-autopairs.conds")
local utils = require('nvim-autopairs.utils')
local function quote_creator(opt)
local quote = function(...)
local move_func = opt.enable_moveright and cond.move_right or cond.none
local rule = Rule(...)
:with_move(move_func())
:with_pair(cond.not_add_quote_inside_quote())
if #opt.ignored_next_char > 1 then
rule:with_pair(cond.not_after_regex(opt.ignored_next_char))
end
rule:use_undo(opt.break_undo)
return rule
end
return quote
end
local function bracket_creator(opt)
local quote = quote_creator(opt)
local bracket = function(...)
local rule = quote(...)
if opt.enable_check_bracket_line == true then
rule:with_pair(cond.is_bracket_line())
:with_move(cond.is_bracket_line_move())
end
if opt.enable_bracket_in_quote then
-- still add bracket if text is quote "|" and next_char have "
rule:with_pair(cond.is_bracket_in_quote(), 1)
end
return rule
end
return bracket
end
local function setup(opt)
local quote = quote_creator(opt)
local bracket = bracket_creator(opt)
local rules = {
Rule("<!--", "-->", { "html", "markdown" }):with_cr(cond.none()),
Rule("```", "```", { "markdown", "vimwiki", "rmarkdown", "rmd", "pandoc" }),
Rule("```.*$", "```", { "markdown", "vimwiki", "rmarkdown", "rmd", "pandoc" }):only_cr():use_regex(true),
Rule('"""', '"""', { "python", "elixir", "julia", "kotlin" }):with_pair(cond.not_before_char('"', 3)),
Rule("'''", "'''", { "python" }):with_pair(cond.not_before_char('"', 3)),
quote("'", "'", { "-rust", "-nix" })
:with_pair(function(opts)
-- python literals string
local str = utils.text_sub_char(opts.line, opts.col - 1, 1)
if vim.bo.filetype == 'python' and str:match("[frbuFRBU]") then
return true
end
end)
:with_pair(cond.not_before_regex("%w")),
quote("'", "'", "rust"):with_pair(cond.not_before_regex("[%w<&]")):with_pair(cond.not_after_text(">")),
Rule("''", "''", 'nix'):with_move(cond.after_text("'")),
quote("`", "`"),
quote('"', '"', "-vim"),
quote('"', '"', "vim"):with_pair(cond.not_before_regex("^%s*$", -1)),
bracket("(", ")"),
bracket("[", "]"),
bracket("{", "}"),
Rule(
">[%w%s]*$",
"^%s*</",
{
"html",
"htmldjango",
"php",
"typescript",
"typescriptreact",
"javascript",
"javascriptreact",
"svelte",
"vue",
"xml",
"rescript",
"astro",
}
):only_cr():use_regex(true),
}
return rules
end
return { setup = setup, quote_creator = quote_creator, bracket_creator = bracket_creator }

View File

@ -0,0 +1,9 @@
local endwise = require('nvim-autopairs.ts-rule').endwise
local rules = {
endwise('%sdo$', 'end', 'elixir', nil),
endwise('fn$', 'end', 'elixir', nil),
endwise('fn.*->$', 'end', 'elixir', nil),
}
return rules

View File

@ -0,0 +1,9 @@
local endwise = require('nvim-autopairs.ts-rule').endwise
local rules = {
endwise('then$', 'end', 'lua', 'if_statement'),
endwise('function.*%(.*%)$', 'end', 'lua', {'function_declaration', 'local_function', 'function'}),
}
return rules

View File

@ -0,0 +1,17 @@
local endwise = require('nvim-autopairs.ts-rule').endwise
local rules = {
endwise('%sdo$', 'end', 'ruby', nil),
endwise('%sdo%s|.*|$', 'end', 'ruby', nil),
endwise('begin$', 'end', 'ruby', nil),
endwise('def%s.+$', 'end', 'ruby', nil),
endwise('module%s.+$', 'end', 'ruby', nil),
endwise('class%s.+$', 'end', 'ruby', nil),
endwise('[%s=]%sif%s.+$', 'end', 'ruby', nil),
endwise('[%s=]%sunless%s.+$', 'end', 'ruby', nil),
endwise('[%s=]%scase%s.+$', 'end', 'ruby', nil),
endwise('[%s=]%swhile%s.+$', 'end', 'ruby', nil),
endwise('[%s=]%suntil%s.+$', 'end', 'ruby', nil),
}
return rules

View File

@ -0,0 +1,22 @@
local basic = require('nvim-autopairs.rules.basic')
local utils = require('nvim-autopairs.utils')
local ts_conds = require('nvim-autopairs.ts-conds')
local ts_extend = {
"'",
'"',
'(',
'[',
'{',
'`',
}
return {
setup = function (config)
local rules=basic.setup(config)
for _, rule in pairs(rules) do
if utils.is_in_table(ts_extend, rule.start_pair) then
rule:with_pair(ts_conds.is_not_ts_node_comment())
end
end
return rules
end
}

View File

@ -0,0 +1,159 @@
local log = require('nvim-autopairs._log')
local utils = require('nvim-autopairs.utils')
local ts_get_node_text = vim.treesitter.get_node_text or vim.treesitter.query.get_node_text
local conds = {}
conds.is_endwise_node = function(nodes)
if nodes == nil then return function() return true end end
if type(nodes) == 'string' then nodes = {nodes} end
return function (opts)
log.debug('is_endwise_node')
if not opts.check_endwise_ts then return true end
if nodes == nil then return true end
if #nodes == 0 then return true end
vim.treesitter.get_parser():parse()
local target = vim.treesitter.get_node({ ignore_injections = false })
if target ~= nil and utils.is_in_table(nodes, target:type()) then
local text = ts_get_node_text(target) or {""}
local last = text[#text]:match(opts.rule.end_pair)
-- check last character is match with end_pair
if last == nil then
return true
end
-- log.debug('last:' .. last)
-- if match then we need tocheck parent node
-- some time treesiter is group 2 node then we need check that
local begin_target,_, end_target = target:range()
local begin_parent,_, end_parent = target:parent():range()
-- log.debug(target:range())
-- log.debug(ts_get_node_text(target))
-- log.debug(target:parent():range())
-- log.debug(ts_get_node_text(target:parent()))
if
(
begin_target ~= begin_parent
and end_target == end_parent
)
or
(end_parent - end_target == 1)
then
return true
end
-- return true
else
end
return false
end
end
conds.is_in_range = function(callback, position)
assert(
type(callback) == 'function' and type(position) == 'function',
'callback and position should be a function'
)
return function(opts)
log.debug('is_in_range')
-- `parser` will be a table (on success) or a string (error message)
local _, parser = pcall(vim.treesitter.get_parser)
if not type(parser) == 'string' then
return
end
local cursor = position()
assert(
type(cursor) == 'table' and #cursor == 2,
'position should be return a table like {line, col}'
)
local line = cursor[1]
local col = cursor[2]
local bufnr = 0
local root_lang_tree = vim.treesitter.get_parser(bufnr)
local lang_tree = root_lang_tree:language_for_range({ line, col, line, col })
local result
for _, tree in ipairs(lang_tree:trees()) do
local root = tree:root()
if root and vim.treesitter.is_in_node_range(root, line, col) then
local node = root:named_descendant_for_range(line, col, line, col)
local anonymous_node = root:descendant_for_range(
line,
col,
line,
col
)
result = {
node = node,
lang = lang_tree:lang(),
type = node:type(),
cursor = vim.api.nvim_win_get_cursor(0),
line = vim.api.nvim_buf_get_lines(bufnr, line, line + 1, true)[1],
range = { node:range() },
anonymous = anonymous_node:type(),
}
end
end
return callback(result)
end
end
conds.is_ts_node = function(nodes)
if type(nodes) == 'string' then nodes = {nodes} end
assert(nodes ~= nil, "ts nodes should be string or table")
return function (opts)
log.debug('is_ts_node')
if #nodes == 0 then return end
vim.treesitter.get_parser():parse()
local target = vim.treesitter.get_node({ ignore_injections = false })
if target ~= nil and utils.is_in_table(nodes, target:type()) then
return true
end
return false
end
end
conds.is_not_ts_node = function(nodes)
if type(nodes) == 'string' then nodes = {nodes} end
assert(nodes ~= nil, "ts nodes should be string or table")
return function (opts)
log.debug('is_not_ts_node')
if #nodes == 0 then return end
vim.treesitter.get_parser():parse()
local target = vim.treesitter.get_node({ ignore_injections = false })
if target ~= nil and utils.is_in_table(nodes, target:type()) then
return false
end
end
end
conds.is_not_ts_node_comment = function()
return function(opts)
log.debug('not_in_ts_node_comment')
if not opts.ts_node then return end
vim.treesitter.get_parser():parse()
local target = vim.treesitter.get_node({ ignore_injections = false })
if target ~= nil and utils.is_in_table(opts.ts_node, target:type()) then
return false
end
end
end
conds.is_not_in_context = function()
return function(opts)
local context = require("nvim-autopairs.ts-utils")
.get_language_tree_at_position({ utils.get_cursor() })
if not vim.tbl_contains(opts.rule.filetypes, context:lang()) then
return false
end
end
end
return conds

View File

@ -0,0 +1,18 @@
local Rule = require('nvim-autopairs.rule')
local cond = require('nvim-autopairs.conds')
local ts_conds = require('nvim-autopairs.ts-conds')
return {
endwise = function (...)
local params = {...}
local rule = Rule(...)
:use_regex(true)
:end_wise(cond.is_end_line())
if params[4] then
-- rule:with_cr(ts_conds.is_endwise_node(params[4]))
rule:with_cr(ts_conds.is_ts_node(params[4]))
end
return rule
end
}

View File

@ -0,0 +1,24 @@
local ts_get_node_text = vim.treesitter.get_node_text or vim.treesitter.query.get_node_text
local M = {}
--- Returns the language tree at the given position.
---@return LanguageTree
function M.get_language_tree_at_position(position)
local language_tree = vim.treesitter.get_parser()
language_tree:for_each_tree(function(_, tree)
if tree:contains(vim.tbl_flatten({ position, position })) then
language_tree = tree
end
end)
return language_tree
end
function M.get_tag_name(node)
local tag_name = nil
if node ~=nil then
tag_name = ts_get_node_text(node)
end
return tag_name
end
return M

View File

@ -0,0 +1,198 @@
local M = {}
local api = vim.api
local log = require('nvim-autopairs._log')
M.key = {
del = "<del>",
bs = "<bs>",
c_h = "<C-h>",
left = "<left>",
right = "<right>",
join_left = "<c-g>U<left>",
join_right = "<c-g>U<right>",
undo_sequence = "<c-g>u",
noundo_sequence = "<c-g>U",
abbr = "<c-]>"
}
M.set_vchar = function(text)
text = text:gsub('"', '\\"')
vim.v.char = text
end
M.is_quote = function(char)
return char == "'" or char == '"' or char == '`'
end
M.is_bracket = function(char)
return char == "(" or char == '[' or char == '{' or char == '<'
end
M.is_close_bracket = function(char)
return char == ")" or char == ']' or char == '}' or char == '>'
end
M.compare = function(value, text, is_regex)
if is_regex and string.match(text, value) then
return true
elseif text == value then
return true
end
return false
end
---check cursor is inside a quote
---@param line string
---@param pos number position in line
---@param quote_type nil|string specify a quote
---@return boolean
M.is_in_quotes = function(line, pos, quote_type)
local cIndex = 0
local result = false
local last_char = quote_type or ''
while cIndex < string.len(line) and cIndex < pos do
cIndex = cIndex + 1
local char = line:sub(cIndex, cIndex)
local prev_char = line:sub(cIndex - 1, cIndex - 1)
if
result == true
and char == last_char
and prev_char ~= "\\"
then
result = false
last_char = quote_type or ''
elseif
result == false
and M.is_quote(char)
and (not quote_type or char == quote_type)
--a single quote with a word before is not count unless it is a
-- prefixed string in python (e.g. f'string {with_brackets}')
and not (
char == "'"
and prev_char:match('%w')
and (vim.bo.filetype ~= 'python' or prev_char:match('[^frbuFRBU]'))
)
then
last_char = quote_type or char
result = true
end
end
return result
end
M.is_attached = function(bufnr)
local _, check = pcall(api.nvim_buf_get_var, bufnr or 0, "nvim-autopairs")
return check == 1
end
M.set_attach = function(bufnr, value)
api.nvim_buf_set_var(bufnr or 0, "nvim-autopairs", value)
end
M.is_in_table = function(tbl, val)
if tbl == nil then return false end
for _, value in pairs(tbl) do
if val == value then return true end
end
return false
end
M.check_filetype = function(tbl, filetype)
if tbl == nil then return true end
return M.is_in_table(tbl, filetype)
end
M.check_not_filetype = function(tbl, filetype)
if tbl == nil then return true end
return not M.is_in_table(tbl, filetype)
end
M.is_in_range = function(row, col, range)
local start_row, start_col, end_row, end_col = unpack(range)
return (row > start_row or (start_row == row and col >= start_col))
and (row < end_row or (row == end_row and col <= end_col))
end
M.get_cursor = function(bufnr)
local row, col = unpack(api.nvim_win_get_cursor(bufnr or 0))
return row - 1, col
end
M.text_get_line = function(bufnr, lnum)
return api.nvim_buf_get_lines(bufnr, lnum, lnum + 1, false)[1] or ''
end
M.text_get_current_line = function(bufnr)
local row = unpack(api.nvim_win_get_cursor(0)) or 1
return M.text_get_line(bufnr, row - 1)
end
M.repeat_key = function(key, num)
local text = ''
for _ = 1, num, 1 do
text = text .. key
end
return text
end
--- cut text from position with number character
---@param line string text
---@param col number position of text
---@param prev_count number number char previous
---@param next_count number number char next
---@param is_regex boolean if it is regex then will cut all
M.text_cusor_line = function(line, col, prev_count, next_count, is_regex)
if is_regex then
prev_count = col
next_count = #line - col
end
local prev = M.text_sub_char(line, col, -prev_count)
local next = M.text_sub_char(line, col + 1, next_count)
return prev, next
end
M.text_sub_char = function(line, start, num)
local finish = start
if num < 0 then
start = start + num + 1
else
finish = start + num - 1
end
return string.sub(line, start, finish)
end
-- P(M.text_sub_char("aa'' aaa", 3, -1))
M.insert_char = function(text)
api.nvim_put({ text }, "c", false, true)
end
M.feed = function(text, num)
num = num or 1
if num < 1 then num = 1 end
local result = ''
for _ = 1, num, 1 do
result = result .. text
end
log.debug("result" .. result)
api.nvim_feedkeys(api.nvim_replace_termcodes(
result, true, false, true),
"n", true)
end
M.esc = function(cmd)
return vim.api.nvim_replace_termcodes(cmd, true, false, true)
end
M.is_block_wise_mode = function()
return vim.fn.visualmode() == ''
end
--- get prev_char with out key_map
M.get_prev_char = function(opt)
return opt.line:sub(opt.col - 1, opt.col + #opt.rule.start_pair - 2)
end
return M

View File

@ -0,0 +1,4 @@
column_width = 85
indent_type = "Spaces"
quote_style = "AutoPreferSingle"
indent_width = 4

View File

@ -0,0 +1,99 @@
local npairs = require('nvim-autopairs')
_G.npairs = npairs
npairs.setup({
enable_afterquote = true,
})
local data = {
{
name = 'add bracket after quote ',
filepath = './tests/endwise/init.lua',
filetype = 'lua',
linenr = 5,
key = [[(]],
before = [[const abc=|"test" ]],
after = [[const abc=(|"test") ]],
},
{
name = 'add bracket after quote ',
filepath = './tests/endwise/init.lua',
filetype = 'lua',
linenr = 5,
key = [[(]],
before = [[|"test"]],
after = [[(|"test")]],
},
{
name = 'check quote without any text on end similar',
filepath = './tests/endwise/init.lua',
filetype = 'lua',
linenr = 5,
key = [[(]],
before = [[ const [template, setTemplate] = useState|'')]],
after = [[ const [template, setTemplate] = useState(|'')]],
},
{
name = 'add bracket after quote ',
filepath = './tests/endwise/init.lua',
filetype = 'lua',
linenr = 5,
key = [[{]],
before = [[(|"test") ]],
after = [[({|"test"}) ]],
},
{
name = 'add bracket after quote ',
filepath = './tests/endwise/init.lua',
filetype = 'lua',
linenr = 5,
key = [[(]],
before = [[const abc=|"visu\"dsa" ]],
after = [[const abc=(|"visu\"dsa") ]],
},
{
name = 'not add on exist quote',
filepath = './tests/endwise/init.lua',
filetype = 'lua',
linenr = 5,
key = [[(]],
before = [[const abc=|"visu\"dsa") ]],
after = [[const abc=(|"visu\"dsa") ]],
},
{
name = 'test add close quote on match',
filepath = './tests/endwise/init.lua',
filetype = 'lua',
linenr = 5,
key = [[(]],
before = [[const abc=|"visu\"dsa" ]],
after = [[const abc=(|"visu\"dsa") ]],
},
{
name = 'not add bracket with quote have comma',
filepath = './tests/endwise/init.lua',
filetype = 'lua',
linenr = 5,
key = [[(]],
before = [[|"data", abcdef]],
after = [[(|"data", abcdef]],
},
{
name = 'not add bracket with quote have comma',
filepath = './tests/endwise/init.lua',
filetype = 'lua',
linenr = 5,
key = [[(]],
before = [[|"data", "abcdef"]],
after = { [[(|"data", "abcdef"]] },
},
}
local run_data = _G.Test_filter(data)
describe('[afterquote tag]', function()
_G.Test_withfile(run_data, {})
end)

View File

@ -0,0 +1,22 @@
local data1, data2
local function wind()
vim.api.nvim_get_current_buf()
end

View File

@ -0,0 +1,15 @@
fn main() {
}

View File

@ -0,0 +1,4 @@
module MyFirstModule
end

View File

@ -0,0 +1,18 @@
local M={}
M.autopairs_bs = function(rules)
for _, rule in pairs(rules) do
if rule.start_pair then
end
if rule.start_pair then
end
end
end
return M

View File

@ -0,0 +1,9 @@
# Example Markdown File
```javascript
let;
let;
let;
let;
let;
```

View File

@ -0,0 +1,83 @@
local npairs = require('nvim-autopairs')
local ts = require('nvim-treesitter.configs')
local log = require('nvim-autopairs._log')
ts.setup({
ensure_installed = { 'lua' },
highlight = { enable = true },
})
_G.npairs = npairs
vim.api.nvim_set_keymap(
'i',
'<CR>',
'v:lua.npairs.autopairs_cr()',
{ expr = true, noremap = true }
)
local data = {
{
name = 'lua function add endwise',
filepath = './tests/endwise/init.lua',
filetype = 'lua',
linenr = 5,
key = [[<cr>]],
before = [[function a()| ]],
after = {
[[function a() ]],
[[| ]],
[[ end ]],
},
},
{
name = 'lua function add endwise',
filepath = './tests/endwise/init.lua',
filetype = 'lua',
linenr = 5,
key = [[<cr>]],
before = [[function a()|x ab ]],
after = {
[[function a() ]],
[[|x ab]],
},
},
{
name = 'add if endwise',
filepath = './tests/endwise/init.lua',
filetype = 'lua',
linenr = 5,
key = [[<cr>]],
before = [[if data== 'fdsafdsa' then| ]],
after = {
[[if data== 'fdsafdsa' then ]],
[[|]],
[[end ]],
},
},
{
name = 'undo on<cr> key',
filepath = './tests/endwise/init.lua',
filetype = 'lua',
linenr = 5,
key = [[{<cr><esc>u]],
before = [[local abc = | ]],
after = {
[[local abc = {|} ]],
[[]],
[[]],
},
},
}
local run_data = _G.Test_filter(data)
describe('[endwise tag]', function()
_G.Test_withfile(run_data, {
-- need to understand this ??? new line make change cursor zzz
cursor_add = 1,
before_each = function(value)
npairs.add_rules(
require('nvim-autopairs.rules.endwise-' .. value.filetype)
)
end,
})
end)

View File

@ -0,0 +1,53 @@
-- local npairs = require('nvim-autopairs')
-- _G.npairs = npairs
-- npairs.setup({
-- enable_afterquote = true,
-- fast_wrap = true,
-- })
-- local data = {
-- {
-- only = true,
-- name = 'move end wise after quote ',
-- filepath = './tests/endwise/init.lua',
-- filetype = 'lua',
-- linenr = 5,
-- key = [[<M-e>]],
-- before = [[const abc=(|"test",data ]],
-- after = [[const abc=(|"test"),data ]],
-- },
-- {
-- name = 'move end wise after quote ',
-- filepath = './tests/endwise/init.lua',
-- filetype = 'lua',
-- linenr = 5,
-- key = [[<M-e>]],
-- before = [[const abc=(|"test"),data ]],
-- after = [[const abc=(|"test",data) ]],
-- },
-- {
-- name = 'move end wise after quote ',
-- filepath = './tests/endwise/init.lua',
-- filetype = 'lua',
-- linenr = 5,
-- key = [[<M-e>]],
-- before = [[const abc=(|"test",data),dadas ]],
-- after = [[const abc=(|"test",data,dadas) ]],
-- },
-- {
-- name = 'move end wise after quote ',
-- filepath = './tests/endwise/init.lua',
-- filetype = 'lua',
-- linenr = 5,
-- key = [[<M-e>]],
-- before = [[Plug {(|'dsfdsa',) on = 'aaa'} ]],
-- after = [[Plug {('dsfdsa', on = 'aaa')} ]],
-- },
-- }
-- local run_data = _G.Test_filter(data)
-- describe('[endwise tag]', function()
-- _G.Test_withfile(run_data, {})
-- end)

View File

@ -0,0 +1,30 @@
set rtp +=.
set rtp +=../plenary.nvim/
set rtp +=../nvim-treesitter
set rtp +=../playground/
lua _G.__is_log = true
lua vim.fn.setenv("DEBUG_PLENARY", true)
runtime! plugin/plenary.vim
runtime! plugin/nvim-treesitter.vim
runtime! plugin/playground.vim
runtime! plugin/nvim-autopairs.vim
set noswapfile
set nobackup
filetype indent off
set expandtab
set shiftwidth=4
set nowritebackup
set noautoindent
set nocindent
set nosmartindent
set indentexpr=
lua << EOF
require("plenary/busted")
require("nvim-treesitter").setup()
vim.cmd[[luafile ./tests/test_utils.lua]]
require("nvim-autopairs").setup()
EOF

View File

@ -0,0 +1,838 @@
local npairs = require('nvim-autopairs')
local Rule = require('nvim-autopairs.rule')
local cond = require('nvim-autopairs.conds')
local log = require('nvim-autopairs._log')
_G.log = log
local utils = require('nvim-autopairs.utils')
_G.npairs = npairs;
-- use only = true to test 1 case
-- stylua: ignore
local data = {
{
-- only = true,
name = "1 add normal bracket",
key = [[{]],
before = [[x| ]],
after = [[x{|} ]]
},
{
name = "2 add bracket inside bracket",
key = [[{]],
before = [[{|} ]],
after = [[{{|}} ]]
},
{
name = "3 test single quote ",
filetype = "lua",
key = "'",
before = [[data,|) ]],
after = [[data,'|') ]]
},
{
name = "4 add normal bracket",
key = [[(]],
before = [[aaaa| x ]],
after = [[aaaa(|) x ]]
},
{
name = "5 add normal quote",
key = [["]],
before = [[aa| aa]],
after = [[aa"|" aa]]
},
{
name = "6 add python quote",
filetype = "python",
key = [["]],
before = [[""| ]],
after = [["""|""" ]]
},
{
name = "7 don't repeat python quote",
filetype = "python",
key = [["]],
before = [[a"""|""" ]],
after = [[a""""|"" ]]
},
{
name = "8 add markdown quote",
filetype = "markdown",
key = [[`]],
before = [[``| ]],
after = [[```|``` ]]
},
{
name = "9 don't add single quote with previous alphabet char",
key = [[']],
before = [[aa| aa ]],
after = [[aa'| aa ]]
},
{
name = "10 don't add single quote with alphabet char",
key = [[']],
before = [[a|x ]],
after = [[a'|x ]]
},
{
name = "11 don't add single quote on end line",
key = [[<right>']],
before = [[c aa|]],
after = [[c aa'|]]
},
{
name = "12 don't add quote after alphabet char",
key = [["]],
before = [[aa |aa]],
after = [[aa "|aa]]
},
{
name = "13 don't add quote inside quote",
key = [["]],
before = [["aa | aa]],
after = [["aa "| aa]]
},
{
name = "14 add quote if not inside quote",
key = [["]],
before = [["aa " | aa]],
after = [["aa " "|" aa]]
},
{
name = "15 don't add pair after alphabet char",
key = [[(]],
before = [[aa |aa]],
after = [[aa (|aa]]
},
{
name = "16 don't add pair after dot char",
key = [[(]],
before = [[aa |.aa]],
after = [[aa (|.aa]]
},
{
name = "17 don't add bracket have open bracket in same line",
key = [[(]],
before = [[( many char |))]],
after = [[( many char (|))]]
},
{
filetype = 'vim',
name = "18 add bracket inside quote when nextchar is ignore",
key = [[{]],
before = [["|"]],
after = [["{|}"]]
},
{
filetype = '',
name = "19 add bracket inside quote when next char is ignore",
key = [[{]],
before = [[" |"]],
after = [[" {|}"]]
},
{
name = "20 move right on quote line ",
key = [["]],
before = [["|"]],
after = [[""|]]
},
{
name = "21 move right end line ",
key = [["]],
before = [[aaaa|"]],
after = [[aaaa"|]]
},
{
name = "22 move right when inside quote",
key = [["]],
before = [[("abcd|")]],
after = [[("abcd"|)]]
},
{
name = "23 move right when inside quote",
key = [["]],
before = [[foo("|")]],
after = [[foo(""|)]]
},
{
name = "24 move right square bracket",
key = [[)]],
before = [[("abcd|) ]],
after = [[("abcd)| ]]
},
{
name = "25 move right bracket",
key = [[}]],
before = [[("abcd|}} ]],
after = [[("abcd}|} ]]
},
{
-- ref: issue #331
name = "26 move right, should not move on non-end-pair char: `§|§` with (",
setup_func = function()
npairs.add_rule(Rule("§", "§"):with_move(cond.done()))
end,
key = [[(]],
before = [[§|§]],
after = [[§(|)§]]
},
{
-- ref: issue #331
name = "27 move right, should not move on non-end-pair char: `#|#` with \"",
setup_func = function()
npairs.add_rule(Rule("#", "#"):with_move(cond.done()))
end,
key = [["]],
before = [[#|#]],
after = [[#"|"#]]
},
{
-- ref: issue #331 and #330
name = "28 move right, should not move on non-end-pair char: `<|>` with (",
setup_func = function()
npairs.add_rule(Rule("<", ">"):with_move(cond.done()))
end,
key = [[(]],
before = [[<|>]],
after = [[<(|)>]],
},
{
name = "29 move right when inside grave with special slash",
key = [[`]],
before = [[(`abcd\"|`)]],
after = [[(`abcd\"`|)]]
},
{
name = "30 move right when inside quote with special slash",
key = [["]],
before = [[("abcd\"|")]],
after = [[("abcd\""|)]]
},
{
filetype = 'rust',
name = "31 move right double quote after single quote",
key = [["]],
before = [[ ('x').expect("|");]],
after = [[ ('x').expect(""|);]],
},
{
filetype = 'rust',
name = "32 move right, should not move when bracket not closing",
key = [[}]],
before = [[{{ |} ]],
after = [[{{ }|} ]]
},
{
filetype = 'rust',
name = "33 move right, should move when bracket closing",
key = [[}]],
before = [[{ }|} ]],
after = [[{ }}| ]]
},
{
name = "34 delete bracket",
filetype = "javascript",
key = [[<bs>]],
before = [[aaa(|) ]],
after = [[aaa| ]]
},
{
name = "35 breakline on {",
filetype = "javascript",
key = [[<cr>]],
before = [[a{|}]],
after = {
"a{",
"|",
"}"
}
},
{
setup_func = function()
vim.opt.indentexpr = "nvim_treesitter#indent()"
end,
name = "36 breakline on (",
filetype = "javascript",
key = [[<cr>]],
before = [[function ab(|)]],
after = {
"function ab(",
"|",
")"
}
},
{
name = "37 breakline on ]",
filetype = "javascript",
key = [[<cr>]],
before = [[a[|] ]],
after = {
"a[",
"|",
"]"
}
},
{
name = "38 move ) inside nested function call",
filetype = "javascript",
key = [[)]],
before = {
"fn(fn(|))",
},
after = {
"fn(fn()|)",
}
},
{
name = "39 move } inside singleline function's params",
filetype = "javascript",
key = [[}]],
before = {
"({|}) => {}",
},
after = {
"({}|) => {}",
}
},
{
name = "40 move } inside multiline function's params",
filetype = "javascript",
key = [[}]],
before = {
"({|}) => {",
"",
"}",
},
after = {
"({}|) => {",
"",
"}",
}
},
{
name = "41 breakline on markdown ",
filetype = "markdown",
key = [[<cr>]],
before = [[``` lua|```]],
after = {
[[``` lua]],
[[|]],
[[```]]
}
},
{
name = "42 breakline on < html",
filetype = "html",
key = [[<cr>]],
before = [[<div>|</div>]],
after = {
[[<div>]],
[[|]],
[[</div>]]
}
},
{
name = "43 breakline on < html with text",
filetype = "html",
key = [[<cr>]],
before = [[<div> ads |</div>]],
after = {
[[<div> ads]],
[[|]],
[[</div>]]
},
},
{
name = "44 breakline on < html with space after cursor",
filetype = "html",
key = [[<cr>]],
before = [[<div> ads | </div>]],
after = {
[[<div> ads]],
[[|]],
[[</div>]]
},
},
{
name = "45 do not mapping on > html",
filetype = "html",
key = [[>]],
before = [[<div| ]],
after = [[<div>| ]]
},
{
name = "46 press multiple key",
filetype = "html",
key = [[((((]],
before = [[a| ]],
after = [[a((((|)))) ]]
},
{
setup_func = function()
npairs.add_rules({
Rule('u%d%d%d%d$', 'number', 'lua'):use_regex(true),
})
end,
name = "47 text regex",
filetype = "lua",
key = "4",
before = [[u123| ]],
after = [[u1234|number ]]
},
{
setup_func = function()
npairs.add_rules({
Rule('x%d%d%d%d$', 'number', 'lua'):use_regex(true):replace_endpair(function(opts)
return opts.prev_char:sub(#opts.prev_char - 3, #opts.prev_char)
end),
})
end,
name = "48 text regex with custom end_pair",
filetype = "lua",
key = "4",
before = [[x123| ]],
after = [[x1234|1234 ]]
},
{
setup_func = function()
npairs.add_rules({
Rule('b%d%d%d%d%w$', '', 'vim'):use_regex(true, '<tab>'):replace_endpair(function(opts)
return opts.prev_char:sub(#opts.prev_char - 4, #opts.prev_char) .. '<esc>viwUi'
end),
})
end,
name = "49 text regex with custom key",
filetype = "vim",
key = "<tab>",
before = [[b1234s| ]],
after = [[B|1234S1234S ]]
},
{
setup_func = function()
npairs.add_rules({
Rule('b%d%d%d%d%w$', '', 'vim'):use_regex(true, '<tab>'):replace_endpair(function(opts)
return opts.prev_char:sub(#opts.prev_char - 4, #opts.prev_char) .. '<esc>viwUi'
end),
})
end,
name = "50 test move right custom char",
filetype = "vim",
key = "<tab>",
before = [[b1234s| ]],
after = [[B|1234S1234S ]]
},
{
setup_func = function()
npairs.add_rules({
Rule("-", "+", "vim")
:with_move(function(opt)
return utils.get_prev_char(opt) == "x"
end)
:with_move(cond.done())
})
end,
name = "51 test move right custom char plus",
filetype = "vim",
key = "+",
before = [[x|+ ]],
after = [[x+| ]]
},
{
setup_func = function()
npairs.add_rules({
Rule("/**", "**/", "javascript")
:with_move(cond.none())
})
end,
name = "52 test javascript comment",
filetype = "javascript",
key = "*",
before = [[/*| ]],
after = [[/**|**/ ]]
},
{
setup_func = function()
npairs.add_rules({
Rule("(", ")")
:use_key("<c-h>")
:replace_endpair(function() return "<bs><del>" end, true)
})
end,
name = "53 test map custom key",
filetype = "latex",
key = [[<c-h>]],
before = [[ abcde(|) ]],
after = [[ abcde| ]],
},
{
setup_func = function()
npairs.add_rules {
Rule(' ', ' '):with_pair(function(opts)
local pair = opts.line:sub(opts.col, opts.col + 1)
return vim.tbl_contains({ '()', '[]', '{}' }, pair)
end),
Rule('( ', ' )')
:with_pair(function() return false end)
:with_del(function() return false end)
:with_move(function() return true end)
:use_regex(false, ")")
}
end,
name = "54 test multiple move right",
filetype = "latex",
key = [[)]],
before = [[( | ) ]],
after = [[( )| ]],
},
{
setup_func = function()
npairs.setup({
enable_check_bracket_line = false
})
end,
name = "55 test disable check bracket line",
filetype = "latex",
key = [[(]],
before = [[(|))) ]],
after = [[((|)))) ]],
},
{
setup_func = function()
npairs.add_rules({
Rule("<", ">", { "rust" })
:with_pair(cond.before_text("Vec"))
})
end,
name = "56 test disable check bracket line",
filetype = "rust",
key = [[<]],
before = [[Vec| ]],
after = [[Vec<|> ]],
},
{
setup_func = function()
npairs.add_rule(Rule("!", "!"):with_pair(cond.not_filetypes({ "lua" })))
end,
name = "57 disable pairs in lua",
filetype = "lua",
key = "!",
before = [[x| ]],
after = [[x!| ]]
},
{
setup_func = function()
npairs.clear_rules()
npairs.add_rules({
Rule("%(.*%)%s*%=>", " { }", { "typescript", "typescriptreact", "javascript" })
:use_regex(true)
:set_end_pair_length(2)
})
end,
name = "58 mapping regex with custom end_pair_length",
filetype = "typescript",
key = ">",
before = [[(o)=| ]],
after = [[(o)=> { | } ]]
},
{
setup_func = function()
npairs.add_rules({
Rule('(', ')'):use_key('<c-w>'):replace_endpair(function()
return '<bs><del><del><del>'
end, true),
Rule('(', ')'):use_key('<c-h>'):replace_endpair(function()
return '<bs><del>'
end, true),
})
end,
name = "59 mapping same pair with different key",
filetype = "typescript",
key = "(",
before = [[(test|) ]],
after = [[(test(|)) ]]
},
{
setup_func = function()
npairs.clear_rules()
npairs.add_rule(Rule("", ""))
end,
name = "60 multibyte character from custom keyboard",
not_replace_term_code = true,
key = "",
before = [[a | ]],
after = [[a „|” ]],
end_cursor = 3
},
{
setup_func = function()
npairs.clear_rules()
npairs.add_rule(Rule("", ""):with_move(cond.done()))
end,
name = "61 multibyte character move_right",
not_replace_term_code = true,
key = "",
before = [[a „|”xx ]],
after = [[a „”|xx ]],
end_cursor = 6
},
{
setup_func = function()
npairs.clear_rules()
npairs.add_rule(Rule("", ""):with_move(cond.done()))
end,
name = "62 multibyte character delete",
key = "<bs>",
before = [[a „|” ]],
after = [[a | ]],
},
{
setup_func = function()
npairs.clear_rules()
npairs.add_rule(Rule("a„", "”b"):with_move(cond.done()))
end,
not_replace_term_code = true,
name = "63 multibyte character and multiple ",
key = "",
before = [[a| ]],
after = [[a„|”b ]],
end_cursor = 2
},
{
setup_func = function()
npairs.setup({ map_c_h = true })
end,
name = "64 map <c-h>",
key = "<c-h>",
before = [[aa'|' ]],
after = [[aa| ]],
},
{
setup_func = function()
npairs.setup({
map_c_w = true
})
end,
name = "65 map <c-w>",
key = "<c-w>",
before = [[aa'|' ]],
after = [[aa| ]],
},
{
setup_func = function()
npairs.clear_rules()
npairs.add_rule(Rule("x", "x", { '-vim', '-rust' }))
end,
filetype = 'vim',
name = "66 disable filetype vim",
key = [[x]],
before = [[a | ]],
after = [[a x| ]]
},
{
filetype = 'vim',
name = "67 undo on quote",
key = [[{123<esc>u]],
end_cursor = 12,
before = [[local abc=| ]],
after = [[local abc={|} ]]
},
{
filetype = 'vim',
name = "68 undo on bracket",
key = [['123<esc>u]],
end_cursor = 12,
before = [[local abc=| ]],
after = [[local abc='|' ]]
},
{
filetype = 'vim',
name = "69 double quote on vim after char",
key = [["ab]],
before = [[echo | ]],
after = [[echo "ab|" ]]
},
{
filetype = 'vim',
name = "70 double quote on vim on begin",
key = [["ab]],
before = [[ | aaa]],
after = [[ "ab| aaa]]
},
{
setup_func = function()
npairs.add_rule(
Rule('struct%s[a-zA-Z]+%s?{$', '};')
:use_regex(true, "{")
)
end,
filetype = 'javascript',
name = "71 custom endwise rule",
key = [[{]],
before = [[struct abc | ]],
after = [[struct abc {|};]],
},
{
setup_func = function()
npairs.clear_rules()
npairs.add_rule(Rule("{", "}"):end_wise())
end,
filetype = 'javascript',
name = "72 custom endwise rule",
key = [[<cr>]],
before = [[function () {| ]],
after = {
[[function () {]],
[[|]],
[[}]],
},
},
{
setup_func = function()
vim.opt.smartindent = true
end,
filetype = 'ps1',
name = "73 indent on powershell",
key = [[<cr>]],
before = [[function () {|} ]],
after = {
[[function () {]],
[[|]],
[[}]],
},
},
{
setup_func = function()
npairs.clear_rules()
npairs.add_rule(
Rule("{", "")
:replace_endpair(function()
return "}"
end)
:end_wise()
)
end,
filetype = 'javascript',
name = "74 custom endwise rule with custom end_pair",
key = [[<cr>]],
before = [[function () {| ]],
after = {
[[function () {]],
[[|]],
[[}]],
},
},
{
name = "75 open bracker on back tick",
key = [[(]],
before = [[ |`abcd`]],
after = [[ (`abcd`) ]]
},
{
name = "76 should not add bracket on line have bracket ",
key = [[(]],
before = [[ |(abcd))]],
after = [[ ((abcd)) ]]
},
{
name = "77 not add bracket on line have bracket ",
key = [[(]],
before = [[ |(abcd) ( visual)]],
after = [[ ()(abcd) ( visual)]]
},
{
name = "78 should add single quote when it have primes char",
key = [[']],
before = [[Ben's friends say: | ]],
after = [[Ben's friends say: '|' ]]
},
{
name = "79 a quote with single quote string",
key = "'",
before = [[{{("It doesn't name %s", ''), 'ErrorMsg'| }}, ]],
after = [[{{("It doesn't name %s", ''), 'ErrorMsg''|' }}, ]],
end_cursor = 41
},
{
name = "80 add normal quote with '",
key = [["]],
before = [[aa| 'aa]],
after = [[aa"|" 'aa]]
},
{
name = "81 add closing single quote for python prefixed string",
filetype = "python",
key = [[']],
before = [[print(f|)]],
after = [[print(f'|')]]
},
{
name = "82 add closing single quote for capital python prefixed string",
filetype = "python",
key = [[']],
before = [[print(B|)]],
after = [[print(B'|')]]
},
{
name = "83 don't add closing single quote for random prefix string",
filetype = "python",
key = [[']],
before = [[print(s|)]],
after = [[print(s'|)]]
},
{
name = "84 don't add closing single quote for other filetype prefixed string",
filetype = "lua",
key = [[']],
before = [[print(f|)]],
after = [[print(f'|)]]
},
{
name = "85 allow brackets in prefixed python single quote string",
filetype = "python",
key = [[{]],
before = [[print(f'|')]],
after = [[print(f'{|}')]]
},
{
name = "86 move ' is working on python",
filetype = "python",
key = [[']],
before = [[('|') ]],
after = [[(''|) ]]
},
{
setup_func = function()
npairs.add_rules({
Rule('123456', '789'):with_pair(cond.before_regex('^12345$', 5)),
})
end,
name = '87 test before_regex with a specific string length',
key = [[123456]],
before = [[ some text before| ]],
after = [[ some text before123456|789 ]],
},
}
local run_data = _G.Test_filter(data)
describe('autopairs ', function()
_G.Test_withfile(run_data, {
cursor_add = 0,
before_each = function(value)
npairs.setup()
vim.opt.indentexpr = ""
if value.setup_func then
value.setup_func()
end
end,
})
end)

View File

@ -0,0 +1,189 @@
local utils = require('nvim-autopairs.utils')
local log = require('nvim-autopairs._log')
local api = vim.api
local ts_get_node_text = vim.treesitter.get_node_text or vim.treesitter.query.get_node_text
local helpers = {}
function helpers.feed(text, feed_opts, is_replace)
feed_opts = feed_opts or 'n'
if not is_replace then
text = vim.api.nvim_replace_termcodes(text, true, false, true)
end
vim.api.nvim_feedkeys(text, feed_opts, true)
end
function helpers.insert(text, is_replace)
helpers.feed('i' .. text, 'x', is_replace)
end
utils.insert_char = function(text)
api.nvim_put({ text }, 'c', true, true)
end
utils.feed = function(text, num)
local result = ''
for _ = 1, num, 1 do
result = result .. text
end
api.nvim_feedkeys(
---@diagnostic disable-next-line: param-type-mismatch
api.nvim_replace_termcodes(result, true, false, true),
'x',
true
)
end
_G.eq = assert.are.same
_G.Test_filter = function(data)
local run_data = {}
for _, value in pairs(data) do
if value.only == true then
table.insert(run_data, value)
break
end
end
if #run_data == 0 then
run_data = data
end
return run_data
end
local compare_text = function(linenr, text_after, name, cursor_add, end_cursor)
cursor_add = cursor_add or 0
local new_text = vim.api.nvim_buf_get_lines(
0,
linenr - 1,
linenr + #text_after - 1,
true
)
for i = 1, #text_after, 1 do
local t = string.gsub(text_after[i], '%|', '')
if t
and new_text[i]
and t:gsub('%s+$', '') ~= new_text[i]:gsub('%s+$', '')
then
eq(t, new_text[i], '\n\n text error: ' .. name .. '\n')
end
local p_after = string.find(text_after[i], '%|')
if p_after then
local row, col = utils.get_cursor()
if end_cursor then
eq(row, linenr + i - 2, '\n\n cursor row error: ' .. name .. '\n')
eq(
col + 1,
end_cursor,
'\n\n end cursor column error : ' .. name .. '\n'
)
else
eq(row, linenr + i - 2, '\n\n cursor row error: ' .. name .. '\n')
p_after = p_after + cursor_add
eq(
col,
math.max(p_after - 2, 0),
'\n\n cursor column error : ' .. name .. '\n'
)
end
end
end
return true
end
_G.Test_withfile = function(test_data, cb)
for _, value in pairs(test_data) do
it('test ' .. value.name, function(_)
local text_before = {}
value.linenr = value.linenr or 1
local pos_before = {
linenr = value.linenr,
colnr = 0,
}
if not vim.tbl_islist(value.before) then
value.before = { value.before }
end
for index, text in pairs(value.before) do
local txt = string.gsub(tostring(text), '%|', '')
table.insert(text_before, txt)
if string.match(tostring(text), '%|') then
if string.find(tostring(text), '%|') then
pos_before.colnr = string.find(tostring(text), '%|')
pos_before.linenr = value.linenr + index - 1
end
end
end
if not vim.tbl_islist(value.after) then
value.after = { value.after }
end
vim.bo.filetype = value.filetype or 'text'
vim.cmd(':bd!')
if cb.before_each then
cb.before_each(value)
end
---@diagnostic disable-next-line: missing-parameter
if vim.fn.filereadable(vim.fn.expand(value.filepath)) == 1 then
vim.cmd(':e ' .. value.filepath)
if value.filetype then
vim.bo.filetype = value.filetype
end
vim.cmd(':e')
else
vim.cmd(':new')
if value.filetype then
vim.bo.filetype = value.filetype
end
end
local status, parser = pcall(vim.treesitter.get_parser, 0)
if status then
parser:parse(true)
end
vim.api.nvim_buf_set_lines(
0,
value.linenr - 1,
value.linenr + #text_before,
false,
text_before
)
vim.api.nvim_win_set_cursor(
0,
{ pos_before.linenr, pos_before.colnr - 1 }
)
if type(value.key) == "function" then
log.debug("call key")
value.key()
else
log.debug('insert:' .. value.key)
helpers.insert(value.key, value.not_replace_term_code)
vim.wait(2)
helpers.feed('<esc>')
end
compare_text(
value.linenr,
value.after,
value.name,
cb.cursor_add,
value.end_cursor
)
if cb.after_each then
cb.after_each(value)
end
vim.cmd(':bd!')
end)
end
end
_G.dump_node = function(node)
local text = ts_get_node_text(node)
for _, txt in pairs(text) do
print(txt)
end
end
_G.dump_node_text = function(target)
for node in target:iter_children() do
local node_type = node:type()
local text = ts_get_node_text(node)
log.debug('type:' .. node_type .. ' ')
log.debug(text)
end
end

View File

@ -0,0 +1,131 @@
local npairs = require('nvim-autopairs')
local ts = require('nvim-treesitter.configs')
local log = require('nvim-autopairs._log')
local Rule = require('nvim-autopairs.rule')
local ts_conds = require('nvim-autopairs.ts-conds')
_G.npairs = npairs
vim.api.nvim_set_keymap(
'i',
'<CR>',
'v:lua.npairs.check_break_line_char()',
{ expr = true, noremap = true }
)
ts.setup({
ensure_installed = { 'lua', 'javascript', 'rust', 'markdown', 'markdown_inline' },
highlight = { enable = true },
autopairs = { enable = true },
})
local data = {
{
name = 'treesitter lua quote',
filepath = './tests/endwise/init.lua',
filetype = 'lua',
linenr = 5,
key = [["]],
before = {
[[ [[ aaa| ]],
[[ ]],
']]',
},
after = [[ [[ aaa"| ]],
},
{
setup_func = function()
npairs.add_rules({
Rule('%', '%', 'lua'):with_pair(
ts_conds.is_ts_node({ 'string', 'comment', 'string_content' })
),
})
end,
name = 'ts_conds is_ts_node quote',
filepath = './tests/endwise/init.lua',
filetype = 'lua',
linenr = 5,
key = [[%]],
before = {
[[ [[ abcde | ]],
[[ ]],
']]',
},
after = [[ [[ abcde %|% ]],
},
{
name = 'ts_conds is_ts_node failed',
filepath = './tests/endwise/init.lua',
linenr = 5,
filetype = 'lua',
key = '%',
before = { [[local abcd| = ' visual ']] },
after = [[local abcd%| = ' visual ']],
},
{
setup_func = function()
npairs.add_rules({
Rule('<', '>', 'rust'):with_pair(ts_conds.is_ts_node({
'type_identifier',
'let_declaration',
'parameters',
})),
})
end,
name = 'ts_conds is_ts_node failed',
filepath = './tests/endwise/main.rs',
linenr = 5,
filetype = 'rust',
key = '<',
before = [[pub fn noop(_inp: Vec|) {]],
after = [[pub fn noop(_inp: Vec<|>) {]],
},
{
setup_func = function()
npairs.add_rules({
Rule('*', '*', { 'markdown', 'markdown_inline' })
:with_pair(ts_conds.is_not_in_context()),
})
end,
name = 'ts_context markdown `*` success md_context',
filepath = './tests/endwise/sample.md',
linenr = 2,
filetype = 'markdown',
key = '*',
before = [[|]],
after = [[*|*]],
},
{
setup_func = function()
npairs.add_rules({
Rule('*', '*', { 'markdown', 'markdown_inline' })
:with_pair(ts_conds.is_not_in_context()),
})
end,
name = 'ts_context codeblock `*` fail js_context',
filepath = './tests/endwise/sample.md',
linenr = 6,
filetype = 'markdown',
key = '*',
before = [[let calc = 1 |]],
after = [[let calc = 1 *|]],
},
}
local run_data = _G.Test_filter(data)
describe('[treesitter check]', function()
_G.Test_withfile(run_data, {
before_each = function(value)
npairs.setup({
check_ts = true,
ts_config = {
javascript = { 'template_string', 'comment' },
},
})
if value.setup_func then
value.setup_func()
end
end,
})
end)

View File

@ -0,0 +1,65 @@
local utils = require('nvim-autopairs.utils')
local log = require('nvim-autopairs._log')
local eq = assert.are.same
local data = {
{
text = "add normal bracket",
start = 2,
num = 2,
result = 'dd'
},
{
text = "iood",
start = 1,
num = 2,
result = 'io'
},
{
text = "add normal bracket",
start = 0,
num = -2,
result = ''
},
{
text = "add normal bracket",
start = 3,
num = -2,
result = 'dd'
},
{
text = [["""]],
start = 3,
num = -3,
result = '"""'
},
{
text = [["""]],
start = 3,
num = 3,
result = '"'
},
{
text = [["""]],
start = 2,
num = 2,
result = '""'
},
}
describe('utils test substring ', function()
for _, value in pairs(data) do
it('test sub: ' .. value.text, function()
local result = utils.text_sub_char(value.text, value.start, value.num)
eq(value.result, result, 'start ' .. value.start .. ' num' .. value.num)
end)
end
end)
vim.wait(100)