luaneovim

How to programmatically create Autocmds with Lua in Neovim


I'm transitioning to configuring Neovim with Lua. I have years of programming experience with but don't know Lua nor vimscript.

Concretely I want to organize a bunch of hotkey bindings where each key modified by either Ctrl or Alt will have its effect.

Ideally I'd specify a list of hotkey-command pairs and iterate over it to programmatically define these repetitive autocmds, but perhaps there's a better way to do this I don't see because of my unfamiliarity.

The example below shows that <A-j> and <C-j> execute :CoqNext in either normal or insert mode, and that <A-k> and <C-k> likewise execute :CoqUndo.

local coqbindings = vim.api.nvim_create_augroup("coqbindings", { clear = true })
vim.api.nvim_create_autocmd({ "Filetype" }, {
  pattern = "coq",
  group = coqbindings,
  command = "inoremap <A-j> <Esc>:CoqNext<CR>",
})
vim.api.nvim_create_autocmd({ "Filetype" }, {
  pattern = "coq",
  group = coqbindings,
  command = "nnoremap <A-j> :CoqNext<CR>",
})

vim.api.nvim_create_autocmd({ "Filetype" }, {
  pattern = "coq",
  group = coqbindings,
  command = "inoremap <A-k> <Esc>:CoqUndo<CR>",
})
vim.api.nvim_create_autocmd({ "Filetype" }, {
  pattern = "coq",
  group = coqbindings,
  command = "nnoremap <A-k> :CoqUndo<CR>",
})

vim.api.nvim_create_autocmd({ "Filetype" }, {
  pattern = "coq",
  group = coqbindings,
  command = "inoremap <C-j> <Esc>:CoqNext<CR>",
})
vim.api.nvim_create_autocmd({ "Filetype" }, {
  pattern = "coq",
  group = coqbindings,
  command = "nnoremap <C-j> :CoqNext<CR>",
})

vim.api.nvim_create_autocmd({ "Filetype" }, {
  pattern = "coq",
  group = coqbindings,
  command = "inoremap <C-k> <Esc>:CoqUndo<CR>",
})
vim.api.nvim_create_autocmd({ "Filetype" }, {
  pattern = "coq",
  group = coqbindings,
  command = "nnoremap <C-k> :CoqUndo<CR>",
})

Solution

  • Loop over a table

    A lua table with string keys and values looks like the following, see the lua reference for details.

    local coqbindingstable = {
      j = "CoqNext",
      k = "CoqUndo",
      l = "CoqToLine",
      L = "CoqOmitToLine",
      q = "CoqStop",
      G = "CoqJumpToEnd",
    }
    

    Lua doesn't have lists, so for the modifiers (Ctrl and Alt) you create a table where the keys are implicitly the 1-based indices: {"A","C"}.

    The for-loop syntax over pairs is for key, value in pairs(table) do ... end, you can ignore the index or value by using an underscore in place of the variable name.

    The string concatenation operator is ...

    The full code results in

    local coqbindingstable = {
      j = "CoqNext",
      k = "CoqUndo",
      l = "CoqToLine",
      L = "CoqOmitToLine",
      q = "CoqStop",
      G = "CoqJumpToEnd",
    }
    
    for key, command in pairs(coqbindingstable) do
      for _, modifier in pairs({ "A", "C" }) do
        vim.api.nvim_create_autocmd({ "Filetype" }, {
          pattern = "coq",
          group = coqbindings,
          command = "inoremap <" .. modifier .. "-" .. key .. "> <Esc>:" .. command .. "<CR>",
        })
        vim.api.nvim_create_autocmd({ "Filetype" }, {
          pattern = "coq",
          group = coqbindings,
          command = "nnoremap <" .. modifier .. "-" .. key .. "> :" .. command .. "<CR>",
        })
      end
    end