├── .gitignore ├── LICENSE ├── lua ├── nvim-ultivisual │ ├── utils │ │ ├── copy.lua │ │ ├── delete.lua │ │ ├── brackets.lua │ │ ├── indent.lua │ │ └── paste.lua │ ├── string.lua │ └── configs.lua └── nvim-ultivisual.lua ├── README.md └── doc └── nvim-ultivisual.txt /.gitignore: -------------------------------------------------------------------------------- 1 | doc/tags 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Aru 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /lua/nvim-ultivisual/utils/copy.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | - NOTE - copy.lua 3 | - Author - Aru 4 | - 5 | - Created - 2024.07.29 6 | - Github - https://github.com/aruyu 7 | - Contact - vine9151@gmail.com 8 | ]] 9 | 10 | 11 | 12 | require("nvim-ultivisual.string") 13 | 14 | local M = {} 15 | local keyset = vim.keymap.set 16 | local noremap_opt = { noremap = true } 17 | local t = function(str) 18 | return vim.api.nvim_replace_termcodes(str, true, true, true) 19 | end 20 | 21 | 22 | function M.do_visual_copy_cut(feature) 23 | local present_column = vim.fn.col('.') 24 | local present_word = vim.fn.getline('.'):sub(present_column, present_column) 25 | local commands 26 | 27 | if feature == 'cut' then 28 | commands = 'd' 29 | elseif feature == 'copy' then 30 | commands = 'y' 31 | end 32 | 33 | if (present_word == '') and (present_column ~= 1) then 34 | vim.api.nvim_feedkeys(t(''), 'n', true) 35 | vim.api.nvim_feedkeys(t(commands .. ''), 'n', true) 36 | else 37 | vim.api.nvim_feedkeys(t(commands .. ''), 'n', true) 38 | end 39 | end 40 | 41 | 42 | function M.set_keymaps(keymaps, copy_cut) 43 | keyset('v', keymaps.key, 44 | 'lua require("nvim-ultivisual.utils.copy").do_visual_copy_cut("' .. copy_cut .. '")', 45 | noremap_opt 46 | ) 47 | end 48 | 49 | return M 50 | -------------------------------------------------------------------------------- /lua/nvim-ultivisual.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | - NOTE - nvim-ultivisual.lua 3 | - Author - Aru 4 | - 5 | - Created - 2023.02.27 6 | - Github - https://github.com/aruyu 7 | - Contact - vine9151@gmail.com 8 | ]] 9 | 10 | 11 | 12 | local configs = require("nvim-ultivisual.configs") 13 | local indent_utils = require("nvim-ultivisual.utils.indent") 14 | local brackets_utils = require("nvim-ultivisual.utils.brackets") 15 | local paste_utils = require("nvim-ultivisual.utils.paste") 16 | local copy_utils = require("nvim-ultivisual.utils.copy") 17 | local delete_utils = require("nvim-ultivisual.utils.delete") 18 | local M = {} 19 | 20 | 21 | function M.setup(opt) 22 | configs.setup_config(opt) 23 | 24 | if configs.features.indent == true then 25 | indent_utils.set_keymaps(configs.keymaps.make_indent, configs.keymaps.delete_indent) 26 | end 27 | if configs.features.brackets == true then 28 | brackets_utils.set_keymaps(configs.keymaps.brackets, configs.options.brackets) 29 | end 30 | if configs.features.paste == true then 31 | paste_utils.set_keymaps(configs.keymaps.paste) 32 | end 33 | if configs.features.copy == true then 34 | copy_utils.set_keymaps(configs.keymaps.copy, 'copy') 35 | end 36 | if configs.features.cut == true then 37 | copy_utils.set_keymaps(configs.keymaps.cut, 'cut') 38 | end 39 | if configs.features.delete == true then 40 | delete_utils.set_keymaps(configs.keymaps.delete) 41 | end 42 | end 43 | 44 | return M 45 | -------------------------------------------------------------------------------- /lua/nvim-ultivisual/string.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | - NOTE - string.lua 3 | - Author - Aru 4 | - 5 | - Created - 2023.02.27 6 | - Github - https://github.com/aruyu 7 | - Contact - vine9151@gmail.com 8 | ]] 9 | 10 | 11 | 12 | function string:contains(sub) 13 | return self:find(sub, 1, true) ~= nil 14 | end 15 | 16 | function string:startswith(start) 17 | return self:sub(1, #start) == start 18 | end 19 | 20 | function string:endswith(ending) 21 | return ending == "" or self:sub(-#ending) == ending 22 | end 23 | 24 | function string:split(delimiter) 25 | local retval = {} 26 | local from = 1 27 | local delim_from, delim_to = self:find(delimiter, from) 28 | while delim_from do 29 | table.insert(retval, self:sub(from , delim_from-1)) 30 | from = delim_to + 1 31 | delim_from, delim_to = self:find(delimiter, from) 32 | end 33 | table.insert(retval, self:sub(from)) 34 | return retval 35 | end 36 | 37 | function string:replace(old, new) 38 | local postfix, retval 39 | local search_start_idx = 1 40 | 41 | while true do 42 | local start_idx, end_idx = self:find(old, search_start_idx, true) 43 | if (not start_idx) then 44 | break 45 | end 46 | 47 | postfix = self:sub(end_idx + 1) 48 | retval = self:sub(1, (start_idx - 1)) .. new .. postfix 49 | 50 | search_start_idx = -1 * postfix:len() 51 | end 52 | 53 | return retval 54 | end 55 | 56 | function string:replace_once(old, new) 57 | local postfix, retval 58 | 59 | local start_idx, end_idx = self:find(old, 1, true) 60 | postfix = self:sub(end_idx + 1) 61 | retval = self:sub(1, (start_idx - 1)) .. new .. postfix 62 | 63 | return retval 64 | end 65 | 66 | function string:insert(pos, text) 67 | return self:sub(1, pos - 1) .. text .. self:sub(pos) 68 | end 69 | -------------------------------------------------------------------------------- /lua/nvim-ultivisual/utils/delete.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | - NOTE - delete.lua 3 | - Author - Aru 4 | - 5 | - Created - 2024.07.29 6 | - Github - https://github.com/aruyu 7 | - Contact - vine9151@gmail.com 8 | ]] 9 | 10 | 11 | 12 | require("nvim-ultivisual.string") 13 | 14 | local M = {} 15 | local keyset = vim.keymap.set 16 | local noremap_opt = { noremap = true } 17 | local t = function(str) 18 | return vim.api.nvim_replace_termcodes(str, true, true, true) 19 | end 20 | 21 | 22 | local function getword(index) 23 | return vim.fn.getline('.'):sub(vim.fn.col('.') - index, vim.fn.col('.') - index) 24 | end 25 | 26 | function M.do_delete() 27 | local present_column = vim.fn.col('.') 28 | 29 | if present_column ~= 1 then 30 | if getword(0) ~= '' then 31 | if getword(1) == ' ' and getword(2) == ' ' then 32 | vim.api.nvim_feedkeys(t('v'), 'n', true) 33 | vim.api.nvim_feedkeys(t(''), 'n', true) 34 | vim.api.nvim_feedkeys(t('di'), 'n', true) 35 | 36 | elseif getword(1) == ' ' or getword(1) == ' ' then 37 | vim.api.nvim_feedkeys(t('vdi'), 'n', true) 38 | 39 | else 40 | vim.api.nvim_feedkeys(t('vbdi'), 'n', true) 41 | end 42 | 43 | else 44 | if getword(1) == ' ' and getword(2) == ' ' then 45 | vim.api.nvim_feedkeys(t('v'), 'n', true) 46 | vim.api.nvim_feedkeys(t(''), 'n', true) 47 | vim.api.nvim_feedkeys(t('da'), 'n', true) 48 | 49 | elseif getword(1) == ' ' or getword(1) == ' ' then 50 | vim.api.nvim_feedkeys(t('vda'), 'n', true) 51 | 52 | else 53 | vim.api.nvim_feedkeys(t('vbda'), 'n', true) 54 | end 55 | end 56 | end 57 | end 58 | 59 | 60 | function M.set_keymaps(keymaps) 61 | for _, key in ipairs(keymaps.key) do 62 | keyset('i', key, 63 | 'lua require("nvim-ultivisual.utils.delete").do_delete()', 64 | noremap_opt 65 | ) 66 | end 67 | end 68 | 69 | return M 70 | -------------------------------------------------------------------------------- /lua/nvim-ultivisual/configs.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | - NOTE - configs.lua 3 | - Author - Aru 4 | - 5 | - Created - 2023.02.27 6 | - Github - https://github.com/aruyu 7 | - Contact - vine9151@gmail.com 8 | ]] 9 | 10 | 11 | 12 | local M = {} 13 | 14 | -- Default options. 15 | local default_options = { 16 | -- You can switch the ultimate visual features. 17 | features = { 18 | indent = true, 19 | brackets = true, 20 | paste = true, 21 | copy = true, 22 | cut = true, 23 | delete = true, 24 | }, 25 | 26 | -- The keymap settings for features here. 27 | -- key; mapped key 28 | -- modes; included mode (include Visual mode for ultimate features!) 29 | keymaps = { 30 | make_indent = { 31 | key = '', 32 | modes = { 'v','n' }, -- 'v'; visual, 'n'; normal, 'i'; insert 33 | }, 34 | 35 | delete_indent = { 36 | key = '', 37 | modes = { 'v','n' }, -- 'v'; visual, 'n'; normal, 'i'; insert 38 | }, 39 | 40 | brackets = { 41 | -- Only for 'v'; visual mode. 42 | -- You can config the brackets' shape whatever you want. 43 | { key = '(', shapes = { '(',')' } }, 44 | { key = '{', shapes = { '{','}' } }, 45 | }, 46 | 47 | paste = { 48 | key = '', 49 | modes = { 'v','n','i' }, -- 'v'; visual, 'n'; normal, 'i'; insert 50 | }, 51 | 52 | copy = { 53 | -- Only for 'v'; visual mode. 54 | key = '', 55 | }, 56 | 57 | cut = { 58 | -- Only for 'v'; visual mode. 59 | key = '', 60 | }, 61 | 62 | delete = { 63 | -- Only for 'i'; insert mode. 64 | key = { '', '', '' }, 65 | }, 66 | }, 67 | 68 | -- The option settings for features here. 69 | options = { 70 | brackets = { 71 | -- Ignore the first line's indent when included while making brackets. 72 | ignore_indent = true, 73 | }, 74 | }, 75 | } 76 | 77 | 78 | M.values = vim.deepcopy(default_options) 79 | 80 | function M.setup_config(user_options) 81 | M.values = vim.tbl_extend('force', default_options, user_options or {}) 82 | end 83 | 84 | -- This metatable allows for easier access to the config values. Instead of 85 | -- writing `config.values.key` you can just write `config.key`. 86 | return setmetatable(M, { 87 | __index = function(t, key) 88 | if key == "setup_config" then 89 | return t.setup_config 90 | else 91 | return t.values[key] 92 | end 93 | end, 94 | }) 95 | -------------------------------------------------------------------------------- /lua/nvim-ultivisual/utils/brackets.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | - NOTE - brackets.lua 3 | - Author - Aru 4 | - 5 | - Created - 2023.02.27 6 | - Github - https://github.com/aruyu 7 | - Contact - vine9151@gmail.com 8 | ]] 9 | 10 | 11 | 12 | require("nvim-ultivisual.string") 13 | 14 | local M = {} 15 | local keyset = vim.keymap.set 16 | local noremap_opt = { noremap = true } 17 | 18 | 19 | function M.make_group_brackets(front_character, back_character, options) 20 | options = options or false 21 | 22 | local line_start = vim.fn.line("'<") 23 | local line_end = vim.fn.line("'>") 24 | local col_start = vim.fn.col("'<") 25 | local col_end = vim.fn.col("'>") 26 | 27 | local front_line, selected_line, selected_start_line, back_line, added_char 28 | 29 | if col_start == 1 and options then 30 | local is_expandtab = vim.api.nvim_eval('&expandtab') == 1 31 | local column, indent_size, indent_style, indent_charactor 32 | 33 | if is_expandtab then 34 | indent_style = '%s+' 35 | indent_charactor = ' ' 36 | else 37 | indent_style = '\t+' 38 | indent_charactor = ' ' 39 | end 40 | 41 | column, indent_size = string.find(vim.fn.getline(line_start), indent_style) 42 | 43 | if indent_size == nil or column ~= 1 then 44 | front_line = vim.fn.getline(line_start):sub(1, col_start-1) .. front_character 45 | selected_line = vim.fn.getline(line_start):sub(col_start, col_end) 46 | selected_start_line = vim.fn.getline(line_start):sub(col_start) 47 | else 48 | front_line = indent_charactor:rep(indent_size) .. front_character 49 | selected_line = vim.fn.getline(line_start):sub(indent_size+1, col_end) 50 | selected_start_line = vim.fn.getline(line_start):sub(indent_size+1) 51 | end 52 | 53 | else 54 | front_line = vim.fn.getline(line_start):sub(1, col_start-1) .. front_character 55 | selected_line = vim.fn.getline(line_start):sub(col_start, col_end) 56 | selected_start_line = vim.fn.getline(line_start):sub(col_start) 57 | end 58 | 59 | back_line = back_character .. vim.fn.getline(line_end):sub(col_end+1) 60 | 61 | if line_start == line_end then 62 | front_line = front_line .. selected_line 63 | 64 | vim.fn.setline(line_end, front_line .. back_line) 65 | added_char = 2 66 | 67 | else 68 | front_line = front_line .. selected_start_line 69 | back_line = vim.fn.getline(line_end):sub(1, col_end) .. back_line 70 | 71 | vim.fn.setline(line_start, front_line) 72 | vim.fn.setline(line_end, back_line) 73 | added_char = 1 74 | end 75 | 76 | vim.api.nvim_call_function('cursor', { line_end,col_end+(added_char+1) }) 77 | end 78 | 79 | 80 | function M.set_keymaps(keymaps, options) 81 | if options.ignore_indent == false then 82 | for _, keymap in ipairs(keymaps) do 83 | keyset('v', keymap.key, 84 | '::lua require("nvim-ultivisual.utils.brackets").make_group_brackets("' .. 85 | keymap.shapes[1] .. '","' .. keymap.shapes[2] .. '")', 86 | noremap_opt 87 | ) 88 | end 89 | else 90 | for _, keymap in ipairs(keymaps) do 91 | keyset('v', keymap.key, 92 | '::lua require("nvim-ultivisual.utils.brackets").make_group_brackets("' .. 93 | keymap.shapes[1] .. '","' .. keymap.shapes[2] .. '",true)', 94 | noremap_opt 95 | ) 96 | end 97 | end 98 | end 99 | 100 | return M 101 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Neovim Ultivisual 2 | 3 | [![Lua](https://img.shields.io/badge/Lua-blue.svg?style=for-the-badge&logo=lua)](http://www.lua.org) 4 | [![Neovim](https://img.shields.io/badge/Neovim-green.svg?style=for-the-badge&logo=neovim)](https://neovim.io) 5 | 6 |
7 | 8 | ```css 9 | __ ______ _ _ __ 10 | / / / / / /_(_) __(_)______ ______ _/ / 11 | / / / / / __/ / | / / / ___/ / / / __ `/ / 12 | / /_/ / / /_/ /| |/ / (__ ) /_/ / /_/ / / 13 | \____/_/\__/_/ |___/_/____/\__,_/\__,_/_/ 14 | ``` 15 | 16 | ### The Ultimate visual mode plugin for neovim written by Lua Script 17 | 18 | ![main](https://user-images.githubusercontent.com/75081360/223333562-0f954e54-44be-450b-acac-9396261e925b.gif) 19 | 20 |
21 | 22 | ## Features 23 | 24 | 1. Ultimate **indent** making. 25 | - Whatever much times you wrote spaces, ***Ultivisual*** would make indent accurately. 26 | - As your *'shiftwidth'* setting, the plugin would change all the selected lines. ***(change accurately according to 'shiftwidth')*** 27 | - Also noexpandtab supported. 28 | - You can repeat to make indent as long as you don't deselect the line! 29 | 30 | https://user-images.githubusercontent.com/75081360/223304085-dff6c9e2-145d-4975-ace3-e4f727da0056.mp4 31 | 32 | 2. Ultimate **brackets** making. 33 | - Just select the line by ***visual mode***, and push key to brackets the selected line! 34 | - You can set the pair of ***brackets' shape*** whatever you want. 35 | - Optionally, you can set to ignore the indent of the start line. 36 | - At last, the cursor would automatically move to ***the end of the brackets***. 37 | 38 | https://user-images.githubusercontent.com/75081360/223304118-abfd88f1-f854-4a3c-9291-c2807c24599e.mp4 39 | 40 | 3. Ultimate yank **paste**. 41 | - This feature makes that you can paste copied lines as you ***intended***. 42 | - At last, the cursor would automatically move to ***the end of the pasted line***. 43 | 44 | https://user-images.githubusercontent.com/75081360/223304150-fc7abeb8-c150-40dc-bdfb-11e6bf540278.mp4 45 | 46 | ## Installation 47 | 48 | ### [packer.nvim](https://github.com/wbthomason/packer.nvim) 49 | 50 | ```lua 51 | use { 52 | 'aruyu/nvim-ultivisual', 53 | config = function() require("nvim-ultivisual").setup() end, 54 | } 55 | ``` 56 | 57 | ### [vim-plug](https://github.com/junegunn/vim-plug) 58 | 59 | ```vim 60 | Plug 'aruyu/nvim-ultivisual' 61 | lua << EOF 62 | require("nvim-ultivisual").setup() 63 | EOF 64 | ``` 65 | 66 | ## Configuration 67 | 68 | You can setup the features by the following options. 69 | 70 | ```lua 71 | -- Default options. 72 | require("nvim-ultivisual").setup({ 73 | -- You can switch the ultimate visual features. 74 | features = { 75 | indent = true, 76 | brackets = true, 77 | paste = true, 78 | copy = true, 79 | cut = true, 80 | delete = true, 81 | }, 82 | 83 | -- The keymap settings for features here. 84 | -- key; mapped key 85 | -- modes; included mode (include Visual mode for ultimate features!) 86 | keymaps = { 87 | make_indent = { 88 | key = '', 89 | modes = { 'v','n' }, -- 'v'; visual, 'n'; normal, 'i'; insert 90 | }, 91 | 92 | delete_indent = { 93 | key = '', 94 | modes = { 'v','n' }, -- 'v'; visual, 'n'; normal, 'i'; insert 95 | }, 96 | 97 | brackets = { 98 | -- Only for 'v'; visual mode. 99 | -- You can config the brackets' shape whatever you want. 100 | { key = '(', shapes = { '(',')' } }, 101 | { key = '{', shapes = { '{','}' } }, 102 | }, 103 | 104 | paste = { 105 | key = '', 106 | modes = { 'v','n','i' }, -- 'v'; visual, 'n'; normal, 'i'; insert 107 | }, 108 | 109 | copy = { 110 | -- Only for 'v'; visual mode. 111 | key = '', 112 | }, 113 | 114 | cut = { 115 | -- Only for 'v'; visual mode. 116 | key = '', 117 | }, 118 | 119 | delete = { 120 | -- Only for 'i'; insert mode. 121 | key = { '', '', '' }, 122 | }, 123 | }, 124 | 125 | -- The option settings for features here. 126 | options = { 127 | brackets = { 128 | -- Ignore the first line's indent when included while making brackets. 129 | ignore_indent = true, 130 | }, 131 | }, 132 | }) 133 | ``` 134 | 135 | ## Contributors 136 | 137 | > Always be welcomed all the pull requests! :D 138 | -------------------------------------------------------------------------------- /lua/nvim-ultivisual/utils/indent.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | - NOTE - indent.lua 3 | - Author - Aru 4 | - 5 | - Created - 2023.02.27 6 | - Github - https://github.com/aruyu 7 | - Contact - vine9151@gmail.com 8 | ]] 9 | 10 | 11 | 12 | require("nvim-ultivisual.string") 13 | 14 | local M = {} 15 | local keyset = vim.keymap.set 16 | local noremap_opt = { noremap = true } 17 | local t = function(str) 18 | return vim.api.nvim_replace_termcodes(str, true, true, true) 19 | end 20 | 21 | 22 | function M.make_group_indent(novisual) 23 | novisual = novisual or false 24 | 25 | local is_expandtab = vim.api.nvim_eval('&expandtab') == 1 26 | local indent_size = vim.api.nvim_eval('&shiftwidth') 27 | local temp, indent_count, indent_diff 28 | 29 | local select_line_start = vim.fn.line("'<") 30 | local select_line_end = vim.fn.line("'>") 31 | local select_line_diff = select_line_end - select_line_start 32 | 33 | for line = select_line_start, select_line_end, 1 do 34 | temp, indent_count = string.find(vim.fn.getline(line), '%s+') 35 | 36 | if indent_count == nil or temp ~= 1 then 37 | indent_diff = 0 38 | else 39 | indent_diff = indent_size - (indent_count % indent_size) 40 | end 41 | 42 | if is_expandtab then 43 | if indent_diff == 0 then 44 | vim.fn.setline(line, string.rep(' ', indent_size) .. vim.fn.getline(line)) 45 | else 46 | vim.fn.setline(line, string.rep(' ', indent_diff) .. vim.fn.getline(line)) 47 | end 48 | else 49 | vim.fn.setline(line, ' ' .. vim.fn.getline(line)) 50 | end 51 | 52 | if vim.fn.getline(line) == string.rep(' ', indent_size) or vim.fn.getline(line) == ' ' then 53 | vim.fn.setline(line, '') 54 | end 55 | end 56 | 57 | if novisual == false then 58 | if select_line_diff ~= 0 then 59 | vim.api.nvim_feedkeys(t(''.. select_line_diff .. '-'), 'n', true) 60 | else 61 | vim.api.nvim_feedkeys(t(''), 'n', true) 62 | end 63 | else 64 | vim.api.nvim_feedkeys(t(''), 'n', true) 65 | end 66 | end 67 | 68 | function M.delete_group_indent(novisual) 69 | novisual = novisual or false 70 | 71 | local is_expandtab = vim.api.nvim_eval('&expandtab') == 1 72 | local indent_size = vim.api.nvim_eval('&shiftwidth') 73 | 74 | local select_line_start = vim.fn.line("'<") 75 | local select_line_end = vim.fn.line("'>") 76 | local select_line_diff = select_line_end - select_line_start 77 | 78 | for line = select_line_start, select_line_end, 1 do 79 | if is_expandtab then 80 | if vim.fn.getline(line):startswith(string.rep(' ', indent_size)) then 81 | vim.fn.setline(line, vim.fn.getline(line):replace_once(string.rep(' ', indent_size), '')) 82 | end 83 | 84 | else 85 | if vim.fn.getline(line):startswith(' ') then 86 | vim.fn.setline(line, vim.fn.getline(line):replace_once(' ', '')) 87 | end 88 | end 89 | end 90 | 91 | if novisual == false then 92 | if select_line_diff ~= 0 then 93 | vim.api.nvim_feedkeys(t(''.. select_line_diff .. '-'), 'n', true) 94 | else 95 | vim.api.nvim_feedkeys(t(''), 'n', true) 96 | end 97 | else 98 | vim.api.nvim_feedkeys(t(''), 'n', true) 99 | end 100 | end 101 | 102 | 103 | function M.set_keymaps(make_keymaps, delete_keymaps) 104 | for _, mode in ipairs(make_keymaps.modes) do 105 | if mode == 'v' then 106 | keyset('v', make_keymaps.key, 107 | '::lua require("nvim-ultivisual.utils.indent").make_group_indent()', 108 | noremap_opt 109 | ) 110 | elseif mode == 'n' then 111 | keyset('n', make_keymaps.key, 112 | 'v::lua require("nvim-ultivisual.utils.indent").make_group_indent()', 113 | noremap_opt 114 | ) 115 | elseif mode == 'i' then 116 | keyset('i', make_keymaps.key, 117 | 'v::lua require("nvim-ultivisual.utils.indent").make_group_indent(true)', 118 | noremap_opt 119 | ) 120 | end 121 | end 122 | 123 | for _, mode in ipairs(delete_keymaps.modes) do 124 | if mode == 'v' then 125 | keyset('v', delete_keymaps.key, 126 | '::lua require("nvim-ultivisual.utils.indent").delete_group_indent()', 127 | noremap_opt 128 | ) 129 | elseif mode == 'n' then 130 | keyset('n', delete_keymaps.key, 131 | 'v::lua require("nvim-ultivisual.utils.indent").delete_group_indent()', 132 | noremap_opt 133 | ) 134 | elseif mode == 'i' then 135 | keyset('i', delete_keymaps.key, 136 | 'v::lua require("nvim-ultivisual.utils.indent").delete_group_indent(true)', 137 | noremap_opt 138 | ) 139 | end 140 | end 141 | end 142 | 143 | return M 144 | -------------------------------------------------------------------------------- /lua/nvim-ultivisual/utils/paste.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | - NOTE - paste.lua 3 | - Author - Aru 4 | - 5 | - Created - 2023.02.27 6 | - Github - https://github.com/aruyu 7 | - Contact - vine9151@gmail.com 8 | ]] 9 | 10 | 11 | 12 | require("nvim-ultivisual.string") 13 | 14 | local M = {} 15 | local keyset = vim.keymap.set 16 | local noremap_opt = { noremap = true } 17 | 18 | 19 | function M.do_visual_paste() 20 | local line_start = vim.fn.line("'<") 21 | local line_end = vim.fn.line("'>") 22 | local col_start = vim.fn.col("'<") 23 | local col_end = vim.fn.col("'>") 24 | local yanked = vim.fn.getreg('"'):split('\n') 25 | 26 | local front_line, back_line 27 | local lines_table = {} 28 | local cursor_pos = { (line_start-1)+(#yanked) } 29 | 30 | front_line = vim.fn.getline(line_start):sub(1, col_start-1) 31 | back_line = vim.fn.getline(line_end):sub(col_end+1) 32 | 33 | if #yanked == 1 then 34 | front_line = front_line .. yanked[1] 35 | table.insert(cursor_pos, front_line:len()+1) 36 | table.insert(lines_table, front_line .. back_line) 37 | 38 | else 39 | front_line = front_line .. yanked[1] 40 | back_line = yanked[#yanked] .. back_line 41 | table.insert(cursor_pos, yanked[#yanked]:len()+1) 42 | 43 | lines_table = yanked 44 | table.remove(lines_table, 1) 45 | table.remove(lines_table, #yanked) 46 | table.insert(lines_table, 1, front_line) 47 | table.insert(lines_table, back_line) 48 | end 49 | 50 | vim.api.nvim_buf_set_lines(0, line_start-1, line_end, true, lines_table) 51 | vim.api.nvim_call_function('cursor', cursor_pos) 52 | end 53 | 54 | function M.do_normal_paste() 55 | local present_column = vim.fn.col('.') 56 | local present_line = vim.fn.line('.') 57 | local yanked = vim.fn.getreg('"'):split('\n') 58 | 59 | local front, back 60 | local lines_table = {} 61 | local cursor_pos = { (present_line-1)+(#yanked) } 62 | 63 | if present_column == 1 then 64 | front = vim.fn.getline(present_line):sub(1, present_column-1) 65 | back = vim.fn.getline(present_line):sub(present_column) 66 | else 67 | front = vim.fn.getline(present_line):sub(1, present_column) 68 | back = vim.fn.getline(present_line):sub(present_column+1) 69 | end 70 | 71 | if #yanked == 1 then 72 | front = front .. yanked[1] 73 | table.insert(cursor_pos, front:len()) 74 | table.insert(lines_table, front .. back) 75 | 76 | else 77 | front = front .. yanked[1] 78 | back = yanked[#yanked] .. back 79 | table.insert(cursor_pos, yanked[#yanked]:len()) 80 | 81 | lines_table = yanked 82 | table.remove(lines_table, 1) 83 | table.remove(lines_table, #yanked) 84 | table.insert(lines_table, 1, front) 85 | table.insert(lines_table, back) 86 | end 87 | 88 | vim.api.nvim_buf_set_lines(0, present_line-1, present_line, true, lines_table) 89 | vim.api.nvim_call_function('cursor', cursor_pos) 90 | end 91 | 92 | function M.do_insert_paste() 93 | local present_column = vim.fn.col('.') 94 | local present_line = vim.fn.line('.') 95 | local yanked = vim.fn.getreg('"'):split('\n') 96 | 97 | local front, back 98 | local lines_table = {} 99 | local cursor_pos = { (present_line-1)+(#yanked) } 100 | 101 | front = vim.fn.getline(present_line):sub(1, present_column-1) 102 | back = vim.fn.getline(present_line):sub(present_column) 103 | 104 | if #yanked == 1 then 105 | front = front .. yanked[1] 106 | table.insert(cursor_pos, front:len()+1) 107 | table.insert(lines_table, front .. back) 108 | 109 | else 110 | front = front .. yanked[1] 111 | back = yanked[#yanked] .. back 112 | table.insert(cursor_pos, yanked[#yanked]:len()+1) 113 | 114 | lines_table = yanked 115 | table.remove(lines_table, 1) 116 | table.remove(lines_table, #yanked) 117 | table.insert(lines_table, 1, front) 118 | table.insert(lines_table, back) 119 | end 120 | 121 | vim.api.nvim_buf_set_lines(0, present_line-1, present_line, true, lines_table) 122 | vim.api.nvim_call_function('cursor', cursor_pos) 123 | end 124 | 125 | 126 | function M.set_keymaps(keymaps) 127 | for _, mode in ipairs(keymaps.modes) do 128 | if mode == 'v' then 129 | keyset('v', keymaps.key, 130 | '::lua require("nvim-ultivisual.utils.paste").do_visual_paste()', 131 | noremap_opt 132 | ) 133 | elseif mode == 'n' then 134 | keyset('n', keymaps.key, 135 | 'lua require("nvim-ultivisual.utils.paste").do_normal_paste()', 136 | noremap_opt 137 | ) 138 | elseif mode == 'i' then 139 | keyset('i', keymaps.key, 140 | 'lua require("nvim-ultivisual.utils.paste").do_insert_paste()', 141 | noremap_opt 142 | ) 143 | end 144 | end 145 | end 146 | 147 | return M 148 | -------------------------------------------------------------------------------- /doc/nvim-ultivisual.txt: -------------------------------------------------------------------------------- 1 | *nvim-ultivisual* The Ultimate visual mode plugin written by Lua Script 2 | 3 | __ ______ _ _ __ 4 | / / / / / /_(_) __(_)______ ______ _/ / 5 | / / / / / __/ / | / / / ___/ / / / __ `/ / 6 | / /_/ / / /_/ /| |/ / (__ ) /_/ / /_/ / / 7 | \____/_/\__/_/ |___/_/____/\__,_/\__,_/_/ 8 | 9 | ================================================================================ 10 | Contents *nvim-ultivisual-contents* 11 | 12 | Contents....................................... |nvim-ultivisual-contents| 13 | Introduction................................... |nvim-ultivisual-introduction| 14 | Setup.......................................... |nvim-ultivisual-setup| 15 | License........................................ |nvim-ultivisual-license| 16 | 17 | ================================================================================ 18 | Introduction *nvim-ultivisual-introduction* 19 | 20 | This plugin makes convenient to use visual mode for neovim editor. 21 | The features are in below. 22 | 23 | 1. Ultimate indent making. 24 | - Make indent accurately. 25 | - Also noexpandtab supported. 26 | - You can repeat to make indent as long as you don't deselect the line! 27 | 28 | 2. Ultimate brackets making. 29 | - You can set the pair of brackets' shape whatever you want. 30 | - Optionally, you can set to ignore the indent of the start line. 31 | - Move the cursor to the end of the brackets. 32 | 33 | 3. Ultimate yank paste. 34 | - This feature makes that you can paste copied lines as you intended. 35 | - Move the cursor to the end of the pasted line. 36 | 37 | ================================================================================ 38 | Setup *nvim-ultivisual-setup* 39 | 40 | To use nvim-ultivisual plugin, you have to call the setup function. 41 | The code in below is the default options. 42 | 43 | Example: 44 | > 45 | -- Default options. 46 | require("nvim-ultivisual").setup({ 47 | features = { 48 | indent = true, 49 | brackets = true, 50 | paste = true, 51 | copy = true, 52 | cut = true, 53 | delete = true, 54 | }, 55 | 56 | keymaps = { 57 | make_indent = { 58 | key = '', 59 | modes = { 'v','n' }, 60 | }, 61 | 62 | delete_indent = { 63 | key = '', 64 | modes = { 'v','n' }, 65 | }, 66 | 67 | brackets = { 68 | { key = '(', shapes = { '(',')' } }, 69 | { key = '{', shapes = { '{','}' } }, 70 | }, 71 | 72 | paste = { 73 | key = '', 74 | modes = { 'v','n','i' }, 75 | }, 76 | 77 | copy = { 78 | key = '', 79 | }, 80 | 81 | cut = { 82 | key = '', 83 | }, 84 | 85 | delete = { 86 | key = { '', '', '' }, 87 | }, 88 | }, 89 | 90 | options = { 91 | brackets = { 92 | ignore_indent = true, 93 | }, 94 | }, 95 | }) 96 | < 97 | 98 | *nvim-ultivisual.features* 99 | Enable or disable the plugin's features. 100 | 101 | *nvim-ultivisual.features.indent* 102 | Switch whether use ultivisual indent or not. 103 | Type: `boolean`, Default: `true` 104 | 105 | *nvim-ultivisual.features.brackets* 106 | Switch whether use ultivisual brackets or not. 107 | Type: `boolean`, Default: `true` 108 | 109 | *nvim-ultivisual.features.paste* 110 | Switch whether use ultivisual paste or not. 111 | Type: `boolean`, Default: `true` 112 | 113 | *nvim-ultivisual.keymaps* 114 | Map the keys and specific modes for ultivisual features. 115 | 116 | *nvim-ultivisual.keymaps.make_indent* 117 | Make indent accurately when you push what you mapped in specific modes. 118 | (only if |features.indent| is `true`) 119 | 120 | This process would make indent all the selected line as `shiftwidth` size. 121 | > 122 | vim.opt.shiftwidth = 2 123 | or 124 | set shiftwidth=2 125 | < 126 | - 1 space would be changed to 2 spaces! 127 | 128 | > 129 | vim.opt.shiftwidth = 4 130 | or 131 | set shiftwidth 4 132 | < 133 | - 1~3 space would be changed to 4 spaces! 134 | 135 | *nvim-ultivisual.keymaps.make_indent.key* 136 | Set the key for making indent feature. 137 | Type: `string`, Default: `''` 138 | 139 | *nvim-ultivisual.keymaps.make_indent.modes* 140 | Specify the modes for making indent feature. 141 | Type: `{string}`, Default: `{ 'v','n' }` 142 | 143 | *nvim-ultivisual.keymaps.delete_indent* 144 | Delete indent accurately when you push what you mapped in specific modes. 145 | (only if |features.indent| is `true`) 146 | 147 | *nvim-ultivisual.keymaps.delete_indent.key* 148 | Set the key for deleting indent feature. 149 | Type: `string`, Default: `''` 150 | 151 | *nvim-ultivisual.keymaps.delete_indent.modes* 152 | Specify the modes for deleting indent feature. 153 | Type: `{string}`, Default: `{ 'v','n' }` 154 | 155 | *nvim-ultivisual.keymaps.brackets* 156 | Make brackets accurately when you push what you mapped in specific modes. 157 | (only if |features.brackets| is `true`) 158 | Type: `table`, Default: 159 | `{` 160 | `{ key = '(', shapes = { '(',')' } },` 161 | `{ key = '{', shapes = { '{','}' } },` 162 | `}` 163 | - The table needs |key| which includes one arguments. 164 | - The table needs |shapes| which includes table set by two arguments. 165 | - You can set the brackets' shape whatever you want to use. 166 | 167 | *nvim-ultivisual.keymaps.paste* 168 | Paste accurately when you push what you mapped in specific modes. 169 | (only if |features.paste| is `true`) 170 | 171 | *nvim-ultivisual.keymaps.paste.key* 172 | Set the key for paste yanked line(s). 173 | Type: `string`, Default: `''` 174 | 175 | *nvim-ultivisual.keymaps.paste.modes* 176 | Specify the modes for paste yanked line(s). 177 | Type: `{string}`, Default: `{ 'v','n','i' }` 178 | 179 | *nvim-ultivisual.options* 180 | *nvim-ultivisual.options.brackets.ignore_indent* 181 | Switch whether ignore all indents or not when you make brackets. 182 | (only if |features.brackets| is `true`) 183 | Type: `boolean`, Default: `true` 184 | 185 | 186 | ================================================================================ 187 | LICENSE *nvim-ultivisual-license* 188 | > 189 | MIT License 190 | 191 | Copyright (c) 2023 Aru 192 | 193 | Permission is hereby granted, free of charge, to any person obtaining a copy 194 | of this software and associated documentation files (the "Software"), to deal 195 | in the Software without restriction, including without limitation the rights 196 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 197 | copies of the Software, and to permit persons to whom the Software is 198 | furnished to do so, subject to the following conditions: 199 | 200 | The above copyright notice and this permission notice shall be included in all 201 | copies or substantial portions of the Software. 202 | 203 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 204 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 205 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 206 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 207 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 208 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 209 | SOFTWARE. 210 | < 211 | 212 | ================================================================================ 213 | 214 | vim:tw=78:ts=8:noet:ft=help:norl: 215 | --------------------------------------------------------------------------------