Josh Brunton
Josh Brunton

Reputation: 527

A neovim lua function to select around an allman-style block

I'm trying to create a neovim function which selects around {}, plus relevant context on the line above if allman-style.

For what I've tried: In my mind, I have this series of steps

  1. Find the opening { by simulating F{ in normal mode
  2. Jump to the matching } by simulating %
  3. Begin a visual selection by simulating pressing v
  4. Jump back to the opening { by simulating % again
  5. If the topmost line is the first line of the file, return
  6. If the resulting selection spans only 1 line, return
  7. If the topmost line does not match the regex ^\s*{\s*$, return
  8. Check the content of the line above the topmost line
  9. IF the line above the topmost line contains a semicolon, select up to it by simulating T;
  10. ELSE, select the entire line by simulating k_

My problem seems to arise when I try to get the number and content of lines. No matter where in the sequence I do this, it seems that the current line will always be regarded as the line on which I started, not the line where the cursor is (or rather, should be). In other words, calling vim.cmd("normal! F{") and then asking for vim.api.nvim_get_current_line() will not neccesarily give me the line on which "{" appears, but rather the line on which I started at the beginning of invoking the function.

In case it matters, I'm invoking the function with the keybind <leader>{, declared like so:

vim.keymap.set("n", "<leader>{", select_allman, { noremap = true, silent = true })

An early implementation of the code, stopping where I got stuck at querying line content:

local function select_allman()

    vim.cmd("normal! F{%v%")

    local start_line = vim.fn.line("'<")
    local end_line = vim.fn.line("'>")

    if start_line == end_line then
        -- we always return here because start_line and end_line are the line where we started invoking the function
        return
    end

    local line_content = vim.fn.getline(start_line)
    
    if not string.match(line_content, "^%s*{%s*$") then
        -- we always return here since line_content is where we started, not the line above {
        return
    end

    -- here we'd select another line above or back to after ; etc
end

Is there something I'm missing while trying to get the current line numbers and/or content, or perhaps is my entire idea for how to implement this off-base?

Upvotes: 1

Views: 19

Answers (0)

Related Questions