~detegr/nvim-bqn

This thread contains a patchset. You're looking at the original emails, but you may wish to use the patch review UI. Review patch
3 2

[PATCH] Use virtual lines to show output

Details
Message ID
<20211203000624.300133-1-8mayday@gmail.com>
DKIM signature
missing
Download raw message
Patch: +32 -38
Neovim 0.6.0 was recently released and has added a new feature called
virtual lines. This allows to attach a set of "out-of-buffer" lines to
some locations via extmark mechanism.

This patch changes how output is shown - instead of making a separate
split buffer it shows computed value as a set of virtual lines attached
to the "end" of the computed region.
---
This adds some comments describing why something being done.

 lua/bqn.lua | 70 ++++++++++++++++++++++++-----------------------------
 1 file changed, 32 insertions(+), 38 deletions(-)

diff --git a/lua/bqn.lua b/lua/bqn.lua
index de733b3..491477f 100644
--- a/lua/bqn.lua
+++ b/lua/bqn.lua
@@ -1,36 +1,18 @@
local buf = nil
local win = nil
local ns = vim.api.nvim_create_namespace('bqn-out')

local function check_buf()
    if win == nil or not vim.api.nvim_win_is_valid(win) then
        local prev = vim.api.nvim_get_current_win()
        if buf == nil then
            buf = vim.api.nvim_create_buf(false, false)
        end
        vim.api.nvim_buf_set_name(buf, "BQN")
        vim.api.nvim_buf_set_option(buf, "buftype", "nofile")
        vim.api.nvim_buf_set_option(buf, "swapfile", false)
        vim.api.nvim_buf_set_option(buf, "modeline", false)
        vim.cmd("below 3split")
        vim.api.nvim_win_set_buf(vim.api.nvim_get_current_win(), buf)
        win = vim.api.nvim_get_current_win()
        vim.api.nvim_win_set_option(win, "wrap", false)
        vim.api.nvim_set_current_win(prev)
    elseif vim.api.nvim_win_is_valid(win) then
        local winid = vim.api.nvim_eval("bufwinid(" .. buf .. ")")
        if winid == -1 then
            local prev = vim.api.nvim_get_current_win()
            vim.cmd("below 3split")
            vim.api.nvim_win_set_buf(vim.api.nvim_get_current_win(), buf)
            win = vim.api.nvim_get_current_win()
            vim.api.nvim_set_current_win(prev)
        end
function evalBQN(from, to, pretty)
    -- Compute `to` position by looking back till we find first non-empty line.
    while to > 0 do
      local line = vim.api.nvim_buf_get_lines(0, to - 1, to, true)[1]
      if #line ~= 0 then break end
      to = to - 1
    end
end

function evalBQN(from, to, pretty)
    local code = vim.api.nvim_buf_get_lines(0, from - 1, to, true)
    if from > to then
      from = to
    end

    local code = vim.api.nvim_buf_get_lines(0, 0, to, true)
    local program = ""
    for k, v in ipairs(code) do
        program = program .. v .. "\n"
@@ -49,23 +31,35 @@ function evalBQN(from, to, pretty)
    if not found then
        bqn = "BQN"
    end

    local executable = assert(io.popen(bqn .. " -" .. flag .. " \"" .. program .. "\""))
    local cmd = bqn .. " -" .. flag .. " \"" .. program .. "\""
    local executable = assert(io.popen(cmd))
    local output = executable:read('*all')
    executable:close()

    check_buf()

    local lines = {}
    local line_count = 0
    for line in output:gmatch("[^\n]+") do
        table.insert(lines, line)
        table.insert(lines, {{' ' .. line, 'Comment'}})
        line_count = line_count + 1
    end
    table.insert(lines, {{' ', 'Comment'}})

    -- Compute `cto` (clear to) position by looking forward from `to` till we
    -- find first non-empty line. We do this so we clear all "orphaned" virtual
    -- line blocks (which correspond to already deleted lines).
    local total_lines = vim.api.nvim_buf_line_count(0)
    local cto = to + 1
    while cto <= total_lines do
      local line = vim.api.nvim_buf_get_lines(0, cto-1, cto, true)[1]
      if #line ~= 0 then break end
      cto = cto + 1
    end

    vim.api.nvim_buf_set_lines(buf, -1, -1, false, lines)
    vim.api.nvim_win_set_height(win, line_count)
    vim.api.nvim_win_set_cursor(win, {vim.api.nvim_buf_line_count(buf), 0})
    vim.api.nvim_buf_clear_namespace(0, ns, from - 1, cto - 1)
    vim.api.nvim_buf_set_extmark(0, ns, to - 1, 0, {
      end_line = to - 1,
      end_col = 3,
      virt_lines=lines
    })
