windwp / nvim-autopairs

autopairs for neovim written in lua

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Creating a custom tree sitter rule for the angle bracket pair (`<>`) that also escapes does not work

j-barnak opened this issue · comments

Description

I am trying to add a treesitter rule that creates the <> pair

The only time I don't want <> to pair is when it's used as a binary expression. By
that, I mean for the bitshift operator (<<) and the less than operator (<). Here's what I mean

if (x < y) // Do not pair the `<`
    std::cout << "foo"; // Do not pair the <<

The bitshift operator and less than operator are both binary expressions so I thought
I could encapsulate the pairing with the following

local npairs = require("nvim-autopairs")
local Rule = require("nvim-autopairs.rule")
local ts_conds = require("nvim-autopairs.ts-conds")

npairs.setup(opts)

npairs.add_rules({
  Rule("<", ">", "cpp"):with_pair(ts_conds.is_not_ts_node({ "binary_expression" })),
})

but this doesn't work.

I've also tried to manually add all the instances of where the bracket is used as a pair with

npairs.add_rules({
  Rule("<", ">", "cpp"):with_pair(
    ts_conds.is_ts_node({ "preproc_include", "template_declaration", "template_argument_list" })
  ),
})

This works for preproc_include, but not template_declaration. The AST for templates look like

(template_declaration) ; [3:1 - 55]
 parameters: (template_parameter_list) ; [3:10 - 21]
  (type_parameter_declaration) ; [3:11 - 20]
   (type_identifier) ; [3:20 - 20]

And this is what it looks like for templated class declarations

value: (compound_literal_expression) ; [6:23 - 40]
 type: (qualified_identifier) ; [6:23 - 38]
  scope: (namespace_identifier) ; [6:23 - 25]
  name: (template_type) ; [6:28 - 38]
   name: (type_identifier) ; [6:28 - 33]
   arguments: (template_argument_list) ; [6:34 - 38]
    (type_descriptor) ; [6:35 - 37]
     type: (primitive_type) ; [6:35 - 37]

How could I get this rule to work?

Mapping bug

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>.

Steps to reproduce

No response

Minimal config

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()]]

local npairs = require("nvim-autopairs")
local Rule = require("nvim-autopairs.rule")
local ts_conds = require("nvim-autopairs.ts-conds")

npairs.setup(opts)

npairs.add_rules({
  Rule("<", ">", "cpp"):with_pair(ts_conds.is_not_ts_node({ "binary_expression" })),
})
commented
-- add <> pairs if not in a statement
npairs.add_rules({
   rule("<", ">", "cpp"):with_pair(ts_conds.is_not_ts_node({ "compound_statement"})),
})

sadly this doesn't work in cases like this

 std::vector<int> a = std::vector<int>{1,2,3};
                      compound_statement-----

the second vector won't get pairs
but that cases is rare and am not sure we can do better with treesitter

u can check it out yourself if u want

local ts_utils = require('nvim-treesitter.ts_utils')
vim.keymap.set("i", "<C-q>", function()
   print(ts_utils.get_node_at_cursor())
end)
commented

hmm this actually seems like a good idea for cmp completion

@Sam-programs

It seems that the suggested doesn't work for for

std::cout << "foo"; 

Attempting the above will generate

std::cout  <> "foo";

Additionally, I've been trying to write a rule to pairs { and }; for classes and structs. And I've tried

npairs.add_rules({
  rule("{", "};", "cpp"):with_pair(
    ts_conds.is_ts_node({ "class_specifier", "type_identifier", "field_declaration_list" })
  ),
})

And a combination of those 3 nodes and no luck?

commented

@j-barnak
for the second issue i use this

local cpp_cond = function ()
   local line = vim.api.nvim_get_current_line()
   -- match for() while() functions()
   -- the regex won't work if 'magic' is not set
 
   local pattern = '^.*(.*)'

   if vim.fn.match({ line }, pattern) == 0 then
      return false
   end
end

npairs.add_rules({
   rule("{", "};", "cpp"):with_pair(
   cpp_cond
   ),
})

i am trying to figure out a way for the first one we might have to restore to checking if template is on the line rather than using treesitter

@Sam-programs Thanks!

i am trying to figure out a way for the first one we might have to restore to checking if template is on the line rather than using treesitter

I've been scratching my head over this problem for the past few days and I couldn't come up with anything.

