diff options
-rw-r--r-- | runtime/doc/lua.txt | 29 | ||||
-rw-r--r-- | runtime/doc/vim_diff.txt | 4 | ||||
-rw-r--r-- | runtime/lua/vim/highlight.lua | 41 | ||||
-rw-r--r-- | src/nvim/lua/vim.lua | 63 | ||||
-rw-r--r-- | test/functional/lua/vim_spec.lua | 22 |
5 files changed, 158 insertions, 1 deletions
diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index 09034353a3..2b83c35c90 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -693,6 +693,27 @@ identical identifiers, highlighting both as |hl-WarningMsg|: > (eq? @WarningMsg.left @WarningMsg.right)) ------------------------------------------------------------------------------ +VIM.HIGHLIGHT *lua-highlight* + +Nvim includes a function for highlighting a selection on yank (see for example +https://github.com/machakann/vim-highlightedyank). To enable it, add +> + au TextYankPost * silent! lua require'vim.highlight'.on_yank() +< +to your `init.vim`. You can customize the highlight group and the duration of +the highlight via +> + au TextYankPost * silent! lua require'vim.highlight'.on_yank("IncSearch", 500) +< + +vim.highlight.on_yank([{higroup}, {timeout}, {event}]) + *vim.highlight.on_yank()* + Highlights the yanked text. Optional arguments are the highlight group + to use ({higroup}, default `"IncSearch"`), the duration of highlighting + in milliseconds ({timeout}, default `500`), and the event structure + that is fired ({event}, default `vim.v.event`). + +------------------------------------------------------------------------------ VIM.REGEX *lua-regex* Vim regexes can be used directly from lua. Currently they only allow @@ -758,6 +779,14 @@ vim.empty_dict() *vim.empty_dict()* Note: if numeric keys are added to the table, the metatable will be ignored and the dict converted to a list/array anyway. +vim.region({bufnr}, {pos1}, {pos2}, {type}, {inclusive}) *vim.region()* + Converts a selection specified by the buffer ({bufnr}), starting + position ({pos1}, a zero-indexed pair `{line1,column1}`), ending + position ({pos2}, same format as {pos1}), the type of the register + for the selection ({type}, see |regtype|), and a boolean indicating + whether the selection is inclusive or not, into a zero-indexed table + of linewise selections of the form `{linenr = {startcol, endcol}}` . + vim.rpcnotify({channel}, {method}[, {args}...]) *vim.rpcnotify()* Sends {event} to {channel} via |RPC| and returns immediately. If {channel} is 0, the event is broadcast to all channels. diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index 376375e4ef..24b562543e 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -116,6 +116,10 @@ backwards-compatibility cost. Some examples: - Directories for 'directory' and 'undodir' are auto-created. - Terminal features such as 'guicursor' are enabled where possible. +Some features are built in that otherwise required external plugins: + +- Highlighting the yanked region, see |lua-highlight|. + ARCHITECTURE ~ External plugins run in separate processes. |remote-plugin| This improves diff --git a/runtime/lua/vim/highlight.lua b/runtime/lua/vim/highlight.lua new file mode 100644 index 0000000000..5c98c626a4 --- /dev/null +++ b/runtime/lua/vim/highlight.lua @@ -0,0 +1,41 @@ +local api = vim.api + +local highlight = {} + +--- Highlight the yanked region +-- +--- use from init.vim via +--- au TextYankPost * lua require'vim.highlight'.on_yank() +--- customize highlight group and timeout via +--- au TextYankPost * lua require'vim.highlight'.on_yank("IncSearch", 500) +-- @param higroup highlight group for yanked region +-- @param timeout time in ms before highlight is cleared +-- @param event event structure +function highlight.on_yank(higroup, timeout, event) + event = event or vim.v.event + if event.operator ~= 'y' or event.regtype == '' then return end + higroup = higroup or "IncSearch" + timeout = timeout or 500 + + local bufnr = api.nvim_get_current_buf() + local yank_ns = api.nvim_create_namespace('') + api.nvim_buf_clear_namespace(bufnr, yank_ns, 0, -1) + + local pos1 = vim.fn.getpos("'[") + local pos2 = vim.fn.getpos("']") + + pos1 = {pos1[2] - 1, pos1[3] - 1 + pos1[4]} + pos2 = {pos2[2] - 1, pos2[3] - 1 + pos2[4]} + + local region = vim.region(bufnr, pos1, pos2, event.regtype, event.inclusive) + for linenr, cols in pairs(region) do + api.nvim_buf_add_highlight(bufnr, yank_ns, higroup, linenr, cols[1], cols[2]) + end + + vim.defer_fn( + function() api.nvim_buf_clear_namespace(bufnr, yank_ns, 0, -1) end, + timeout + ) +end + +return highlight diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua index 43a0a76b94..523d23ec12 100644 --- a/src/nvim/lua/vim.lua +++ b/src/nvim/lua/vim.lua @@ -415,4 +415,67 @@ do vim.wo = new_win_opt_accessor(nil) end +--- Get a table of lines with start, end columns for a region marked by two points +--- +--@param bufnr number of buffer +--@param pos1 (line, column) tuple marking beginning of region +--@param pos2 (line, column) tuple marking end of region +--@param regtype type of selection (:help setreg) +--@param inclusive boolean indicating whether the selection is end-inclusive +--@return region lua table of the form {linenr = {startcol,endcol}} +function vim.region(bufnr, pos1, pos2, regtype, inclusive) + if not vim.api.nvim_buf_is_loaded(bufnr) then + vim.fn.bufload(bufnr) + end + + -- in case of block selection, columns need to be adjusted for non-ASCII characters + -- TODO: handle double-width characters + local bufline + if regtype:byte() == 22 then + bufline = vim.api.nvim_buf_get_lines(bufnr, pos1[1], pos1[1] + 1, true)[1] + pos1[2] = vim.str_utfindex(bufline, pos1[2]) + end + + local region = {} + for l = pos1[1], pos2[1] do + local c1, c2 + if regtype:byte() == 22 then -- block selection: take width from regtype + c1 = pos1[2] + c2 = c1 + regtype:sub(2) + -- and adjust for non-ASCII characters + bufline = vim.api.nvim_buf_get_lines(bufnr, l, l + 1, true)[1] + if c1 < #bufline then + c1 = vim.str_byteindex(bufline, c1) + end + if c2 < #bufline then + c2 = vim.str_byteindex(bufline, c2) + end + else + c1 = (l == pos1[1]) and (pos1[2]) or 0 + c2 = (l == pos2[1]) and (pos2[2] + (inclusive and 1 or 0)) or -1 + end + table.insert(region, l, {c1, c2}) + end + return region +end + +--- Defers calling `fn` until `timeout` ms passes. +--- +--- Use to do a one-shot timer that calls `fn` +--@param fn Callback to call once `timeout` expires +--@param timeout Number of milliseconds to wait before calling `fn` +--@return timer luv timer object +function vim.defer_fn(fn, timeout) + vim.validate { fn = { fn, 'c', true}; } + local timer = vim.loop.new_timer() + timer:start(timeout, 0, vim.schedule_wrap(function() + timer:stop() + timer:close() + + fn() + end)) + + return timer +end + return module diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua index 46ae56955b..2ea51e7b0b 100644 --- a/test/functional/lua/vim_spec.lua +++ b/test/functional/lua/vim_spec.lua @@ -1046,4 +1046,24 @@ describe('lua stdlib', function() eq({}, exec_lua[[return {re1:match_line(0, 1, 1, 7)}]]) eq({0,3}, exec_lua[[return {re1:match_line(0, 1, 0, 7)}]]) end) -end) + + it('vim.defer_fn', function() + exec_lua [[ + vim.g.test = 0 + vim.defer_fn(function() vim.g.test = 1 end, 10) + ]] + eq(0, exec_lua[[return vim.g.test]]) + exec_lua [[vim.cmd("sleep 10m")]] + eq(1, exec_lua[[return vim.g.test]]) + end) + + it('vim.region', function() + helpers.insert(helpers.dedent( [[ + text tααt tααt text + text tαxt txtα tex + text tαxt tαxt + ]])) + eq({5,15}, exec_lua[[ return vim.region(0,{1,5},{1,14},'v',true)[1] ]]) + end) + + end) |