This commit makes nvim-bqn use virtual lines feature of neovim 0.6.0
show output of running BQN expressions.
The set of available command has changed, first we have the following
commands to eval BQN expressions:
:EvalBQNTillLine
:EvalBQNRange
:EvalBQNFile
and then a set of commands to clear output:
:ClearBQNAfterLine
:ClearBQNRange
:ClearBQNFile
There's one change to the mappings is that `<CR>` is not mapped to
`:EvalBQNTillLine` and thus it means it evaluates everything from the
start of the buffer till the line the cursor is at. This behaviour is
useful when you bindings defined above the cursor.
When rendering output the highlight group is used either `bqnoutok` or
`bqnouterr` depending on if the interpreter returned an error. By
default those groups are configured as:
hi link bqnoutok Comment
hi link bqnouterr Error
---
Ok, I think this is good now.
Regarding mappings — I didn't map "clearing output" commands to anything yet.
I was thinking of <C-CR> but then maybe it's useful to keep for the possible
future action "eval this expression and step to the next one". Your call here.
I also want to point out that <C-Space> mapping is a bit problematic for macOS
users and by default <C-Space> changes keyboard layout in macOS.
ftplugin/bqn.vim | 2 +-lua/bqn.lua | 84 +++++++++++++++++++++++++++---------------------plugin/bqn.vim | 36 ++++++++++++++++++---
3 files changed, 80 insertions(+), 42 deletions(-)
diff --git a/ftplugin/bqn.vim b/ftplugin/bqn.vim
index 5721033..069cb05 100644
--- a/ftplugin/bqn.vim+++ b/ftplugin/bqn.vim
@@ -1,3 +1,3 @@
-nnoremap <buffer> <CR> :EvalBQNRange<CR>+nnoremap <buffer> <CR> :EvalBQNTillLine<CR>nnoremap <buffer> <C-Space> :EvalBQNFile<CR>
xnoremap <buffer> <CR> :EvalBQNRange<CR>
diff --git a/lua/bqn.lua b/lua/bqn.lua
index de733b3..33b95c8 100644
--- a/lua/bqn.lua+++ b/lua/bqn.lua
@@ -1,36 +1,28 @@
-local buf = nil-local win = nil+local ns = vim.api.nvim_create_namespace('bqnout')-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- end+function clearBQN(from, to)+ vim.api.nvim_buf_clear_namespace(0, ns, from, to)end
function evalBQN(from, to, pretty)
- local code = vim.api.nvim_buf_get_lines(0, from - 1, to, true)+ if to < 0 then+ to = vim.api.nvim_buf_line_count(0) + to + 1+ end+ -- 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 and line:find("^%s*#") == nil then break end+ to = to - 1+ end++ if from > to then+ from = to+ end+ to = math.max(to, 1)+ from = math.max(from, 0)++ local code = vim.api.nvim_buf_get_lines(0, from, to, true) local program = ""
for k, v in ipairs(code) do
program = program .. v .. "\n"
@@ -49,25 +41,43 @@ 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
+ local is_error = nil for line in output:gmatch("[^\n]+") do
- table.insert(lines, line)+ if is_error == nil then+ is_error = line:find("^Error:") ~= nil+ end+ local hl = 'bqnoutok'+ if is_error then hl = 'bqnouterr' end+ table.insert(lines, {{' ' .. line, hl}}) line_count = line_count + 1
end
+ table.insert(lines, {{' ', 'bqnoutok'}})++ -- 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+ while cto < total_lines do+ local line = vim.api.nvim_buf_get_lines(0, cto, cto + 1, true)[1]+ if #line ~= 0 and line:find("^%s*#") == nil 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, to - 1, cto)+ vim.api.nvim_buf_set_extmark(0, ns, to - 1, 0, {+ end_line = to - 1,+ virt_lines=lines+ })end
return {
evalBQN = evalBQN,
+ clearBQN = clearBQN,}
diff --git a/plugin/bqn.vim b/plugin/bqn.vim
index e87288d..9c84e58 100644
--- a/plugin/bqn.vim+++ b/plugin/bqn.vim
@@ -1,6 +1,34 @@
-function! EvalBQNWrapper() range- return luaeval('require("bqn").evalBQN(_A[1], _A[2], true)', [a:firstline, a:lastline])+function! EvalBQNTillLine()+ return luaeval(+ \ 'require("bqn").evalBQN(0, _A[1], true)',+ \ [line(".")])endfunction
-command! -range EvalBQNRange <line1>,<line2>call EvalBQNWrapper()-command! EvalBQNFile :lua require("bqn").evalBQN(1, -1, false)+function! EvalBQNRange() range+ return luaeval(+ \ 'require("bqn").evalBQN(_A[1] - 1, _A[2], true)',+ \ [a:firstline, a:lastline])+endfunction++function! ClearBQNAfterLine()+ return luaeval(+ \ 'require("bqn").clearBQN(_A[1] - 1, -1)',+ \ [line(".")])+endfunction++function! ClearBQNRange()+ return luaeval(+ \ 'require("bqn").clearBQN(_A[1] - 1, _A[2])',+ \ [a:firstline, a:lastline])+endfunction++hi link bqnoutok Comment+hi link bqnouterr Error++command! EvalBQNTillLine call EvalBQNTillLine()+command! -range EvalBQNRange <line1>,<line2>call EvalBQNRange()+command! EvalBQNFile :lua require("bqn").evalBQN(0, -1, true)++command! ClearBQNAfterLine call ClearBQNAfterLine()+command! -range ClearBQNRange <line1>,<line2>call ClearBQNRange()+command! ClearBQNFile :lua require("bqn").clearBQN(0, -1)
--
2.30.2
On Sat, Dec 04, 2021 at 05:16:00PM +0300, Andrey Popp wrote:
> This commit makes nvim-bqn use virtual lines feature of neovim 0.6.0> show output of running BQN expressions.> > The set of available command has changed, first we have the following> commands to eval BQN expressions:> > :EvalBQNTillLine> :EvalBQNRange> :EvalBQNFile> > and then a set of commands to clear output:> > :ClearBQNAfterLine> :ClearBQNRange> :ClearBQNFile
These look good.
> There's one change to the mappings is that `<CR>` is not mapped to> `:EvalBQNTillLine` and thus it means it evaluates everything from the> start of the buffer till the line the cursor is at. This behaviour is> useful when you bindings defined above the cursor.
I guess this should say "is mapped" instead of "is not mapped".
> When rendering output the highlight group is used either `bqnoutok` or> `bqnouterr` depending on if the interpreter returned an error. By> default those groups are configured as:> > hi link bqnoutok Comment> hi link bqnouterr Error
This was a welcome change! Did not think about it on the previous
iteration but it is a great idea to use different color for errors.
> ---> > Ok, I think this is good now.> > Regarding mappings — I didn't map "clearing output" commands to anything yet.> I was thinking of <C-CR> but then maybe it's useful to keep for the possible> future action "eval this expression and step to the next one". Your call here.
The issue of <C-CR> is that it cannot be reliably mapped. That was the
initial mapping I wanted to do for evaluating a whole file, but I did
not get it to map properly so I went for <C-Space> instead.
> I also want to point out that <C-Space> mapping is a bit problematic for macOS> users and by default <C-Space> changes keyboard layout in macOS.
Good to know. Do you have any suggestions of what could be better? I'm
thinking something along the lines of <leader>Space.
I think this is quite usable as-is, but I managed to run into some
annoying corner cases. I'll post those into the bug tracker later today.
In the meanwhile, I'll apply this commit into a branch called
virtual-lines.
> ftplugin/bqn.vim | 2 +-> lua/bqn.lua | 84 +++++++++++++++++++++++++++---------------------> plugin/bqn.vim | 36 ++++++++++++++++++---> 3 files changed, 80 insertions(+), 42 deletions(-)> > diff --git a/ftplugin/bqn.vim b/ftplugin/bqn.vim> index 5721033..069cb05 100644> --- a/ftplugin/bqn.vim> +++ b/ftplugin/bqn.vim> @@ -1,3 +1,3 @@> -nnoremap <buffer> <CR> :EvalBQNRange<CR>> +nnoremap <buffer> <CR> :EvalBQNTillLine<CR>> nnoremap <buffer> <C-Space> :EvalBQNFile<CR>> xnoremap <buffer> <CR> :EvalBQNRange<CR>> diff --git a/lua/bqn.lua b/lua/bqn.lua> index de733b3..33b95c8 100644> --- a/lua/bqn.lua> +++ b/lua/bqn.lua> @@ -1,36 +1,28 @@> -local buf = nil> -local win = nil> +local ns = vim.api.nvim_create_namespace('bqnout')> > -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> - end> +function clearBQN(from, to)> + vim.api.nvim_buf_clear_namespace(0, ns, from, to)> end> > function evalBQN(from, to, pretty)> - local code = vim.api.nvim_buf_get_lines(0, from - 1, to, true)> + if to < 0 then> + to = vim.api.nvim_buf_line_count(0) + to + 1> + end> + -- 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 and line:find("^%s*#") == nil then break end> + to = to - 1> + end> +> + if from > to then> + from = to> + end> > + to = math.max(to, 1)> + from = math.max(from, 0)> +> + local code = vim.api.nvim_buf_get_lines(0, from, to, true)> local program = ""> for k, v in ipairs(code) do> program = program .. v .. "\n"> @@ -49,25 +41,43 @@ 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> + local is_error = nil> for line in output:gmatch("[^\n]+") do> - table.insert(lines, line)> + if is_error == nil then> + is_error = line:find("^Error:") ~= nil> + end> + local hl = 'bqnoutok'> + if is_error then hl = 'bqnouterr' end> + table.insert(lines, {{' ' .. line, hl}})> line_count = line_count + 1> end> + table.insert(lines, {{' ', 'bqnoutok'}})> +> + -- 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> + while cto < total_lines do> + local line = vim.api.nvim_buf_get_lines(0, cto, cto + 1, true)[1]> + if #line ~= 0 and line:find("^%s*#") == nil 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, to - 1, cto)> + vim.api.nvim_buf_set_extmark(0, ns, to - 1, 0, {> + end_line = to - 1,> + virt_lines=lines> + })> end> > return {> evalBQN = evalBQN,> + clearBQN = clearBQN,> }> diff --git a/plugin/bqn.vim b/plugin/bqn.vim> index e87288d..9c84e58 100644> --- a/plugin/bqn.vim> +++ b/plugin/bqn.vim> @@ -1,6 +1,34 @@> -function! EvalBQNWrapper() range> - return luaeval('require("bqn").evalBQN(_A[1], _A[2], true)', [a:firstline, a:lastline])> +function! EvalBQNTillLine()> + return luaeval(> + \ 'require("bqn").evalBQN(0, _A[1], true)',> + \ [line(".")])> endfunction> > -command! -range EvalBQNRange <line1>,<line2>call EvalBQNWrapper()> -command! EvalBQNFile :lua require("bqn").evalBQN(1, -1, false)> +function! EvalBQNRange() range> + return luaeval(> + \ 'require("bqn").evalBQN(_A[1] - 1, _A[2], true)',> + \ [a:firstline, a:lastline])> +endfunction> +> +function! ClearBQNAfterLine()> + return luaeval(> + \ 'require("bqn").clearBQN(_A[1] - 1, -1)',> + \ [line(".")])> +endfunction> +> +function! ClearBQNRange()> + return luaeval(> + \ 'require("bqn").clearBQN(_A[1] - 1, _A[2])',> + \ [a:firstline, a:lastline])> +endfunction> +> +hi link bqnoutok Comment> +hi link bqnouterr Error> +> +command! EvalBQNTillLine call EvalBQNTillLine()> +command! -range EvalBQNRange <line1>,<line2>call EvalBQNRange()> +command! EvalBQNFile :lua require("bqn").evalBQN(0, -1, true)> +> +command! ClearBQNAfterLine call ClearBQNAfterLine()> +command! -range ClearBQNRange <line1>,<line2>call ClearBQNRange()> +command! ClearBQNFile :lua require("bqn").clearBQN(0, -1)> -- > 2.30.2>
--
Antti
Hi,
On Sun Dec 5, 2021 at 10:35 AM MSK, Antti Keränen wrote:
> On Sat, Dec 04, 2021 at 05:16:00PM +0300, Andrey Popp wrote:> > The set of available command has changed, first we have the following> > commands to eval BQN expressions:> > > > :EvalBQNTillLine> > :EvalBQNRange> > :EvalBQNFile> > > > and then a set of commands to clear output:> > > > :ClearBQNAfterLine> > :ClearBQNRange> > :ClearBQNFile>> These look good.
Regarding those I have a suggestion - maybe rename them so BQN stands out as a
prefix? I feel like this will make all BQN related commands more discoverable:
:BQNEvalTillLine
:BQNEvalRange
:BQNEvalFile
:BQNClearAfterLine
:BQNClearRange
:BQNClearFile
> > There's one change to the mappings is that `<CR>` is not mapped to> > `:EvalBQNTillLine` and thus it means it evaluates everything from the> > start of the buffer till the line the cursor is at. This behaviour is> > useful when you bindings defined above the cursor.>> I guess this should say "is mapped" instead of "is not mapped".
Yes, correct.
> > When rendering output the highlight group is used either `bqnoutok` or> > `bqnouterr` depending on if the interpreter returned an error. By> > default those groups are configured as:> > > > hi link bqnoutok Comment> > hi link bqnouterr Error>> This was a welcome change! Did not think about it on the previous> iteration but it is a great idea to use different color for errors.
I actually have another patch in the pipeline which sets a diagnostic message
(using new vim.diagnostic API) when it receives an error. The message is
attached to the correct line number in the current buffer.
> > ---> > > > Ok, I think this is good now.> > > > Regarding mappings — I didn't map "clearing output" commands to anything yet.> > I was thinking of <C-CR> but then maybe it's useful to keep for the possible> > future action "eval this expression and step to the next one". Your call here.>> The issue of <C-CR> is that it cannot be reliably mapped. That was the> initial mapping I wanted to do for evaluating a whole file, but I did> not get it to map properly so I went for <C-Space> instead.
Yeah, that's true.
I actually like when a plugin doesn't set any mappings and instead provides
a set of <Plug> mappings so users can map them to whatever they like. But <CR>
is too good not to be mapped!
So I'd keep <CR> and either provide <Plug> mappings or that + map those <Plug>
mappings to keys with `<leader>b` prefix (where `b` stands for BQN).
> > I also want to point out that <C-Space> mapping is a bit problematic for macOS> > users and by default <C-Space> changes keyboard layout in macOS.>> Good to know. Do you have any suggestions of what could be better? I'm> thinking something along the lines of <leader>Space.
Following my suggestion above - probably `<leader>bef` - "BQN eval file" or
"<leader>bf" if this is needed really often (I found myself mostly using just
<CR>).
> I think this is quite usable as-is, but I managed to run into some> annoying corner cases. I'll post those into the bug tracker later today.> In the meanwhile, I'll apply this commit into a branch called> virtual-lines.
Yes please! My usage is pretty trivial right now (only single file, short
programs). I've too managed to run into some weird cases but those were bugs in
neovim's virtual line implementation... as I understood.
Hi.
On Sun, Dec 05, 2021 at 04:26:29PM +0300, Andrey Popp wrote:
> Hi,> > On Sun Dec 5, 2021 at 10:35 AM MSK, Antti Keränen wrote:> > On Sat, Dec 04, 2021 at 05:16:00PM +0300, Andrey Popp wrote:> > > The set of available command has changed, first we have the following> > > commands to eval BQN expressions:> > > > > > :EvalBQNTillLine> > > :EvalBQNRange> > > :EvalBQNFile> > > > > > and then a set of commands to clear output:> > > > > > :ClearBQNAfterLine> > > :ClearBQNRange> > > :ClearBQNFile> >> > These look good.> > Regarding those I have a suggestion - maybe rename them so BQN stands out as a> prefix? I feel like this will make all BQN related commands more discoverable:> > :BQNEvalTillLine> :BQNEvalRange> :BQNEvalFile> > :BQNClearAfterLine> :BQNClearRange> :BQNClearFile>
Absolutely. I agree this makes sense because of multiple reasons.
> > > There's one change to the mappings is that `<CR>` is not mapped to> > > `:EvalBQNTillLine` and thus it means it evaluates everything from the> > > start of the buffer till the line the cursor is at. This behaviour is> > > useful when you bindings defined above the cursor.> >> > I guess this should say "is mapped" instead of "is not mapped".> > Yes, correct.> > > > When rendering output the highlight group is used either `bqnoutok` or> > > `bqnouterr` depending on if the interpreter returned an error. By> > > default those groups are configured as:> > > > > > hi link bqnoutok Comment> > > hi link bqnouterr Error> >> > This was a welcome change! Did not think about it on the previous> > iteration but it is a great idea to use different color for errors.> > I actually have another patch in the pipeline which sets a diagnostic message> (using new vim.diagnostic API) when it receives an error. The message is> attached to the correct line number in the current buffer.
I'm not sure what that means in practice, but looking forward to it :)
> > > > ---> > > > > > Ok, I think this is good now.> > > > > > Regarding mappings — I didn't map "clearing output" commands to anything yet.> > > I was thinking of <C-CR> but then maybe it's useful to keep for the possible> > > future action "eval this expression and step to the next one". Your call here.> >> > The issue of <C-CR> is that it cannot be reliably mapped. That was the> > initial mapping I wanted to do for evaluating a whole file, but I did> > not get it to map properly so I went for <C-Space> instead.> > Yeah, that's true.> > I actually like when a plugin doesn't set any mappings and instead provides> a set of <Plug> mappings so users can map them to whatever they like. But <CR>> is too good not to be mapped!> > So I'd keep <CR> and either provide <Plug> mappings or that + map those <Plug>> mappings to keys with `<leader>b` prefix (where `b` stands for BQN).> > > > I also want to point out that <C-Space> mapping is a bit problematic for macOS> > > users and by default <C-Space> changes keyboard layout in macOS.> >> > Good to know. Do you have any suggestions of what could be better? I'm> > thinking something along the lines of <leader>Space.> > Following my suggestion above - probably `<leader>bef` - "BQN eval file" or> "<leader>bf" if this is needed really often (I found myself mostly using just> <CR>).
I like <leader>bf better. 4 characters for one command feels like too
much.
> > > I think this is quite usable as-is, but I managed to run into some> > annoying corner cases. I'll post those into the bug tracker later today.> > In the meanwhile, I'll apply this commit into a branch called> > virtual-lines.> > Yes please! My usage is pretty trivial right now (only single file, short> programs). I've too managed to run into some weird cases but those were bugs in> neovim's virtual line implementation... as I understood.
Posted a couple of bugs in https://todo.sr.ht/~detegr/nvim-bqn
--
Antti