Is it possible to just escape the pairs and not complete the pair. For example, with mini.pairs, the following does just that.

require("mini.pairs").map(
  "i",
  "<",
  { action = "open", pair = "<>", neigh_pattern = "\r.", register = { cr = false } }
)

require("mini.pairs").map("i", ">", { action = "close", pair = "<>", register = { cr = false } })
commented

i don't really understand what the mini example is doing
and i almost got a working one

local pair_cond = function ()
   local line = vim.api.nvim_get_current_line()
   -- only variable inits and structs/classes need semicolons
   local pattern = '^\\s*template'
   local c_node = ts_utils.get_node_at_cursor():type()
   if vim.fn.match({ line }, pattern) ~= 0 and
      (c_node ~= "initializer_list") 
      then
      return false
   end
end

it is just missing global templated variables
actually it still doesn't work with cout nvm

commented

i just tested the mini example it's only putting the pairs at the start of the line
if that's what u want then this should do

local pair_cond = function()
   return vim.api.nvim_win_get_cursor(0)[2] == 0
end

npairs.add_rules({
   rule("<", ">", "cpp"):with_pair(
      pair_cond
   ),
})

@Sam-programs

I was under the impression that the mini.pairs snippet just allowed you to escape <> pair by pressing >.

actually it still doesn't work with cout nvm

I also ran into bumps when dealing with the less than operator.

commented

@j-barnak
i think i found something that could be helpful
cmp returns this as the label for vector
vector<typename Tp, typename Alloc>
which has a > at the end
WE COULD CHECK FOR THAT TO ADD THE PAIR!
and for the case of writing a template we could check for a template match

