Writing useful lua functions to my neovim
Tags: neovim, nvim, lua
Published: 2021-08-15 20:36:03

---
title: Writing useful lua functions to my neovim
published: true
description: Utils to improve our daily life in neovim
tags: neovim, nvim, lua
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1gq0uo3nedv4w10hzqyf.png
---

## Intro
I used to have a very large vimrc but now, I decided to write my own init.lua, after writing almost everything essential I started to port my functions, things like the **Preserve** function, so I can delete trailing spaces without moving my cursor. 

```lua
local M = {}

M.preserve = function(arguments)
    local arguments = string.format("keepjumps keeppatterns execute %q", arguments)
    -- local original_cursor = vim.fn.winsaveview()
    local line, col = unpack(vim.api.nvim_win_get_cursor(0))
    vim.api.nvim_command(arguments)
    local lastline = vim.fn.line("$")
    -- vim.fn.winrestview(original_cursor)
    if line > lastline then
        line = lastline
    end
    vim.api.nvim_win_set_cursor({ 0 }, { line, col })
end

return M

```
After writing the above function you can do:

```lua
vim.cmd([[cnoreab cls Preserve]])
vim.cmd([[command! Preserve lua preserve('%s/\\s\\+$//ge')]])
vim.cmd([[command! Reindent lua preserve("sil keepj normal! gg=G")]])
vim.cmd([[command! BufOnly lua preserve("silent! %bd|e#|bd#")]])
```

## Change file header helper
What about updating your file header every time you save, I mean, type: 
`:update<cr>`?

```lua

--> :lua changeheader()
-- This function is called with the BufWritePre event (autocmd)
-- and when I want to save a file I use ":update" which
-- only writes a buffer if it was modified
M.changeheader = function()
    -- We only can run this function if the file is modifiable
    if not vim.api.nvim_buf_get_option(vim.api.nvim_get_current_buf(), "modifiable") then
        return
    end
    if vim.fn.line("$") >= 7 then
        os.setlocale("en_US.UTF-8") -- show Sun instead of dom (portuguese)
        time = os.date("%a, %d %b %Y %H:%M")
        M.preserve("sil! keepp keepj 1,7s/\\vlast (modified|change):\\zs.*/ " .. time .. "/ei")
    end
end
```
Next section you can see how the function above is triggered/associated with the `BufWritePre` event, changing automatically your file header where you can read: "Last Change:". 

## For you autocommands you can do:

```lua
-- autocommands
--- This function is taken from https://github.com/norcalli/nvim_utils
function nvim_create_augroups(definitions)
    for group_name, definition in pairs(definitions) do
        api.nvim_command('augroup '..group_name)
        api.nvim_command('autocmd!')
        for _, def in ipairs(definition) do
            local command = table.concat(vim.tbl_flatten{'autocmd', def}, ' ')
            api.nvim_command(command)
        end
        api.nvim_command('augroup END')
    end
end

local autocmds = {
    reload_vimrc = {
        -- Reload vim config automatically
        {"BufWritePost",[[$VIM_PATH/{*.vim,*.yaml,vimrc} nested source $MYVIMRC | redraw]]};
    };
    change_header = {
        {"BufWritePre", "*", "lua changeheader()"}
    };
    packer = {
        { "BufWritePost", "plugins.lua", "PackerCompile" };
    };
    terminal_job = {
        { "TermOpen", "*", [[tnoremap <buffer> <Esc> <c-\><c-n>]] };
        { "TermOpen", "*", "startinsert" };
        { "TermOpen", "*", "setlocal listchars= nonumber norelativenumber" };
    };
    restore_cursor = {
        { 'BufRead', '*', [[call setpos(".", getpos("'\""))]] };
    };
    save_shada = {
        {"VimLeave", "*", "wshada!"};
    };
    resize_windows_proportionally = {
        { "VimResized", "*", ":wincmd =" };
    };
    toggle_search_highlighting = {
        { "InsertEnter", "*", "setlocal nohlsearch" };
    };
    lua_highlight = {
        { "TextYankPost", "*", [[silent! lua vim.highlight.on_yank() {higroup="IncSearch", timeout=400}]] };
    };
    ansi_esc_log = {
        { "BufEnter", "*.log", ":AnsiEsc" };
    };
}

nvim_create_augroups(autocmds)
-- autocommands END
```
## Squeeze blank lines

```lua
M.squeeze_blank_lines = function()
-- references: https://vi.stackexchange.com/posts/26304/revisions
	if vim.bo.binary == false and vim.opt.filetype:get() ~= "diff" then
		local old_query = vim.fn.getreg("/") -- save search register
		M.preserve("sil! 1,.s/^\\n\\{2,}/\\r/gn") -- set current search count number
		local result = vim.fn.searchcount({ maxcount = 1000, timeout = 500 }).current
		local line, col = unpack(vim.api.nvim_win_get_cursor(0))
		M.preserve("sil! keepp keepj %s/^\\n\\{2,}/\\r/ge")
		M.preserve("sil! keepp keepj %s/\\v($\\n\\s*)+%$/\\r/e")
		if result > 0 then
			vim.api.nvim_win_set_cursor({ 0 }, { (line - result), col })
		end
		vim.fn.setreg("/", old_query) -- restore search register
	end
end
```

## map helper

```lua
-- map helper
local function map(mode, lhs, rhs, opts)
    local options = {noremap = true}
    if opts then options = vim.tbl_extend('force', options, opts) end
    vim.api.nvim_set_keymap(mode, lhs, rhs, options)
end

map('n', '<leader>d', ':lua delblanklines()<cr>')
```

## This article can change/grow a little bit

## References: 
The main source I have used was [this post](https://neovim.discourse.group/t/fixing-a-function-that-receives-commands-as-arguments/993/6) on neovim.discourse.group