end

return {
-- 
2.30.2
Details
Message ID
<CAOPq0As-WnTN9ujra5yP2ZWb8jphwoZX=u_0ivNfHQiqnCZq0g@mail.gmail.com>
In-Reply-To
<20211203000624.300133-1-8mayday@gmail.com> (view parent)
DKIM signature
missing
Download raw message
Ah, forgot about another thing — this patch changes how it determines
what to eval — it always sends code from the start of the buffer to
the current `to`. I actually think this is a good thing when you just
press Enter (you really want all previously defined bindings to be
available) but for visual mode — maybe it should send exactly what was
selected...
-- 
Andrey Popp / 8mayday@gmail.com
Details
Message ID
<20211203083956.juanfe3qy63icrly@haukka.localdomain>
In-Reply-To
<20211203000624.300133-1-8mayday@gmail.com> (view parent)
DKIM signature
missing
Download raw message
On Fri, Dec 03, 2021 at 03:06:24AM +0300, Andrey Popp wrote:
> Neovim 0.6.0 was recently released and has added a new feature called
> virtual lines. This allows to attach a set of "out-of-buffer" lines to
> some locations via extmark mechanism.
> 
> This patch changes how output is shown - instead of making a separate
> split buffer it shows computed value as a set of virtual lines attached
> to the "end" of the computed region.

Wow, this looks awesome! I had no idea this feature was a thing. Thanks
for the contribution.

I did run into some minor issues: evaluating an empty buffer results in
an error: bqn.lua:57: Line number outside range

An error is thrown (bqn.lua:58: end_col value outside range) also when
evaluating a single variable:

a←3
a

Also, the :EvalBQNFile seems to always end up in an error:
bqn.lua:53: attempt to get length of local 'line' (a nil value)

The error messages look like these are probably quite easy to fix.

Also, I think it would be good to have a key binding that clears up the
virtual lines below executed line. This could be handy when executing
some intermediate result that you don't care about and would like to get
rid of the output.

The code looks good to me, I have no prior experience of Lua so you
probably know more than I do.

> ---
> This adds some comments describing why something being done.
> 
>  lua/bqn.lua | 70 ++++++++++++++++++++++++-----------------------------
>  1 file changed, 32 insertions(+), 38 deletions(-)
> 
> diff --git a/lua/bqn.lua b/lua/bqn.lua
> index de733b3..491477f 100644
> --- a/lua/bqn.lua
> +++ b/lua/bqn.lua
> @@ -1,36 +1,18 @@
> -local buf = nil
> -local win = nil
> +local ns = vim.api.nvim_create_namespace('bqn-out')
>  
> -local function check_buf()
> -    if win == nil or not vim.api.nvim_win_is_valid(win) then
> -        local prev = vim.api.nvim_get_current_win()
> -        if buf == nil then
> -            buf = vim.api.nvim_create_buf(false, false)
> -        end
> -        vim.api.nvim_buf_set_name(buf, "BQN")
> -        vim.api.nvim_buf_set_option(buf, "buftype", "nofile")
> -        vim.api.nvim_buf_set_option(buf, "swapfile", false)
> -        vim.api.nvim_buf_set_option(buf, "modeline", false)
> -        vim.cmd("below 3split")
> -        vim.api.nvim_win_set_buf(vim.api.nvim_get_current_win(), buf)
> -        win = vim.api.nvim_get_current_win()
> -        vim.api.nvim_win_set_option(win, "wrap", false)
> -        vim.api.nvim_set_current_win(prev)
> -    elseif vim.api.nvim_win_is_valid(win) then
> -        local winid = vim.api.nvim_eval("bufwinid(" .. buf .. ")")
> -        if winid == -1 then
> -            local prev = vim.api.nvim_get_current_win()
> -            vim.cmd("below 3split")
> -            vim.api.nvim_win_set_buf(vim.api.nvim_get_current_win(), buf)
> -            win = vim.api.nvim_get_current_win()
> -            vim.api.nvim_set_current_win(prev)
> -        end
> +function evalBQN(from, to, pretty)
> +    -- Compute `to` position by looking back till we find first non-empty line.
> +    while to > 0 do
> +      local line = vim.api.nvim_buf_get_lines(0, to - 1, to, true)[1]
> +      if #line ~= 0 then break end
> +      to = to - 1
>      end
> -end
>  
> -function evalBQN(from, to, pretty)
> -    local code = vim.api.nvim_buf_get_lines(0, from - 1, to, true)
> +    if from > to then
> +      from = to
> +    end
>  
> +    local code = vim.api.nvim_buf_get_lines(0, 0, to, true)
>      local program = ""
>      for k, v in ipairs(code) do
>          program = program .. v .. "\n"
> @@ -49,23 +31,35 @@ function evalBQN(from, to, pretty)
>      if not found then
>          bqn = "BQN"
>      end
> -
> -    local executable = assert(io.popen(bqn .. " -" .. flag .. " \"" .. program .. "\""))
> +    local cmd = bqn .. " -" .. flag .. " \"" .. program .. "\""
> +    local executable = assert(io.popen(cmd))
>      local output = executable:read('*all')
> -    executable:close()
> -
> -    check_buf()
>  
>      local lines = {}
>      local line_count = 0
>      for line in output:gmatch("[^\n]+") do
> -        table.insert(lines, line)
> +        table.insert(lines, {{' ' .. line, 'Comment'}})
>          line_count = line_count + 1
>      end
> +    table.insert(lines, {{' ', 'Comment'}})
> +
> +    -- Compute `cto` (clear to) position by looking forward from `to` till we
> +    -- find first non-empty line. We do this so we clear all "orphaned" virtual
> +    -- line blocks (which correspond to already deleted lines).
> +    local total_lines = vim.api.nvim_buf_line_count(0)
> +    local cto = to + 1
> +    while cto <= total_lines do
> +      local line = vim.api.nvim_buf_get_lines(0, cto-1, cto, true)[1]
> +      if #line ~= 0 then break end
> +      cto = cto + 1
> +    end
>  
> -    vim.api.nvim_buf_set_lines(buf, -1, -1, false, lines)
> -    vim.api.nvim_win_set_height(win, line_count)
> -    vim.api.nvim_win_set_cursor(win, {vim.api.nvim_buf_line_count(buf), 0})
> +    vim.api.nvim_buf_clear_namespace(0, ns, from - 1, cto - 1)
> +    vim.api.nvim_buf_set_extmark(0, ns, to - 1, 0, {
> +      end_line = to - 1,
> +      end_col = 3,
> +      virt_lines=lines
> +    })
>  end
>  
>  return {
> -- 
> 2.30.2
> 

-- 
Antti
Details
Message ID
<20211203084619.554czt63zfrqsxzg@haukka.localdomain>
In-Reply-To
<CAOPq0As-WnTN9ujra5yP2ZWb8jphwoZX=u_0ivNfHQiqnCZq0g@mail.gmail.com> (view parent)
DKIM signature
missing
Download raw message
On Fri, Dec 03, 2021 at 03:31:58AM +0300, Andrey Popp wrote:
> Ah, forgot about another thing — this patch changes how it determines
> what to eval — it always sends code from the start of the buffer to
> the current `to`. I actually think this is a good thing when you just
> press Enter (you really want all previously defined bindings to be
> available) but for visual mode — maybe it should send exactly what was
> selected...

I think the enter behavior in this implementation makes sense. It
greatly reduces the amount of visual selections needed.

About the visual selection, I think your suggestion about sending
exactly the selection is the most reasonable thing to do. It could be
handy for example when there's an expensive computation somewhere in the
previous lines of the file and you would want to avoid evaluating that
every time.

-- 
Antti
Reply to thread Export thread (mbox)