commented
local cmp = require('cmp')
local function template_on_confirm(event)
   if vim.o.filetype ~= "cpp" then
      return
   end
   local entry = event.entry
   local item = entry:get_completion_item()
   local pairs = '<>'
   local functionsig = item.label
   if functionsig:sub(#functionsig, #functionsig) == '>' or functionsig == ' template' then
      pairs = vim.api.nvim_replace_termcodes(pairs .. "<left>", true, false, true)
      vim.api.nvim_feedkeys(pairs, "n", false)
   end
end

cmp.event:on('confirm_done', template_on_confirm)

best part is you don't even have to write the pairs anymore

commented

@j-barnak sorry if the mentions are annoying

@Sam-programs No worries about the mentions. I appreciate them.

Awesome work. Do you know if it's possible to add a with_move to escape the <> pair?

I'm currently using mini.pairs, but only using

require("mini.pairs").map("i", ">", { action = "close", pair = "<>", register = { cr = false } })

to escape the <> pairing.

I'm doing this for the preproc_include rule

local rule = require("nvim-autopairs.rule")
local cond = require("nvim-autopairs.conds")
local npairs = require("nvim-autopairs")

npairs.add_rules({
  rule("<", ">", "cpp")
    :with_pair(ts_conds.is_ts_node({ "preproc_include" }))
    :with_move(cond.not_before_regex(">", 1)),
})

And one last small thing, when you select the snipped for for #include, it creates (the | represents the cursor)

`#include<|>`

Is it possible to do generate

`#include <|> // Note the space

Thanks for everything. Huge help!

commented

the #include was an accidental side effect lol

commented

@j-barnak
for escaping you can add this rule

   rule("<", ">", "cpp"):
       with_pair(conds.none()): -- never add the end pair
       with_move(conds.done()), -- move when endpair is pressed

and here is a modified version of template_on_confirm
that adds a space for include

local kind = cmp.lsp.CompletionItemKind
local function template_on_confirm(event)
   if not (vim.o.filetype == "c" or vim.o.filetype == "cpp") then
      return
   end
   local entry = event.entry
   local item = entry:get_completion_item()
   local _, c = unpack(vim.api.nvim_win_get_cursor(0))
   local line = vim.api.nvim_get_current_line()
   local pairs = ''
   local functionsig = item.label
   local is_function = item.kind == kind.Function
   if line:sub(c, c) ~= '>' and
      (vim.fn.match(functionsig,'<.*>') ~= -1 or
      functionsig == ' template')
   then
      if functionsig:sub(2, 8) == 'include' then
         pairs = ' '
      end
      pairs = pairs .. '<>'
      local old_lz = vim.o.lz
      vim.o.lz = true
      pairs = vim.api.nvim_replace_termcodes(
         (is_function and "<C-g>u<left>" or "") .. pairs .. "<C-g>u<left>" .. "<cmd>lua vim.o.lz =" .. (old_lz and "true" or "false") .. "<cr>", true, false, true)
      vim.api.nvim_feedkeys(pairs, "n", false)
   end
end

also you can do
```LANG
```
to highlight syntax

i think i should make a pr with something like this it seems really useful

commented

hold your horses the first check is failing
update:
was an off by 1 error
fun fact i didn't know that escaping meant pressing the endpair moves the cursor
because i have 1 hotkey that moves the cursor 1 column
alt-l <A-l>
since most pair keys are far from letters i think this is a better idea

@Sam-programs Awesome stuff! I'll test it in the morning.

And just to clarify, we include with_pair(conds.none()) for the following snippet

rule("<", ">", "cpp"):
    with_pair(conds.none())-- never add the end pair
    with_move(conds.done()), -- move when endpair is pressed

because template_on_confirm is used to complete the pairing.

And with_move(conds.done()) enables escaping the <> pair by typing >?

commented

yes

commented

great...

   12 void foo(){
   13    std::vector<int> a;
   14    std::vector<int> b;
   15    std::vector
   16 }        vector<typename Tp, typename Alloc> Class
~              vector(…)                           Constructor
//               NOT A >

i think there could be a work around with cmp's sorting i haven't used it before tho

commented

yea this

   sorting = {
      comparators = {function (entry1,entry2)
         local item1 = entry1.completion_item
         local item2 = entry2.completion_item
         if item1.kind == item2.kind then
            return nil
         end
         if math.max(item1.kind,item2.kind) == item1.kind then 
            return true 
         end 
         return false 
      end}
   },

not sure of a nice way to make it fit into autopairs
and i don't suggest using this for now because it might make cmp's results ugly
i am gonna try to find a good range/way to sort them (or yoink cmp's)

commented
local config = require('cmp.config')
local cmp_comparetors = config.get().sorting.comparators

local unpack = unpack or table.unpack
local function cpp_sort_cmp(entry1, entry2)
   local kind1 = entry1.completion_item.kind
   local kind2 = entry2.completion_item.kind
   if vim.o.filetype ~= "cpp" then
      return nil
   end
   if kind1 == kind.Constructor and kind2 == kind.Class then
      return false
   end
   if kind1 == kind.Class and kind2 == kind.Constructor then
      return true
   end
   return nil
end

cmp.setup({
   sorting = {
      comparators = {
         cpp_sort_cmp,
         unpack(cmp_comparetors),
      }
   }
})

this seems to work best
sadly i couldn't check if cpp_sort_cmp was already in the comparator list
i hope windwp doesn't think of this as too shady

commented

after learning how to interact with lsp
i found that this actually gives the template signature when under a templated class

vim.keymap.set("i", "<C-q>", function()
   vim.lsp.handlers["textDocument/signatureHelp"] = function (_,info)
      print(info.signatures[1].label)
   end
   vim.lsp.buf.signature_help()
end)

which would make it possible for the <> pairs to work on their own
but i think it's way too overkill tho
not gonna stop me for making it in case anyone is curious

-- only works for templated variables those are the hard part
local is_tempalate = function()
   local line = vim.api.nvim_get_current_line()
   line = line .. '<'
   vim.api.nvim_set_current_line(line) 
   local r,c = unpack(vim.api.nvim_win_get_cursor(0))
   vim.api.nvim_win_set_cursor(0,{r,c + 1}) 

   vim.cmd("redraw") -- redraw to add the first < 
   local old_handler = vim.lsp.handlers["textDocument/signatureHelp"]
   vim.lsp.handlers["textDocument/signatureHelp"] = function(_, info)
      if info then
         if info.signatures[1] then
            local functionsig = info.signatures[1].label
            if vim.fn.match({functionsig},'^\\w\\+<') == 0 then
               vim.api.nvim_set_current_line(line .. '>') 
            end
         end
      end
   end
   vim.lsp.buf.signature_help()
   vim.lsp.handlers["textDocument/signatureHelp"] = old_handler
end

vim.keymap.set("i","<",is_tempalate)
commented

i need to learn to shut up
this is the most relevant comment
#405 (comment)

@Sam-programs

Thanks for everything. I'll test this to see if it works well. I'll also test #407 as well!

Thanks

which would make it possible for the <> pairs to work on their own
but i think it's way too overkill tho
not gonna stop me for making it in case anyone is curious

That'd be awesome if you got this to work!

sadly i couldn't check if cpp_sort_cmp was already in the comparator list

Maybe adjust the priority so that it's at the top?

Just having the following:

local conds = require("nvim-autopairs.conds")
local rule = require("nvim-autopairs.rule")
rule("<", ">", "cpp"):with_pair(conds.none()):with_move(conds.done()) 

doesn't seem to work?

@Sam-programs A small correction. It seems that unpack was deprecated in lua 5.4. Changing it to table.unpack fixes it.

Edit: Seems like you address this in the PR. My bad!

commented

i almost got the function working
just losing my sanity with lua 1 indexing rn

commented

#405 (comment)
i am not sure what isn't working it shouldn't place a pair in that case
i haven't done too much testing but this huge snippet should fix everything

         local is_tempalate = function()
            local line = vim.api.nvim_get_current_line()
            local r, c = unpack(vim.api.nvim_win_get_cursor(0))
            if not (vim.o.filetype == "cpp" or vim.o.filetype == "c") then
               line = line:sub(1, c) .. '<' .. line:sub(c + 1)
               vim.api.nvim_set_current_line(line)
               vim.api.nvim_win_set_cursor(0, { r, c + 1 })
               return
            end

            if vim.fn.match({ line }, 'template') == 0 then
               -- c - 1 = 2 chars before the cursor
               line = line:sub(1, c) .. '<>' .. line:sub(c + 1)
               vim.api.nvim_set_current_line(line)
               vim.api.nvim_win_set_cursor(0, { r, c + 1 })
               return
            end

            if vim.fn.match({ line }, '#include') == 0 then
               line = line:sub(1, c) .. '<>' .. line:sub(c + 1)
               if line:sub(c, c) ~= ' ' then
                  line = line:sub(1, c) .. ' ' .. line:sub(c + 1)
                  c = c + 1
               end
               vim.api.nvim_set_current_line(line)
               vim.api.nvim_win_set_cursor(0, { r, c + 1 })
               return
            end
            if vim.fn.match({ line:sub(0, c) }, 'cast\\s*$') == 0 then
               -- c - 1 = 2 chars before the cursor
               line = line:sub(1, c) .. '<>' .. line:sub(c + 1)
               vim.api.nvim_set_current_line(line)
               vim.api.nvim_win_set_cursor(0, { r, c + 1 })
               return
            end

            -- c is 1 before the cursor
            line = line:sub(1, c) .. '<' .. line:sub(c + 1)
            vim.api.nvim_set_current_line(line)
            vim.api.nvim_win_set_cursor(0, { r, c + 1 })
            vim.cmd("redraw") -- redraw to add the first <
            -- since we added < the lsp can detect it
            local old_handler = vim.lsp.handlers["textDocument/signatureHelp"]
            vim.lsp.handlers["textDocument/signatureHelp"] = function(_, info)
               if info and info.signatures and info.signatures[1] and info.signatures[1].label then
                  local functionsig = info.signatures[1].label
                  if vim.fn.match({ functionsig }, '^\\w\\+<') == 0 then
                     -- c + 1 is after including the openning pair very shady code lol
                     vim.api.nvim_set_current_line(line:sub(0, c + 1) .. '>' .. line:sub(c + 2))
                  end
               end
            end
            vim.lsp.buf.signature_help()
            vim.lsp.handlers["textDocument/signatureHelp"] = old_handler
         end
         vim.keymap.set("i", "<", is_tempalate)
commented

sadly i couldn't check if cpp_sort_cmp was already in the comparator list

that's also fixed in the pr with a global variable
also don't worry about unpack

commented

DAMN IT COUT BROKE AGAIN

commented

changing the info if statement to check everything fixes it

if info and info.signatures and info.signatures[1] and info.signatures[1].label then

Replacing

local r, c = unpack(vim.api.nvim_win_get_cursor(0))

with

@Sam-programs

local r, c = table.unpack(vim.api.nvim_win_get_cursor(0))

yields the following error

E5108: Error executing lua: /home/jared/.config/nvim/lua/plugins/pair.lua:19: attempt to call upvalue 'unpack' (a nil value)
stack traceback:
        /home/jared/.config/nvim/lua/plugins/pair.lua:19: in function </home/jared/.config/nvim/lua/plugins/pair.lua:17>

I opted to switch unpack with table.unpack because I got an error say that unpack was deprecated.

@Sam-programs

And just noting,

npairs.add_rules({
   rule("<",">"):
   with_pair(cond.none()):
   with_move(cond.done()):
   use_key('<kPoint>') -- a random key so it doesn't map < 
})

The above doesn't allow you to escape the end pair >.

commented

oh i didn't notice the that

commented

i have no clue why but this works

npairs.add_rules({
   rule("<",">"):
   with_pair(cond.none()):
   with_move(cond.done()):
   use_key('>') -- a random key so it doesn't map < 
})
commented

@j-barnak
use this if u really care about that warning

local unpack = unpack or table.unpack

@Sam-programs

With regards to this

npairs.add_rules({
   rule("<",">"):
   with_pair(cond.none()):
   with_move(cond.done()):
   use_key('>') -- a random key so it doesn't map < 
})

I think this works because the move is triggered when the pair is completed and use_key triggers the completion. I'll let you know if this runs into errors.

commented

i thought it meant pair start trigger
because that key is used in the completion to add the () pair
https://github.com/windwp/nvim-autopairs/blob/master/lua/nvim-autopairs/completion/handlers.lua#L52
also the completion starting has nothing to do with npairs unless you mean end pair completion then you are correct

@Sam-programs

I've done some testing with this snippet and it's been working great. Thanks for the awesome work!

Anything you want to add or should I close the issue? Is there anything you want to add or should I wait until #407 gets merged?

commented

@j-barnak good to know that snippet works ,i am curious to why u prefer it over the cmp snippet #405 (comment)
because it feels like it's worse than cmp's

@Sam-programs

i am curious to why u prefer it over the cmp snippet

I was under the impression that this was better

Edit: it works. Though, is there any reason you specifically prefer this one?

And personally

local npairs = require("nvim-autopairs")
local cmp = require("cmp")

npairs.setup(opts)

local function template_on_confirm(event)
  if not (vim.o.filetype == "c" or vim.o.filetype == "cpp") then
    return
  end
  local entry = event.entry
  local item = entry:get_completion_item()
  local _, c = unpack(vim.api.nvim_win_get_cursor(0))
  local line = vim.api.nvim_get_current_line()
  local pairs = ""
  local functionsig = item.label
  if
    line:sub(c, c) ~= ">"
    and (functionsig:sub(#functionsig, #functionsig) == ">" or functionsig == " template")
  then
    if functionsig:sub(2, 8) == "include" then
      pairs = " "
    end
    pairs = pairs .. "<>"
    pairs = vim.api.nvim_replace_termcodes(pairs .. "<left>", true, false, true)
    vim.api.nvim_feedkeys(pairs, "n", false)
  end
end

cmp.event:on("confirm_done", template_on_confirm)

This didn't work for me.

@Sam-programs

because i have 1 hotkey that moves the cursor 1 column
alt-l <A-l>

Mind sharing this hotkey?

commented

@j-barnak #405 (comment)

This didn't work for me

phew the pr is safe to merge
maybe you were messing the sorter here #405 (comment)
without the sorter it sometimes doesn't add the <> pairs
add the last cmp.setup after your own cmp.setup
or you can just wait until the pr gets merged

Mind sharing this hotkey?

damn you learnt vim properly and didn't use the arrow keys unlike me lol

vim.keymap.set({ "i", "c" }, "<A-l>", "<right>") -- the c mode is command mode

edit:

Edit: it works. Though, is there any reason you specifically prefer this one?

not having to press <

@Sam-programs Thanks! You might also want to add to your config

      npairs.add_rules({
        rule("{", "}", "cpp"):with_pair(ts_conds.is_ts_node({ "namespace_definition" })),
      })

because

local cpp_cond = function ()
   local line = vim.api.nvim_get_current_line()
   -- match for() while() functions()
   -- the regex won't work if 'magic' is not set
 
   local pattern = '^.*(.*)'

   if vim.fn.match({ line }, pattern) == 0 then
      return false
   end
end

npairs.add_rules({
   rule("{", "};", "cpp"):with_pair(
   cpp_cond
   ),
})

triggers{}; when you type namespace.

commented

thanks @j-barnak
i actually thought it was required there

i also noticed that the cmp autocomplete doesn't detect static_cast and other kinds of casts
i fixed it by using a regex to detect templated stuff
the thing is i am having a hard time making it not conflict with the existing pair adder for functions
i got a work around by making it reverse the cursor position for now
#405 (comment)

u will need to add the template_on_confirm to cmp's events after autopairs for functions

         cmp.event:on("confirm_done",cmp_autopairs.on_confirm_done())
         cmp.event:on('confirm_done', template_on_confirm)

but now template_on_confirm sadly doesn't work on it's own

@Sam-programs Thanks!

How would you make casts work with this rule?

Thank you so much! You've been awesome.

commented

i am really not sure how to make it work as i won't have access to cmp's entry
but maybe i could find a lsp method to detect that
i made it look if the last few characters are cast to detect casts for now
updated

that might conflict with a variable ending with cast but i don't have that much time to read/test the lsp requests for now

edit:
i don't really write much c++ so please tell me if there are cases that aren't covered ill fix them when i can

commented

@j-barnak mention lol

@Sam-programs That works! My one and last problem is that I when I mix these two

local cpp_cond = function ()
   local line = vim.api.nvim_get_current_line()
   -- match for() while() functions()
   -- the regex won't work if 'magic' is not set
 
   local pattern = '^.*(.*)'

   if vim.fn.match({ line }, pattern) == 0 then
      return false
   end
end

npairs.add_rules({
   rule("{", "};", "cpp"):with_pair(
   cpp_cond
   ),
})

pairs.add_rules({
  rule("{", "}", "cpp"):with_pair(ts_conds.is_ts_node({ "namespace_identifier" })),
 })

The cpp_cond rule gets overwritten and no longer works.

@Sam-programs I also tried this

local npairs = require("nvim-autopairs")
local rule = require("nvim-autopairs.rule")
local ts_conds = require("nvim-autopairs.ts-conds")

npairs.add_rules({
  rule("<", ">"):with_pair(cond.none()):with_move(cond.done()):use_key(">"),
  rule("{", "};", { "cpp", "c" }):with_pair(cpp_cond),
  rule("{", "}", "cpp"):with_pair(ts_conds.is_ts_node({ "namespace_identifier" })),
})

And it did not work.

commented

don't use ts_conds it's useless most of the time
it checks the node at the cursor which in insert mode is almost always pointing at the wrong thing

translation_unit node
namespace|

i matched the line with namespace which should do it

local cpp_cond = function()
   local line = vim.api.nvim_get_current_line()
   -- match for() while() functions()

   local pattern = '\\m^.*(.*)'

   if vim.fn.match(line, pattern) ~= -1 or
      vim.fn.match(line,"namespace") ~= -1
   then
      return false
   end
end

local npairs = require("nvim-autopairs")
npairs.setup{}
local rule = require("nvim-autopairs.rule")
npairs.add_rules({
   rule("{", "};", { "cpp", "c" }):with_pair(cpp_cond),
})

you could also use my expand plugin to make expanding pairs for all statement with 1 hotkey
expand

Edit: Ignore, I fixed this

@Sam-programs Thanks! I really appreciate it. I'm trying to tweak the above line so that it only returns {}; when the current or above line contains struct or class. I think it makes a lot more sense considering that's really the only time I want those pairs. I changed cpp_cond to struct_or_class

I want the default { pair to be {}, but with

rule("{", "};", { "cpp", "c" }):with_pair(struct_or_class),

It seems to default to {}; even when struct_or_class doesn't return true.

local function get_relative_line(offset)
  local line = vim.api.nvim_win_get_cursor(0)[1]
  local target = line + offset
  return vim.api.nvim_buf_get_lines(0, target - 1, target, false)[1]
end

local struct_or_class = function()
  local line = get_relative_line(0)
  local previous_line = get_relative_line(-1)

  if vim.fn.match(line, "struct") ~= -1 or vim.fn.match(line, "class") ~= -1 then
    return true
  end

  if vim.fn.match(previous_line, "struct") ~= -1 or vim.fn.match(previous_line, "class") ~= -1 then
    return true
  end

  return false -- fixed
end

local npairs = require("nvim-autopairs")
local  rule = require("nvim-autopairs.rule")
local cond = require("nvim-autopairs.conds")

npairs.setup(opts)

npairs.add_rules({
  rule("{", "};", { "cpp", "c" }):with_pair(struct_or_class),
})

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.