From 6bba5fee036ed6051bce3c158f8f4f79c7160e96 Mon Sep 17 00:00:00 2001 From: Yaro Date: Sun, 26 Jan 2025 18:22:27 +0000 Subject: [PATCH 1/2] fix: virtual text double output --- lua/lua-console/config.lua | 2 +- lua/lua-console/injections.lua | 30 ++++++++------- lua/lua-console/utils.lua | 67 +++++++++++++++++++++++----------- spec/log.lua | 11 +++++- spec/spec_helper.lua | 4 +- 5 files changed, 76 insertions(+), 38 deletions(-) diff --git a/lua/lua-console/config.lua b/lua/lua-console/config.lua index eadca56..c8b7e18 100644 --- a/lua/lua-console/config.lua +++ b/lua/lua-console/config.lua @@ -16,7 +16,7 @@ local default_config = { title = ' Lua console ', title_pos = 'left', height = 0.6, -- percentage of main window - zindex = 1, + zindex = 100, }, mappings = { toggle = '`', diff --git a/lua/lua-console/injections.lua b/lua/lua-console/injections.lua index a5b7822..d3a6523 100644 --- a/lua/lua-console/injections.lua +++ b/lua/lua-console/injections.lua @@ -6,32 +6,36 @@ M.set_highlighting = function() local lang_prefix = config.external_evaluators.lang_prefix local lang_pattern = ('^%s([^\\n]-)\\n.+$'):format(lang_prefix) - vim.treesitter.query.add_directive('deindent!', function(_, _, _, predicate, metadata) -- remove indentaion in the region - local capture_id = predicate[2] - if not metadata[capture_id].range then return end + vim.treesitter.query.add_directive( + 'deindent!', + function(_, _, _, predicate, metadata) -- remove indentaion in the region + local capture_id = predicate[2] + if not metadata[capture_id].range then return end - metadata[capture_id].range[2] = tonumber(predicate[3]) -- set indent col to 0 - end, { all = true, force = true }) + metadata[capture_id].range[2] = tonumber(predicate[3]) -- set indent col to 0 + end, + { all = true, force = true } + ) local function extend_query(query) local extended = '' vim.tbl_map(function(path) - extended = extended .. io.open(path):read("*a") .. '\n' + extended = extended .. io.open(path):read('*a') .. '\n' end, vim.treesitter.query.get_files('lua', 'injections')) return extended .. query end - local query = ([[ ;query + local query_string = ([[ ;query ((string_content) @injection.language @injection.content - (#lua-match? @injection.language "^@1") - (#gsub! @injection.language "@2" "%1") - (#offset! @injection.content 1 0 0 0) - (#deindent! @injection.content 0)) + (#lua-match? @injection.language "^@1") + (#gsub! @injection.language "@2" "%1") + (#offset! @injection.content 1 0 0 0) + (#deindent! @injection.content 0)) ]]):gsub('@1', lang_prefix):gsub('@2', lang_pattern) - query = extend_query(query) - vim.treesitter.query.set('lua', 'injections', query) + query_string = extend_query(query_string) + vim.treesitter.query.set('lua', 'injections', query_string) end return M diff --git a/lua/lua-console/utils.lua b/lua/lua-console/utils.lua index 97f730d..b096fdb 100644 --- a/lua/lua-console/utils.lua +++ b/lua/lua-console/utils.lua @@ -26,9 +26,13 @@ end local to_table = function(obj) obj = type(obj) == 'string' and { obj } or obj - return vim.iter(obj):map(function(line) - return vim.split(line or '', '\n', { trimempty = true }) - end):flatten():totable() + return vim + .iter(obj) + :map(function(line) + return vim.split(line or '', '\n', { trimempty = true }) + end) + :flatten() + :totable() end local function remove_indentation(tbl) @@ -47,8 +51,8 @@ end ---@param highlight string higlight group local show_virtual_text = function(buf, id, text, lnum, position, highlight) local ns = vim.api.nvim_create_namespace('Lua-console') - local ext_mark = vim.api.nvim_buf_get_extmark_by_id(0, ns, id, {}) + local ext_mark = vim.api.nvim_buf_get_extmark_by_id(0, ns, id, {}) if #ext_mark > 0 then vim.api.nvim_buf_del_extmark(0, ns, id) end vim.api.nvim_buf_set_extmark(buf, ns, lnum, 0, { @@ -77,8 +81,18 @@ local toggle_help = function(buf) message = [[%s - eval a line or selection, %s - eval buffer, %s - open file, %s - load messages, %s - save console, %s - load console, %s/%s - resize window, %s - toggle help]] - message = - string.format(message, cm.eval, cm.eval_buffer, cm.open, cm.messages, cm.save, cm.load, cm.resize_up, cm.resize_down, cm.help) + message = string.format( + message, + cm.eval, + cm.eval_buffer, + cm.open, + cm.messages, + cm.save, + cm.load, + cm.resize_up, + cm.resize_down, + cm.help + ) local visible_line = vim.fn.line('w0') show_virtual_text(buf, 2, message, visible_line - 1, 'overlay', 'Comment') @@ -159,7 +173,9 @@ local get_last_assignment = function() offset = offset + 1 end - return last_var, last_val, offset + lnum = (lnum - offset) > 0 and lnum - offset or nil + + return last_var, last_val, lnum end ---Pretty prints objects @@ -191,27 +207,32 @@ end ---@param buf number ---@param lines string[] Text to append to current buffer after current selection -local append_current_buffer = function(buf, lines) +---@param lnum? number|nil Line number to append from +local append_current_buffer = function(buf, lines, lnum) if not lines or #lines == 0 then return end + lnum = lnum or vim.fn.line('.') + + local ns = vim.api.nvim_create_namespace('Lua-console') + vim.api.nvim_buf_clear_namespace(buf, ns, 0, -1) - local lnum = vim.fn.line('.') local prefix = config.buffer.result_prefix local empty_results = { 'nil', '', '""', "''" } local virtual_text local line = lines[#lines] - local last_assigned_var, last_assigned_val, last_assignment_offset = get_last_assignment() - if last_assigned_var then - virtual_text = to_string(pretty_print(last_assigned_val), '', true) - show_virtual_text(buf, 3, prefix .. virtual_text, lnum - last_assignment_offset - 1, 'eol', 'Comment') + local _, last_assigned_val, last_assignment_lnum = get_last_assignment() + if last_assignment_lnum then + last_assigned_val = to_string(pretty_print(last_assigned_val), '', true) + show_virtual_text(buf, 3, prefix .. last_assigned_val, last_assignment_lnum - 1, 'eol', 'Comment') end if vim.tbl_contains(empty_results, line) then table.remove(lines) - virtual_text = get_line_assignment(vim.fn.getbufline(buf, lnum, lnum)) or line - show_virtual_text(buf, 4, prefix .. virtual_text, lnum - 1, 'eol', 'Comment') + virtual_text = get_line_assignment(vim.fn.getbufline(buf, lnum, lnum)) or line -- ! resets env._last_assignment by calling evaluator + + if not last_assignment_lnum then show_virtual_text(buf, 4, prefix .. virtual_text, lnum - 1, 'eol', 'Comment') end end if #lines == 0 then return end @@ -293,7 +314,7 @@ function get_ctx(buf) end, _reset_last_assignment = function() mt._last_assignment = nil - end + end, } lc.ctx[buf] = env @@ -314,7 +335,7 @@ function lua_evaluator(lines, ctx) local lines_with_return_last_line = add_return(lines, #lines) if not select(2, load(to_string(lines_with_return_first_line), '', 't', env)) then - lines = lines_with_return_first_line + lines = lines_with_return_first_line elseif not select(2, load(to_string(lines_with_return_last_line), '', 't', env)) then lines = lines_with_return_last_line end @@ -409,7 +430,7 @@ end ---@param range number[] ---@return string local function get_lang(buf, range) - local pattern = ('^.*' .. config.external_evaluators.lang_prefix .. '(.-)%s*$') + local pattern = ('^.*' .. config.external_evaluators.lang_prefix .. '(%w+)%s*$') local line, lang line = vim.api.nvim_buf_get_lines(buf, math.max(0, range[1] - 2), range[2], false)[1] @@ -442,7 +463,10 @@ local eval_code_in_buffer = function(buf, full) buf = buf or vim.fn.bufnr() local win = vim.fn.bufwinid(buf) - if vim.api.nvim_get_mode().mode == 'V' then vim.api.nvim_input('') end + if vim.api.nvim_get_mode().mode == 'V' then + LOG('here') + vim.api.nvim_input('') + end local v_start, v_end if full then @@ -472,6 +496,7 @@ end ---Load messages into console local load_messages = function(buf) local ns = vim.api.nvim_create_namespace('Lua-console') + local lnum = vim.fn.line('.') ---This way we catch the output of messages command, in case it was overriden by some other plugin, like Noice vim.ui_attach(ns, { ext_messages = true }, function(event, entries) ---@diagnostic disable-line @@ -484,8 +509,8 @@ local load_messages = function(buf) if #messages == 0 then return end vim.schedule(function() - vim.api.nvim_input('') -- forcing to redraw buffer - append_current_buffer(buf, to_table(messages)) + append_current_buffer(buf, to_table(messages), lnum) + vim.api.nvim__redraw { flush = true, buf = buf } end) end) diff --git a/spec/log.lua b/spec/log.lua index 01bc0a6..9ea9090 100644 --- a/spec/log.lua +++ b/spec/log.lua @@ -3,17 +3,26 @@ local inspect = require('inspect') local log = function(...) --luacheck: ignore local caller = debug.getinfo(2) + local caller_path = caller.short_src local time = os.date('*t', os.time()) time.min, time.sec = 0, 0 + +---@diagnostic disable-next-line time = os.time() - os.time(time) + if caller_path then + local path_dirs = vim.split(vim.fs.dirname(caller_path), '/') + caller_path = path_dirs[#path_dirs] .. '/' .. vim.fs.basename(caller_path) + end + local result = ('\nLOG #%s (%s:%s:%s) => '):format( time, + caller_path or '', caller.name or '', - caller.short_src or '', caller.currentline or '' ) + local nargs = select('#', ...) local var_no = '' diff --git a/spec/spec_helper.lua b/spec/spec_helper.lua index b06b7e6..a77da88 100644 --- a/spec/spec_helper.lua +++ b/spec/spec_helper.lua @@ -19,7 +19,7 @@ end M.to_string = function(tbl) tbl = tbl or {} - if type(tbl) == 'string' then tbl = { tbl } end + if type(tbl) ~= 'table' then tbl = { tbl } end return table.concat(tbl, '\n'):clean() end @@ -62,7 +62,7 @@ end M.get_virtual_text = function(buf, line_start, line_end) local ns = vim.api.nvim_create_namespace('Lua-console') - local ids = vim.api.nvim_buf_get_extmarks(buf, ns, { line_start or 0, 0 }, { line_end or 0, -1 }, {}) + local ids = vim.api.nvim_buf_get_extmarks(buf, ns, { line_start or 0, 0 }, { line_end or -1, -1 }, {}) if vim.tbl_isempty(ids) then _G.LOG('No extmarks found') From 971d8f1c1f53030f6ee5e9cfeda545e8b40c4955 Mon Sep 17 00:00:00 2001 From: Yaro Date: Fri, 7 Feb 2025 11:31:35 +0000 Subject: [PATCH 2/2] feat: evaluate char selection --- lua/lua-console/config.lua | 1 + lua/lua-console/mappings.lua | 2 +- lua/lua-console/utils.lua | 52 +++++++++++++++++++++++------------- spec/spec_helper.lua | 9 ++++--- spec/unit/mappings_spec.lua | 3 +++ spec/unit/utils_spec.lua | 43 +++++++++++++++++++++++++++-- 6 files changed, 84 insertions(+), 26 deletions(-) diff --git a/lua/lua-console/config.lua b/lua/lua-console/config.lua index c8b7e18..f2a5846 100644 --- a/lua/lua-console/config.lua +++ b/lua/lua-console/config.lua @@ -7,6 +7,7 @@ local default_config = { autosave = true, -- autosave on console hide / close load_on_start = true, -- load saved session on start preserve_context = true, -- preserve results between evaluations + print_one_line_results = true, }, window = { relative = 'editor', diff --git a/lua/lua-console/mappings.lua b/lua/lua-console/mappings.lua index cfc948e..b44702f 100644 --- a/lua/lua-console/mappings.lua +++ b/lua/lua-console/mappings.lua @@ -129,7 +129,7 @@ M.set_evaluator_mappings = function(buf, toggle) callback = function() utils.eval_code_in_buffer(buf) end, - }, { 'n', 'v' }) + }, { 'n', 'x' }) set_map(buf, m.eval_buffer, { desc = 'Eval code in current buffer', diff --git a/lua/lua-console/utils.lua b/lua/lua-console/utils.lua index b096fdb..a036682 100644 --- a/lua/lua-console/utils.lua +++ b/lua/lua-console/utils.lua @@ -169,7 +169,7 @@ local get_last_assignment = function() for i = lnum - 1, 0, -1 do line = vim.api.nvim_buf_get_lines(0, i, i + 1, false)[1] - if line:match('^%s*' .. last_var .. '%s*=') then break end + if line:match('^%s*' .. last_var .. '%s*,?[^=]-=') then break end offset = offset + 1 end @@ -232,11 +232,20 @@ local append_current_buffer = function(buf, lines, lnum) virtual_text = get_line_assignment(vim.fn.getbufline(buf, lnum, lnum)) or line -- ! resets env._last_assignment by calling evaluator - if not last_assignment_lnum then show_virtual_text(buf, 4, prefix .. virtual_text, lnum - 1, 'eol', 'Comment') end + if last_assignment_lnum ~= lnum then + show_virtual_text(buf, 4, prefix .. virtual_text, lnum - 1, 'eol', 'Comment') + end end if #lines == 0 then return end + if #lines == 1 and last_assignment_lnum ~= lnum and not config.buffer.show_one_line_results then + virtual_text = lines[1] + show_virtual_text(buf, 4, prefix .. virtual_text, lnum - 1, 'eol', 'Comment') + + return + end + lines[1] = prefix .. lines[1] table.insert(lines, 1, '') -- insert an empty line @@ -429,23 +438,23 @@ end ---@param buf number ---@param range number[] ---@return string -local function get_lang(buf, range) +local function get_lang(buf, lnum) local pattern = ('^.*' .. config.external_evaluators.lang_prefix .. '(%w+)%s*$') local line, lang - line = vim.api.nvim_buf_get_lines(buf, math.max(0, range[1] - 2), range[2], false)[1] - lang = line:match(pattern) + line = vim.api.nvim_buf_get_lines(buf, math.max(0, lnum - 1), lnum, false)[1] + lang = line and line:match(pattern) if lang then return lang end line = vim.api.nvim_buf_get_lines(buf, 0, 1, false)[1] - lang = line:match(pattern) + lang = line and line:match(pattern) if lang then return lang end return vim.bo[buf].filetype end -local get_evaluator = function(buf, range) - local lang = get_lang(buf, range) +local get_evaluator = function(buf, lnum) + local lang = get_lang(buf, lnum) if lang == '' then vim.notify('Plese specify the language to evaluate or set the filetype', vim.log.levels.WARN) @@ -461,30 +470,35 @@ end ---@param full? boolean evaluate full buffer local eval_code_in_buffer = function(buf, full) buf = buf or vim.fn.bufnr() - local win = vim.fn.bufwinid(buf) - if vim.api.nvim_get_mode().mode == 'V' then - LOG('here') - vim.api.nvim_input('') - end + local mode = vim.api.nvim_get_mode().mode + if mode == 'V' or mode == 'v' then vim.api.nvim_input('') end + + local v_start, v_end, lines - local v_start, v_end if full then v_start, v_end = 1, vim.api.nvim_buf_line_count(buf) - else - v_start, v_end = vim.fn.line('.', win), vim.fn.line('v', win) + elseif mode == 'v' or mode == 'V' then + v_start, v_end = vim.fn.getpos('.'), vim.fn.getpos('v') + lines = vim.fn.getregion(v_start, v_end, { type = mode }) + + v_start, v_end = v_start[2], v_end[2] + if v_start > v_end then v_start, v_end = v_end, v_start end + else + v_start = vim.fn.line('.') + v_end = v_start end - vim.api.nvim_win_set_cursor(win, { v_end, 0 }) + vim.fn.cursor(v_end, 0) - local lines = vim.api.nvim_buf_get_lines(buf, v_start - 1, v_end, false) + lines = lines or vim.api.nvim_buf_get_lines(buf, v_start - 1, v_end, false) lines = remove_empty_lines(lines) if #lines == 0 then return end - local evaluator = get_evaluator(buf, { v_start, v_end }) + local evaluator = get_evaluator(buf, v_start - 1) if not evaluator then return end local result = evaluator(lines) diff --git a/spec/spec_helper.lua b/spec/spec_helper.lua index a77da88..b2fe44c 100644 --- a/spec/spec_helper.lua +++ b/spec/spec_helper.lua @@ -61,17 +61,18 @@ M.send_keys = function(keys) end M.get_virtual_text = function(buf, line_start, line_end) - local ns = vim.api.nvim_create_namespace('Lua-console') - local ids = vim.api.nvim_buf_get_extmarks(buf, ns, { line_start or 0, 0 }, { line_end or -1, -1 }, {}) + local ids = vim.api.nvim_buf_get_extmarks(buf, -1, { line_start or 0, 0 }, { line_end or -1, -1 }, { details = true }) if vim.tbl_isempty(ids) then _G.LOG('No extmarks found') return '' end - local mark = vim.api.nvim_buf_get_extmark_by_id(buf, ns, ids[1][1], { details = true }) + local marks = vim.tbl_map(function(mark) + return mark[4].virt_text[1][1] + end, ids) - return mark[3].virt_text[1][1] + return marks end ---Collects paths for nested keys diff --git a/spec/unit/mappings_spec.lua b/spec/unit/mappings_spec.lua index 8f48402..ae85d93 100644 --- a/spec/unit/mappings_spec.lua +++ b/spec/unit/mappings_spec.lua @@ -29,6 +29,7 @@ describe('lua-console.nvim - mappings', function() buffer = { load_on_start = false, save_path = vim.fn.stdpath('state') .. '/lua-console-test.lua', + show_one_line_results = true, }, } @@ -264,6 +265,7 @@ describe('lua-console.nvim - mappings', function() utils.attach_toggle(other_buf) end) + --TODO: finish spec it('cretes mappings for attached buffer', function() -- code end) @@ -328,6 +330,7 @@ describe('lua-console.nvim - mappings', function() -- code end) + --TODO: finish spec it('removes mappings for dettached buffer', function() -- code end) diff --git a/spec/unit/utils_spec.lua b/spec/unit/utils_spec.lua index 6c12464..ee62f8a 100644 --- a/spec/unit/utils_spec.lua +++ b/spec/unit/utils_spec.lua @@ -1,6 +1,8 @@ local assert = require('luassert.assert') local h = require('spec_helper') +--TODO: add multiple assigmnets andchar selection + describe('lua-console.utils', function() _G.Lua_console = {} local buf, config, utils @@ -8,7 +10,7 @@ describe('lua-console.utils', function() setup(function() utils = require('lua-console.utils') config = require('lua-console.config') - config.setup() + config.setup { buffer = { show_one_line_results = true } } end) before_each(function() @@ -332,6 +334,23 @@ describe('lua-console.utils', function() assert.has_string(result, expected) end) + it('single line - char selection', function() + content = h.to_table([[ + Some text + LOG(vim.bo.filetype) + Some text + ]]) + + h.set_buffer(buf, content) + + vim.api.nvim_win_set_cursor(win, { 2, 4 }) + h.send_keys('v14l') + utils.eval_code_in_buffer() + + result = h.get_buffer(buf) + assert.has_string(result, 'lua') + end) + it('multiline', function() vim.api.nvim_win_set_cursor(win, { 2, 0 }) vim.cmd.exe("'normal V3j'") @@ -495,7 +514,27 @@ describe('lua-console.utils', function() expected = config.buffer.result_prefix .. '5' - result = h.get_virtual_text(buf, 0, 0) + result = h.get_virtual_text(buf) + assert.has_string(result, expected) + end) + + it('shows value of the multiple assignments as virtual text', function() + vim.api.nvim_win_set_cursor(win, { 1, 0 }) + h.send_keys('V4j') + + content = h.to_table([[ + a = 5 + for i = 1, 5 do + i = i + 5 + end + vim.bo.filetype, c = tostring(a + 5) .. 'test', 200 + ]]) + h.set_buffer(buf, content) + utils.eval_code_in_buffer() + + expected = config.buffer.result_prefix .. '[1]"10test", [2] 200' + + result = h.get_virtual_text(buf) assert.has_string(result, expected) end)