├── .github
├── issue_template.md
└── workflows
│ └── luarocks.yml
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── autoload
├── vm.vim
└── vm
│ ├── commands.vim
│ ├── comp.vim
│ ├── cursors.vim
│ ├── ecmds1.vim
│ ├── ecmds2.vim
│ ├── edit.vim
│ ├── funcs.vim
│ ├── global.vim
│ ├── icmds.vim
│ ├── insert.vim
│ ├── maps.vim
│ ├── maps
│ └── all.vim
│ ├── operators.vim
│ ├── plugs.vim
│ ├── region.vim
│ ├── search.vim
│ ├── special
│ ├── case.vim
│ └── commands.vim
│ ├── themes.vim
│ ├── variables.vim
│ └── visual.vim
├── doc
├── visual-multi.txt
├── vm-ex-commands.txt
├── vm-faq.txt
├── vm-mappings.txt
├── vm-settings.txt
├── vm-troubleshooting.txt
└── vm-tutorial
├── plugin
└── visual-multi.vim
├── python
└── vm.py
├── run_tests
├── test
├── README.md
├── default
│ └── vimrc.vim
├── requirements.txt
├── test.py
└── tests
│ ├── abbrev
│ ├── commands.py
│ ├── expected_output_file.txt
│ └── input_file.txt
│ ├── alignment
│ ├── commands.py
│ ├── expected_output_file.txt
│ └── input_file.txt
│ ├── backspace
│ ├── commands.py
│ ├── expected_output_file.txt
│ └── input_file.txt
│ ├── change
│ ├── commands.py
│ ├── expected_output_file.txt
│ └── input_file.txt
│ ├── change2
│ ├── commands.py
│ ├── expected_output_file.txt
│ └── input_file.txt
│ ├── cquote
│ ├── commands.py
│ ├── expected_output_file.txt
│ └── input_file.txt
│ ├── curs2
│ ├── commands.py
│ ├── expected_output_file.txt
│ └── input_file.txt
│ ├── curs_del
│ ├── commands.py
│ ├── expected_output_file.txt
│ └── input_file.txt
│ ├── dot
│ ├── commands.py
│ ├── expected_output_file.txt
│ └── input_file.txt
│ ├── example
│ ├── commands.py
│ ├── expected_output_file.txt
│ └── input_file.txt
│ ├── example2
│ ├── commands.py
│ ├── expected_output_file.txt
│ └── input_file.txt
│ ├── getcc
│ ├── commands.py
│ ├── expected_output_file.txt
│ └── input_file.txt
│ ├── oO
│ ├── commands.py
│ ├── expected_output_file.txt
│ ├── input_file.txt
│ └── vimrc.vim
│ ├── pasteatcur
│ ├── commands.py
│ ├── expected_output_file.txt
│ └── input_file.txt
│ ├── regex
│ ├── commands.py
│ ├── expected_output_file.txt
│ └── input_file.txt
│ ├── repl
│ ├── commands.py
│ ├── expected_output_file.txt
│ └── input_file.txt
│ ├── trans
│ ├── commands.py
│ ├── expected_output_file.txt
│ └── input_file.txt
│ └── vmsearch
│ ├── commands.py
│ ├── expected_output_file.txt
│ └── input_file.txt
└── tutorialrc
/.github/issue_template.md:
--------------------------------------------------------------------------------
1 |
9 | **Describe the issue:**
10 |
11 |
12 |
13 | **Steps to reproduce**
14 |
15 | 1.
16 | 2.
17 | 3.
18 |
19 |
33 |
34 | -----
35 |
36 | - **Operating System**:
37 | - **Vim Version**:
38 | - **commit SHA/branch**:
40 |
--------------------------------------------------------------------------------
/.github/workflows/luarocks.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: Push to Luarocks
3 |
4 | on:
5 | push:
6 | tags:
7 | - '*'
8 | release:
9 | types:
10 | - created
11 | tags:
12 | - '*'
13 | workflow_dispatch:
14 |
15 | jobs:
16 | luarocks-upload:
17 | runs-on: ubuntu-22.04
18 | steps:
19 | - uses: actions/checkout@v3
20 | with:
21 | fetch-depth: 0 # Required to count the commits
22 | - name: Get Version
23 | run: echo "LUAROCKS_VERSION=$(git describe --abbrev=0 --tags)" >> $GITHUB_ENV
24 | - name: LuaRocks Upload
25 | uses: nvim-neorocks/luarocks-tag-release@v5
26 | env:
27 | LUAROCKS_API_KEY: ${{ secrets.LUAROCKS_API_KEY }}
28 | with:
29 | version: ${{ env.LUAROCKS_VERSION }}
30 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *vim-bookmarks
2 | .TODO
3 | tags
4 | doc/tags
5 | *.sw*
6 | generated_output_file.txt
7 | test.log
8 | __pycache__
9 | test/.python-version
10 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: python
2 | python: "3.6"
3 | #------------------------------
4 | # install vim from source
5 | #------------------------------
6 | before_install:
7 | - curl https://raw.githubusercontent.com/kana/vim-version-manager/master/bin/vvm | python2 - setup; true
8 | - source ~/.vvm/etc/login
9 | - vvm update_itself
10 | - vvm use vimorg--v8.0.1529 --install --with-features=huge
11 | #------------------------------
12 | install: pip install -r test/requirements.txt
13 | before_script:
14 | - "export DISPLAY=:99.0"
15 | - "sh -e /etc/init.d/xvfb start"
16 | script:
17 | - cd test
18 | - python3 test.py
19 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Gianmaria Bajo (mg1979.git@gmail.com)
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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## vim-visual-multi
2 |
3 | It's called ___vim-visual-multi___ in analogy with _visual-block_, but the plugin works mostly from normal mode.
4 |
5 | Basic usage:
6 |
7 | - select words with Ctrl-N (like `Ctrl-d` in Sublime Text/VS Code)
8 | - create cursors vertically with Ctrl-Down/Ctrl-Up
9 | - select one character at a time with Shift-Arrows
10 | - press n/N to get next/previous occurrence
11 | - press [/] to select next/previous cursor
12 | - press q to skip current and get next occurrence
13 | - press Q to remove current cursor/selection
14 | - start insert mode with i,a,I,A
15 |
16 | Two main modes:
17 |
18 | - in _cursor mode_ commands work as they would in normal mode
19 | - in _extend mode_ commands work as they would in visual mode
20 | - press Tab to switch between «cursor» and «extend» mode
21 |
22 | Most vim commands work as expected (motions, r to replace characters, ~ to change case, etc). Additionally you can:
23 |
24 | - run macros/ex/normal commands at cursors
25 | - align cursors
26 | - transpose selections
27 | - add patterns with regex, or from visual mode
28 |
29 | And more... of course, you can enter insert mode and autocomplete will work.
30 |
31 |
32 | ### Installation
33 |
34 | With vim-plug:
35 |
36 | Plug 'mg979/vim-visual-multi', {'branch': 'master'}
37 |
38 | With Vim 8+:
39 |
40 | mkdir -p ~/.vim/pack/plugins/start && git clone https://github.com/mg979/vim-visual-multi ~/.vim/pack/plugins/start/vim-visual-multi
41 |
42 |
43 | ### Documentation
44 |
45 | :help visual-multi
46 |
47 | For some specific topic it's often:
48 |
49 | :help vm-some-topic
50 |
51 | ### Tutorial
52 |
53 | To run the tutorial:
54 |
55 | vim -Nu path/to/visual-multi/tutorialrc
56 |
57 |
58 | ### [Wiki](https://github.com/mg979/vim-visual-multi/wiki)
59 |
60 | The wiki was the first documentation for the plugin, but many pictures are
61 | outdated and contain wrong mappings. Still, you can take a look.
62 |
63 | You could read at least the [Quick Start](https://github.com/mg979/vim-visual-multi/wiki/Quick-start).
64 |
65 | -------
66 | Some (sometimes very old) random pics:
67 |
68 | -------
69 | Insert mode with autocomplete, alignment (mappings in pic have changed, don't trust them)
70 |
71 | 
72 |
73 | -------
74 | Undo/Redo edits and selections
75 |
76 | 
77 |
78 | -------
79 | Alternate cursor/extend mode, motions (even %), reverse direction (as in visual mode) and extend from the back. At any time you can switch from extend to cursor mode and viceversa.
80 |
81 | 
82 |
83 | -------
84 | Select inside/around brackets/quotes/etc:
85 |
86 | 
87 |
88 | -------
89 | Select operator, here shown with 'wellle/targets.vim' plugin: sib, sia, saa + selection shift
90 |
91 | 
92 |
93 | -------
94 | Synched column transposition
95 |
96 | 
97 |
98 | -------
99 | Unsynched transposition (cycle all regions, also in different lines)
100 |
101 | 
102 |
103 | -------
104 | Shift regions left and right (M-S-\<\>)
105 |
106 | 
107 |
108 | ------
109 | Find words under cursor, add new words (patterns stack), navigate regions, skip them, add regions with regex.
110 |
111 | 
112 |
113 | -------
114 | Normal/Visual/Ex commands at cursors
115 |
116 | 
117 |
118 | -------
119 | Macros. Shorter lines are skipped when adding cursors vertically.
120 |
121 | 
122 |
123 | -------
124 | Some editing functions: yank, delete, paste from register, paste block from yanked regions
125 |
126 | 
127 |
128 | ----------------------------------------
129 |
130 | Case conversion
131 |
132 | 
133 |
134 |
--------------------------------------------------------------------------------
/autoload/vm.vim:
--------------------------------------------------------------------------------
1 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
2 | " Initialize global variables
3 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
4 |
5 | let g:VM_live_editing = get(g:, 'VM_live_editing', 1)
6 |
7 | let g:VM_custom_commands = get(g:, 'VM_custom_commands', {})
8 | let g:VM_commands_aliases = get(g:, 'VM_commands_aliases', {})
9 | let g:VM_debug = get(g:, 'VM_debug', 0)
10 | let g:VM_reselect_first = get(g:, 'VM_reselect_first', 0)
11 | let g:VM_case_setting = get(g:, 'VM_case_setting', '')
12 | let g:VM_use_first_cursor_in_line = get(g:, 'VM_use_first_cursor_in_line', 0)
13 | let g:VM_disable_syntax_in_imode = get(g:, 'VM_disable_syntax_in_imode', 0)
14 |
15 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
16 | "Reindentation after insert mode
17 |
18 | let g:VM_reindent_filetypes = get(g:, 'VM_reindent_filetypes', [])
19 |
20 | call vm#themes#init()
21 | call vm#plugs#buffer()
22 |
23 |
24 |
25 |
26 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
27 | " Initialize buffer
28 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
29 | " b:VM_Selection (= s:V) contains Regions, Vars (= s:v = plugin variables),
30 | " function classes (Global, Funcs, Edit, Search, Insert, etc)
31 |
32 | " Parameters:
33 | " cmd_type: if > 0, the search register will be set to an empty string
34 | " adding cursors uses 1, starting regex uses 2
35 |
36 | fun! vm#init_buffer(cmd_type) abort
37 | " If already initialized, return current instance.
38 | let v:errmsg = ""
39 | try
40 | if exists('b:visual_multi') | return s:V | endif
41 |
42 | let b:VM_Selection = {'Vars': {}, 'Regions': [], 'Bytes': {}}
43 | let b:visual_multi = 1
44 |
45 | let b:VM_Debug = get(b:, 'VM_Debug', {'lines': []})
46 | let b:VM_Backup = {'ticks': [], 'last': 0, 'first': undotree().seq_cur}
47 |
48 | " funcs script must be sourced first
49 | let s:V = b:VM_Selection
50 | let s:v = s:V.Vars
51 | let s:V.Funcs = vm#funcs#init()
52 |
53 | " init plugin variables
54 | call vm#variables#init()
55 |
56 | if get(g:, 'VM_filesize_limit', 0) && s:V.Funcs.size() > gVM_filesize_limit
57 | call vm#variables#reset_globals()
58 | let v:errmsg = 'VM cannot start, buffer too big.'
59 | return v:errmsg
60 | endif
61 |
62 | " init search register
63 | let @/ = a:cmd_type ? '' : @/
64 |
65 | " hooks and compatibility tweaks before applying mappings
66 | call vm#comp#init()
67 |
68 | " init classes
69 | let s:V.Maps = vm#maps#init()
70 | let s:V.Global = vm#global#init()
71 | let s:V.Search = vm#search#init()
72 | let s:V.Edit = vm#edit#init()
73 | let s:V.Insert = vm#insert#init()
74 | let s:V.Case = vm#special#case#init()
75 |
76 | call s:V.Maps.enable()
77 |
78 | call vm#region#init()
79 | call vm#commands#init()
80 | call vm#operators#init()
81 | call vm#special#commands#init()
82 |
83 | call vm#augroup(0)
84 | call vm#au_cursor(0)
85 |
86 | " set vim variables
87 | call vm#variables#set()
88 |
89 | if !empty(g:VM_highlight_matches)
90 | if !has_key(g:Vm, 'Search')
91 | call vm#themes#init()
92 | else
93 | call vm#themes#search_highlight()
94 | endif
95 | hi clear Search
96 | exe 'hi! ' . g:Vm.Search
97 | endif
98 |
99 | if !v:hlsearch && a:cmd_type != 2
100 | call s:enable_hls()
101 | endif
102 |
103 | call s:V.Funcs.set_statusline(0)
104 |
105 | " backup sync settings for the buffer
106 | if !exists('b:VM_sync_minlines')
107 | let b:VM_sync_minlines = s:V.Funcs.sync_minlines()
108 | endif
109 |
110 | let g:Vm.buffer = bufnr('')
111 | return s:V
112 | catch
113 | let v:errmsg = 'VM cannot start, unhandled exception.'
114 | call vm#variables#reset_globals()
115 | return v:errmsg
116 | endtry
117 | endfun
118 |
119 | fun! s:enable_hls()
120 | if mode(1) == 'n'
121 | call feedkeys("\(VM-Hls)")
122 | else
123 | call timer_start(50, { t -> s:enable_hls() })
124 | endif
125 | endfun
126 |
127 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
128 | " Reset
129 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
130 |
131 | fun! vm#reset(...)
132 | if !exists('b:visual_multi')
133 | return {}
134 | endif
135 | call vm#variables#reset()
136 | call vm#commands#regex_reset()
137 |
138 | call s:V.Global.remove_highlight()
139 | call s:V.Global.backup_last_regions()
140 |
141 | call s:V.Funcs.restore_regs()
142 | call s:V.Maps.disable(1)
143 | silent! call s:V.Insert.auto_end()
144 |
145 | call vm#maps#reset()
146 | call vm#comp#reset()
147 | call vm#augroup(1)
148 | call vm#au_cursor(1)
149 |
150 | " reenable folding, but keep winline and open current fold
151 | if exists('s:v.oldfold')
152 | call s:V.Funcs.Scroll.get(1)
153 | normal! zizv
154 | call s:V.Funcs.Scroll.restore()
155 | endif
156 |
157 | if !empty(g:VM_highlight_matches)
158 | hi clear Search
159 | exe 'hi! ' . g:Vm.search_hi
160 | endif
161 |
162 | if g:Vm.oldupdate && &updatetime != g:Vm.oldupdate
163 | let &updatetime = g:Vm.oldupdate
164 | endif
165 |
166 | call vm#comp#exit()
167 |
168 | call s:V.Funcs.restore_visual_marks()
169 |
170 | "exiting manually
171 | if !get(g:, 'VM_silent_exit', 0) && !a:0
172 | call s:V.Funcs.msg('Exited Visual-Multi.')
173 | else
174 | redraw!
175 | endif
176 |
177 | call vm#variables#reset_globals()
178 | call vm#special#commands#unset()
179 | unlet b:visual_multi
180 | call garbagecollect()
181 | return {}
182 | endfun
183 |
184 | "------------------------------------------------------------------------------
185 |
186 | fun! vm#hard_reset()
187 | silent! call vm#reset(1)
188 | call vm#clearmatches()
189 | endfun
190 |
191 | "------------------------------------------------------------------------------
192 |
193 | fun! vm#clearmatches() abort
194 | for m in getmatches()
195 | if m.group == 'VM_Extend' || m.group == 'MultiCursor'
196 | silent! call matchdelete(m.id)
197 | endif
198 | endfor
199 | endfun
200 |
201 |
202 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
203 | " Autocommands
204 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
205 |
206 | fun! vm#augroup(end) abort
207 | if a:end
208 | autocmd! VM_global
209 | augroup! VM_global
210 | return
211 | endif
212 |
213 | augroup VM_global
214 | au!
215 | au BufLeave * call s:buffer_leave()
216 | au BufEnter * call s:buffer_enter()
217 |
218 | if exists("##TextYankPost")
219 | au TextYankPost call s:set_reg()
220 | au TextYankPost call vm#operators#after_yank()
221 | else
222 | au CursorMoved call s:set_reg()
223 | au CursorMoved call vm#operators#after_yank()
224 | au CursorHold call vm#operators#after_yank()
225 | endif
226 | augroup END
227 | endfun
228 |
229 | fun! vm#au_cursor(end) abort
230 | if a:end
231 | autocmd! VM_cursormoved
232 | augroup! VM_cursormoved
233 | return
234 | endif
235 |
236 | augroup VM_cursormoved
237 | au!
238 | au CursorMoved call s:cursor_moved()
239 | au CursorMoved call s:V.Funcs.set_statusline(2)
240 | au CursorHold call s:V.Funcs.set_statusline(1)
241 | augroup END
242 | endfun
243 |
244 | fun! s:cursor_moved() abort
245 | if !s:v.eco
246 | " if currently on a region, set the index to this region
247 | " so that it's possible to select next/previous from it
248 | let r = s:V.Global.region_at_pos()
249 | if !empty(r) | let s:v.index = r.index | endif
250 | endif
251 | endfun
252 |
253 | fun! s:buffer_leave() abort
254 | if exists('b:VM_skip_reset_once_on_bufleave')
255 | unlet b:VM_skip_reset_once_on_bufleave
256 | elseif !empty(get(b:, 'VM_Selection', {})) && !b:VM_Selection.Vars.insert
257 | call vm#reset(1)
258 | endif
259 | endfun
260 |
261 | fun! s:buffer_enter() abort
262 | if empty(get(b:, 'VM_Selection', {}))
263 | let b:VM_Selection = {}
264 | endif
265 | endfun
266 |
267 | fun! s:set_reg() abort
268 | " Replace old default register if yanking in VM outside a region or cursor.
269 | if s:v.yanked
270 | let s:v.yanked = 0
271 | let g:Vm.registers['"'] = []
272 | let s:v.oldreg = s:V.Funcs.get_reg(v:register)
273 | endif
274 | endfun
275 |
276 |
277 |
278 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
279 | " Python section
280 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
281 |
282 | if !has('python3')
283 | let g:VM_use_python = 0
284 | finish
285 | endif
286 |
287 | let g:VM_use_python = get(g:, 'VM_use_python', !has('nvim'))
288 | if !g:VM_use_python | finish | endif
289 |
290 | let s:root_dir = fnamemodify(resolve(expand(':p')), ':h')
291 |
292 | python3 << EOF
293 | import sys
294 | from os.path import normpath, join
295 | import vim
296 | root_dir = vim.eval('s:root_dir')
297 | python_root_dir = normpath(join(root_dir, '..', 'python'))
298 | sys.path.insert(0, python_root_dir)
299 | import vm
300 | EOF
301 |
302 | " vim: et ts=4 sw=4 sts=4 :
303 |
--------------------------------------------------------------------------------
/autoload/vm/comp.vim:
--------------------------------------------------------------------------------
1 | "script to handle compatibility issues with other plugins
2 |
3 | let s:plugins = extend({
4 | \'ctrlsf': {
5 | \ 'test': { -> &ft == 'ctrlsf' },
6 | \ 'enable': 'call ctrlsf#buf#ToggleMap(1)',
7 | \ 'disable': 'call ctrlsf#buf#ToggleMap(0)',
8 | \},
9 | \'AutoPairs': {
10 | \ 'test': { -> exists('b:autopairs_enabled') && b:autopairs_enabled && exists('*AutoPairsTryInit') },
11 | \ 'enable': 'unlet b:autopairs_loaded | call AutoPairsTryInit() | let b:autopairs_enabled = 1',
12 | \ 'disable': 'let b:autopairs_enabled = 0',
13 | \},
14 | \'smartinput': {
15 | \ 'test': { -> exists('g:loaded_smartinput') && g:loaded_smartinput == 1 },
16 | \ 'enable': 'unlet! b:smartinput_disabled',
17 | \ 'disable': 'let b:smartinput_disabled = 1',
18 | \},
19 | \'tagalong': {
20 | \ 'test': { -> exists('b:tagalong_initialized') },
21 | \ 'enable': 'TagalongInit',
22 | \ 'disable': 'TagalongDeinit'
23 | \},
24 | \}, get(g:, 'VM_plugins_compatibilty', {}))
25 |
26 | let s:disabled_deoplete = 0
27 | let s:disabled_ncm2 = 0
28 |
29 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
30 |
31 | fun! vm#comp#init() abort
32 | " Set variables according to plugin needs. "{{{1
33 | let s:V = b:VM_Selection
34 | let s:v = s:V.Vars
35 | let s:v.disabled_plugins = []
36 |
37 | silent! call VM_Start()
38 | silent doautocmd User visual_multi_start
39 |
40 | if exists('g:loaded_youcompleteme')
41 | let g:VM_use_first_cursor_in_line = 1
42 | endif
43 |
44 | if exists('b:doge_interactive')
45 | call doge#deactivate()
46 | endif
47 |
48 | for plugin in keys(s:plugins)
49 | let p = s:plugins[plugin]
50 |
51 | if p.test()
52 | exe p.disable
53 | call add(s:v.disabled_plugins, plugin)
54 | endif
55 | endfor
56 | endfun "}}}
57 |
58 |
59 | fun! vm#comp#icmds() abort
60 | " Insert mode starts: temporarily disable autocompletion engines. {{{1
61 | if exists('g:loaded_deoplete') && g:deoplete#is_enabled()
62 | call deoplete#disable()
63 | let s:disabled_deoplete = 1
64 | elseif exists('b:ncm2_enable') && b:ncm2_enable
65 | let b:ncm2_enable = 0
66 | let s:disabled_ncm2 = 1
67 | endif
68 | endfun "}}}
69 |
70 |
71 | fun! vm#comp#TextChangedI() abort
72 | " Insert mode change: re-enable autocompletion engines. {{{1
73 | if exists('g:loaded_deoplete') && s:disabled_deoplete
74 | call deoplete#enable()
75 | let s:disabled_deoplete = 0
76 | elseif s:disabled_ncm2
77 | let b:ncm2_enable = 1
78 | let s:disabled_ncm2 = 0
79 | endif
80 | endfun "}}}
81 |
82 |
83 | fun! vm#comp#conceallevel() abort
84 | " indentLine compatibility. {{{1
85 | return exists('b:indentLine_ConcealOptionSet') && b:indentLine_ConcealOptionSet
86 | endfun "}}}
87 |
88 |
89 | fun! vm#comp#iobj() abort
90 | " Inner text objects that should avoid using the select operator. {{{1
91 | return exists('g:loaded_targets') ? ['q'] : []
92 | endfun "}}}
93 |
94 |
95 | fun! vm#comp#reset() abort
96 | " Called during VM exit. "{{{1
97 | if exists('g:loaded_deoplete') && s:disabled_deoplete
98 | call deoplete#enable()
99 | let s:disabled_deoplete = 0
100 | elseif s:disabled_ncm2
101 | let b:ncm2_enable = 1
102 | let s:disabled_ncm2 = 0
103 | endif
104 |
105 | "restore plugins functionality if necessary
106 | for plugin in keys(s:plugins)
107 | if index(s:v.disabled_plugins, plugin) >= 0
108 | exe s:plugins[plugin].enable
109 | endif
110 | endfor
111 | endfun "}}}
112 |
113 |
114 | fun! vm#comp#exit() abort
115 | " Called last on VM exit. "{{{1
116 | silent! call VM_Exit()
117 | silent doautocmd User visual_multi_exit
118 | endfun "}}}
119 |
120 |
121 | fun! vm#comp#add_line() abort
122 | " Ensure a line is added with these text objects, while changing in cursor mode. "{{{1
123 |
124 | let l = []
125 | if exists('g:loaded_textobj_indent')
126 | let l += ['ii', 'ai', 'iI', 'aI']
127 | endif
128 | if exists('g:loaded_textobj_function')
129 | let l += ['if', 'af', 'iF', 'aF']
130 | endif
131 | return l
132 | endfun "}}}
133 |
134 |
135 | fun! vm#comp#no_reindents() abort
136 | " Don't reindent for filetypes. "{{{1
137 | return ['ctrlsf']
138 | endfun "}}}
139 |
140 | " vim: et sw=4 ts=4 sts=4 fdm=marker
141 |
--------------------------------------------------------------------------------
/autoload/vm/cursors.vim:
--------------------------------------------------------------------------------
1 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
2 | " Operations at cursors (yank, delete, change)
3 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
4 |
5 | fun! vm#cursors#operation(op, n, register, ...) abort
6 | " Operations at cursors (yank, delete, change)
7 | call s:init()
8 | let reg = a:register | let oper = a:op
9 |
10 | "shortcut for command in a:1
11 | if a:0 | call s:process(oper, a:1, reg, 0) | return | endif
12 |
13 | call s:F.msg('[VM] ')
14 |
15 | "starting string
16 | let M = (a:n>1? a:n : '') . (reg == s:v.def_reg? '' : '"'.reg) . oper
17 |
18 | "preceding count
19 | let n = a:n>1? a:n : 1
20 |
21 | echon M
22 | while 1
23 | let c = nr2char(getchar())
24 | let is_user_op = index(keys(g:Vm.user_ops), M . c) >= 0
25 |
26 | if is_user_op
27 | " let the entered characters be our operator
28 | echon c | let M .= c | let oper = M
29 | if !g:Vm.user_ops[M]
30 | " accepts a regular text object
31 | continue
32 | else
33 | " accepts a specific number of any characters
34 | let chars2read = g:Vm.user_ops[M]
35 | while chars2read
36 | let c = nr2char(getchar())
37 | echon c | let M .= c
38 | let chars2read -= 1
39 | endwhile
40 | break
41 | endif
42 |
43 | elseif s:double(c) | echon c | let M .= c
44 | let c = nr2char(getchar()) | echon c | let M .= c | break
45 |
46 | elseif oper ==# 'c' && c==?'r' | echon c | let M .= c
47 | let c = nr2char(getchar()) | echon c | let M .= c | break
48 |
49 | elseif oper ==# 'c' && c==?'s' | echon c | let M .= c
50 | let c = nr2char(getchar()) | echon c | let M .= c
51 | let c = nr2char(getchar()) | echon c | let M .= c | break
52 |
53 | elseif oper ==# 'y' && c==?'s' | echon c | let M .= c
54 | let c = nr2char(getchar()) | echon c | let M .= c
55 | if s:double(c)
56 | let c = nr2char(getchar()) | echon c | let M .= c
57 | endif
58 | let c = nr2char(getchar()) | echon c
59 | if c == '<' || c == 't'
60 | redraw
61 | let tag = s:V.Edit.surround_tags()
62 | if tag == ''
63 | echon ' ...Aborted' | return
64 | else
65 | let M .= tag | echon c | break
66 | endif
67 | else
68 | let M .= c | echon c | break
69 | endif
70 |
71 | elseif oper ==# 'd' && c==#'s' | echon c | let M .= c
72 | let c = nr2char(getchar()) | echon c | let M .= c | break
73 |
74 | elseif s:single(c) | echon c | let M .= c | break
75 |
76 | elseif str2nr(c) > 0 | echon c | let M .= c
77 |
78 | " if the entered char is the last character of the operator (eg 'yy', 'gUU')
79 | elseif oper[-1:-1] ==# c | echon c | let M .= '_' | break
80 |
81 | else | echon ' ...Aborted' | return
82 | endif
83 | endwhile
84 |
85 | call s:process(oper, M, reg, n)
86 | endfun
87 |
88 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
89 | " Function: s:process
90 | " @param op: the operator
91 | " @param M: the whole command
92 | " @param reg: the register
93 | " @param n: the count
94 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
95 | ""
96 | fun! s:process(op, M, reg, n) abort
97 | " Process the whole command
98 | let s:v.dot = a:M
99 | let s:v.deleting = a:op == 'd' || a:op == 'c'
100 |
101 | if a:op ==# 'd' | call s:delete_at_cursors(a:M, a:reg, a:n)
102 | elseif a:op ==# 'c' | call s:change_at_cursors(a:M, a:reg, a:n)
103 | elseif a:op ==# 'y' | call s:yank_at_cursors(a:M, a:reg, a:n)
104 | else
105 | " if it's a custom operator, pass the mapping as-is, and hope for the best
106 | call s:V.Edit.run_normal(a:M, {'count': a:n, 'recursive': 1})
107 | endif
108 | endfun
109 |
110 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
111 | " Function: s:parse_cmd
112 | " @param M: the whole command
113 | " @param r: the register
114 | " @param n: count that comes before the operator
115 | " @param op: the operator
116 | " Returns: [ text object, count ]
117 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
118 | ""
119 | fun! s:parse_cmd(M, r, n, op) abort
120 | " Parse command, so that the exact count is found.
121 |
122 | "remove register
123 | let Cmd = substitute(a:M, a:r, '', '')
124 |
125 | "what comes after operator
126 | let Obj = substitute(Cmd, '^\d*'.a:op.'\(.*\)$', '\1', '')
127 |
128 | "if object is n/N, ensure there is a search pattern
129 | if Obj ==? 'n' && empty(@/)
130 | let @/ = s:v.oldsearch[0]
131 | endif
132 |
133 | "count that comes after operator
134 | let x = match(Obj, '^\d') >= 0? substitute(Obj, '^\d\zs.*', '', 'g') : 0
135 | if x | let Obj = substitute(Obj, '^' . x, '', '') | endif
136 |
137 | "final count
138 | let n = a:n
139 | let N = x? n*x : n>1? n : 1
140 | let N = N>1? N : ''
141 |
142 | " if the text object is the last character of the operator (eg 'yy')
143 | if Obj ==# a:op[-1:-1]
144 | let Obj = '_'
145 | endif
146 |
147 | return [Obj, N]
148 | endfun
149 |
150 |
151 | fun! s:delete_at_cursors(M, reg, n) abort
152 | " delete operation at cursors
153 | let Cmd = a:M
154 |
155 | "ds surround
156 | if Cmd[:1] ==# 'ds' | return s:V.Edit.run_normal(Cmd) | endif
157 |
158 | let [Obj, N] = s:parse_cmd(Cmd, '"'.a:reg, a:n, 'd')
159 |
160 | "for D, d$, dd: ensure there is only one region per line
161 | if (Obj == '$' || Obj == '_') | call s:G.one_region_per_line() | endif
162 |
163 | "no matter the entered register, we're using default register
164 | "we're passing the register in the options dictionary instead
165 | "fill_register function will be called and take care of it, if appropriate
166 | call s:V.Edit.run_normal('d'.Obj, {'count': N, 'store': a:reg, 'recursive': s:recursive})
167 | call s:G.reorder_regions()
168 | call s:G.merge_regions()
169 | endfun
170 |
171 |
172 | fun! s:yank_at_cursors(M, reg, n) abort
173 | " yank operation at cursors
174 | let Cmd = a:M
175 |
176 | "ys surround
177 | if Cmd[:1] ==? 'ys' | return s:V.Edit.run_normal(Cmd) | endif
178 |
179 | "reset dot for yank command
180 | let s:v.dot = ''
181 |
182 | call s:G.change_mode()
183 |
184 | let [Obj, N] = s:parse_cmd(Cmd, '"'.a:reg, a:n, 'y')
185 |
186 | "for Y, y$, yy, ensure there is only one region per line
187 | if (Obj == '$' || Obj == '_') | call s:G.one_region_per_line() | endif
188 |
189 | call s:V.Edit.run_normal('y'.Obj, {'count': N, 'store': a:reg, 'vimreg': 1})
190 | endfun
191 |
192 |
193 | fun! s:change_at_cursors(M, reg, n) abort
194 | " change operation at cursors
195 | let Cmd = a:M
196 |
197 | "cs surround
198 | if Cmd[:1] ==? 'cs' | return s:V.Edit.run_normal(Cmd) | endif
199 |
200 | "cr coerce (vim-abolish)
201 | if Cmd[:1] ==? 'cr' | return feedkeys("\(VM-Run-Normal)".Cmd."\") | endif
202 |
203 | let [Obj, N] = s:parse_cmd(Cmd, '"'.a:reg, a:n, 'c')
204 |
205 | "convert w,W to e,E (if motions), also in dot
206 | if Obj ==# 'w' | let Obj = 'e' | call substitute(s:v.dot, 'w', 'e', '')
207 | elseif Obj ==# 'W' | let Obj = 'E' | call substitute(s:v.dot, 'W', 'E', '')
208 | endif
209 |
210 | "for c$, cc, ensure there is only one region per line
211 | if (Obj == '$' || Obj == '_') | call s:G.one_region_per_line() | endif
212 |
213 | "replace c with d because we're doing a delete followed by multi insert
214 | let Obj = substitute(Obj, '^c', 'd', '')
215 |
216 | "we're using _ register, unless a register has been specified
217 | let reg = a:reg != s:v.def_reg? a:reg : "_"
218 |
219 | if Obj == '_'
220 | call vm#commands#motion('^', 1, 0, 0)
221 | call vm#operators#select(1, '$')
222 | let s:v.changed_text = s:V.Edit.delete(1, reg, 1, 0)
223 | call s:V.Insert.key('i')
224 |
225 | elseif index(['ip', 'ap'], Obj) >= 0
226 | call s:V.Edit.run_normal('d'.Obj, {'count': N, 'store': reg, 'recursive': s:recursive})
227 | call s:V.Insert.key('O')
228 |
229 | elseif s:recursive && index(vm#comp#add_line(), Obj) >= 0
230 | call s:V.Edit.run_normal('d'.Obj, {'count': N, 'store': reg})
231 | call s:V.Insert.key('O')
232 |
233 | elseif Obj=='$'
234 | call vm#operators#select(1, '$')
235 | let s:v.changed_text = s:V.Edit.delete(1, reg, 1, 0)
236 | call s:V.Insert.key('i')
237 |
238 | elseif Obj=='l'
239 | call s:G.extend_mode()
240 | if N > 1
241 | call vm#commands#motion('l', N-1, 0, 0)
242 | endif
243 | call feedkeys('"'.reg."c")
244 |
245 | elseif s:forward(Obj) || s:ia(Obj) && !s:inside(Obj)
246 | call vm#operators#select(1, N.Obj)
247 | call feedkeys('"'.reg."c")
248 |
249 | else
250 | call s:V.Edit.run_normal('d'.Obj, {'count': N, 'store': reg, 'recursive': s:recursive})
251 | call s:G.merge_regions()
252 | call s:V.Insert.key('i')
253 | endif
254 | endfun
255 |
256 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
257 | " Helpers
258 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
259 |
260 | fun! s:init() abort
261 | "set up script variables
262 | let s:V = b:VM_Selection
263 | let s:v = s:V.Vars
264 | let s:G = s:V.Global
265 | let s:F = s:V.Funcs
266 | let s:Search = s:V.Search
267 |
268 | let s:recursive = get(g:, 'VM_recursive_operations_at_cursors', 1)
269 | call s:G.cursor_mode()
270 | endfun
271 |
272 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
273 | " Lambdas
274 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
275 |
276 | let s:R = { -> s:V.Regions }
277 |
278 | " motions that move the cursor forward
279 | let s:forward = { c -> index(split('weWE%', '\zs'), c) >= 0 }
280 |
281 | " text objects starting with 'i' or 'a'
282 | let s:ia = { c -> index(['i', 'a'], c[:0]) >= 0 }
283 |
284 | " inside brackets/quotes/tags
285 | let s:inside = { c -> c[:0] == 'i' && index(split('bBt[](){}"''`<>', '\zs') + vm#comp#iobj(), c[1:1]) >= 0 }
286 |
287 | " single character motions
288 | let s:single = { c -> index(split('hljkwebWEB$^0{}()%nN_', '\zs'), c) >= 0 }
289 |
290 | " motions that expect a second character
291 | let s:double = { c -> index(split('iafFtTg', '\zs'), c) >= 0 }
292 |
293 | " vim: et sw=2 ts=2 sts=2 fdm=indent fdn=1
294 |
--------------------------------------------------------------------------------
/autoload/vm/ecmds1.vim:
--------------------------------------------------------------------------------
1 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
2 | " Edit commands #1 (yank, delete, paste, replace)
3 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
4 |
5 | let s:Edit = {}
6 | let s:old_text = []
7 |
8 | fun! vm#ecmds1#init() abort
9 | let s:V = b:VM_Selection
10 | let s:v = s:V.Vars
11 | let s:G = s:V.Global
12 | let s:F = s:V.Funcs
13 |
14 | return extend(s:Edit, vm#ecmds2#init())
15 | endfun
16 |
17 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
18 | " Lambdas
19 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
20 |
21 |
22 | let s:R = { -> s:V.Regions }
23 | let s:X = { -> g:Vm.extend_mode }
24 | let s:min = { n -> s:X() && len(s:R()) >= n }
25 |
26 |
27 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
28 | " Yank
29 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
30 |
31 |
32 | fun! s:Edit.yank(reg, silent, ...) abort
33 | " Yank the regions contents in a VM register. {{{1
34 | let register = (s:v.use_register != s:v.def_reg) ? s:v.use_register : a:reg
35 |
36 | if !s:X() | return vm#cursors#operation('y', v:count, register) | endif
37 | if !s:min(1) | return s:F.msg('No regions selected.') | endif
38 |
39 | "write custom and possibly vim registers.
40 | let [text, type] = self.fill_register(register, s:G.regions_text(), 0)
41 |
42 | "restore default register if a different register was provided
43 | if register !=# s:v.def_reg | call s:F.restore_reg() | endif
44 |
45 | "reset temp register
46 | let s:v.use_register = s:v.def_reg
47 |
48 | if !a:silent
49 | call s:F.msg('Yanked the content of '.len(s:R()).' regions.')
50 | endif
51 | if a:0 | call s:G.change_mode() | endif
52 | endfun " }}}
53 |
54 |
55 |
56 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
57 | " Delete
58 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
59 |
60 |
61 | fun! s:Edit.delete(X, register, count, manual) abort
62 | " Delete the selected text and change to cursor mode.
63 | " Return the deleted text.
64 | " {{{1
65 | if s:F.no_regions() | return | endif
66 | if !s:v.direction | call vm#commands#invert_direction() | endif
67 |
68 | if !a:X "ask for motion
69 | return vm#cursors#operation('d', a:count, a:register)
70 | endif
71 |
72 | let winline = winline()
73 | let size = s:F.size()
74 | let change = 0
75 | let ix = s:G.select_region_at_pos('.').index
76 | let s:old_text = s:G.regions_text()
77 | let retVal = copy(s:old_text)
78 | let s:v.deleting = 1
79 |
80 | " manual deletion: backup current regions
81 | if a:manual | call s:G.backup_regions() | endif
82 |
83 | for r in s:R()
84 | call r.shift(change, change)
85 | call self.extra_spaces.add(r)
86 | call cursor(r.l, r.a)
87 | if r.w == 1
88 | normal! "_dl
89 | else
90 | normal! m[
91 | call cursor(r.L, r.b>1? r.b+1 : 1)
92 | normal! m]`["_d`]
93 | endif
94 |
95 | "update changed size
96 | let change = s:F.size() - size
97 | endfor
98 |
99 | "write custom and possibly vim registers.
100 | call self.fill_register(a:register, s:old_text, a:manual)
101 |
102 | call s:G.change_mode()
103 | call s:G.select_region(ix)
104 |
105 | if a:manual
106 | call self.extra_spaces.remove()
107 | call s:G.update_and_select_region()
108 | endif
109 | if a:register == "_" | call s:F.restore_reg() | endif
110 | call s:F.Scroll.force(winline)
111 | let s:old_text = []
112 | return retVal
113 | endfun " }}}
114 |
115 |
116 | fun! s:Edit.xdelete(key, cnt) abort
117 | " Delete with 'x' or 'X' key, use black hole register in extend mode {{{1
118 | if s:X()
119 | call self.delete(1, '_', a:cnt, 1)
120 | else
121 | call self.run_normal(a:key, {'count': a:cnt, 'recursive': 0})
122 | endif
123 | endfun "}}}
124 |
125 |
126 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
127 | " Paste
128 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
129 |
130 |
131 | fun! s:Edit.paste(before, vim_reg, reselect, register, ...) abort
132 | " Perform a paste of the appropriate type. {{{1
133 | " @param before: 'P' or 'p' behaviour
134 | " @param vim_reg: if forcing regular vim registers
135 | " @param reselect: trigger reselection if run from extend mode
136 | " @param register: the register being used
137 | " @param ...: optional list with replacement text for regions
138 | let X = s:X()
139 | let s:v.use_register = a:register
140 | let vim_reg = a:vim_reg || !has_key(g:Vm.registers, a:register) ||
141 | \ empty(g:Vm.registers[a:register])
142 | let vim_V = vim_reg && getregtype(a:register) ==# 'V'
143 |
144 | if empty(s:old_text) | let s:old_text = s:G.regions_text() | endif
145 |
146 | if vim_V
147 | return self.run_normal('"' . a:register . 'p', {'recursive': 0})
148 |
149 | elseif a:0 | let s:v.new_text = a:1
150 | elseif vim_reg | let s:v.new_text = self.convert_vimreg(a:vim_reg)
151 | else | let s:v.new_text = s:fix_regions_text(g:Vm.registers[a:register])
152 | endif
153 |
154 | call s:G.backup_regions()
155 |
156 | if X | call self.delete(1, "_", 1, 0) | endif
157 |
158 | call self.block_paste(a:before)
159 |
160 | let s:v.W = self.store_widths(s:v.new_text)
161 | call self.post_process((X? 1 : a:reselect), !a:before)
162 | let s:old_text = []
163 | endfun " }}}
164 |
165 |
166 | fun! s:Edit.block_paste(before) abort
167 | " Paste the new text (list-type) at cursors. {{{1
168 | let size = s:F.size()
169 | let text = copy(s:v.new_text)
170 | let change = 0
171 | let s:v.eco = 1
172 |
173 | for r in s:R()
174 | if !empty(text)
175 | call r.shift(change, change)
176 | call cursor(r.l, r.a)
177 | let s = remove(text, 0)
178 | call s:F.set_reg(s)
179 |
180 | if a:before
181 | normal! P
182 | else
183 | normal! p
184 | if !exists('s:v.dont_move_cursors')
185 | call r.update_cursor_pos()
186 | endif
187 | endif
188 |
189 | "update changed size
190 | let change = s:F.size() - size
191 | else
192 | break
193 | endif
194 | endfor
195 | silent! unlet s:v.dont_move_cursors
196 | call s:F.restore_reg()
197 | endfun " }}}
198 |
199 |
200 |
201 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
202 | " Replace
203 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
204 |
205 |
206 | fun! s:Edit.replace_chars() abort
207 | " Replace single characters or selections with character. {{{1
208 | if s:X()
209 | let char = nr2char(getchar())
210 | if char ==? "\" | return | endif
211 |
212 | if s:v.multiline
213 | call s:F.toggle_option('multiline')
214 | call s:G.remove_empty_lines()
215 | endif
216 |
217 | let s:v.W = self.store_widths() | let s:v.new_text = []
218 |
219 | for i in range(len(s:v.W))
220 | let r = ''
221 | while len(r) < s:v.W[i] | let r .= char | endwhile
222 | let s:v.W[i] -= 1
223 | call add(s:v.new_text, r)
224 | endfor
225 |
226 | call self.delete(1, "_", 1, 0)
227 | call self.block_paste(1)
228 | call self.post_process(1, 0)
229 | else
230 | call s:F.msg('Replace char... ')
231 | let char = nr2char(getchar())
232 | if char ==? "\" | return s:F.msg('Canceled.') | endif
233 | call self.run_normal('r'.char, {'recursive': 0, 'stay_put': 1})
234 | endif
235 | endfun " }}}
236 |
237 |
238 | fun! s:Edit.replace() abort
239 | " Replace a pattern in all regions, or start replace mode. {{{1
240 | if !s:X()
241 | let s:V.Insert.replace = 1
242 | return s:V.Insert.key('i')
243 | endif
244 |
245 | let ix = s:v.index
246 | call s:F.Scroll.get()
247 |
248 | echohl Type
249 | let pat = input('Pattern to replace > ')
250 | if empty(pat)
251 | return s:F.msg('Command aborted.')
252 | endif
253 | let repl = input('Replacement > ')
254 | if empty(repl)
255 | call s:F.msg('Hit Enter for an empty replacement... ')
256 | if getchar() != 13
257 | return s:F.msg('Command aborted.')
258 | endif
259 | endif
260 | echohl None
261 |
262 | let text = s:G.regions_text()
263 | let T = []
264 | for t in text
265 | call add(T, substitute(t, '\C' . pat, repl, 'g'))
266 | endfor
267 | call self.replace_regions_with_text(T)
268 | call s:G.select_region(ix)
269 | endfun " }}}
270 |
271 |
272 | fun! s:Edit.replace_expression() abort
273 | " Replace all regions with the result of an expression. {{{1
274 | if !s:X() | return | endif
275 | let ix = s:v.index | call s:F.Scroll.get()
276 |
277 | echohl Type | let expr = input('Expression > ', '', 'expression') | echohl None
278 | if empty(expr) | return s:F.msg('Command aborted.') | endif
279 |
280 | let T = [] | let expr = s:F.get_expr(expr)
281 | for r in s:R()
282 | call add(T, eval(expr))
283 | endfor
284 | call map(T, 'type(v:val) != v:t_string ? string(v:val) : v:val')
285 | call self.replace_regions_with_text(T)
286 | call s:G.select_region(ix)
287 | endfun " }}}
288 |
289 |
290 |
291 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
292 | " Helper functions
293 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
294 |
295 |
296 | fun! s:fix_regions_text(replacement) abort
297 | " Ensure there are enough elements for all regions. {{{1
298 | let L = a:replacement
299 | let i = len(s:R()) - len(L)
300 |
301 | while i>0
302 | call add(L, empty(s:old_text)? '' : s:old_text[-i])
303 | let i -= 1
304 | endwhile
305 | return L
306 | endfun " }}}
307 |
308 |
309 | fun! s:Edit.convert_vimreg(as_block) abort
310 | " Fill the content to paste with the chosen vim register. {{{1
311 | let text = []
312 | let block = char2nr(getregtype(s:v.use_register)[0]) == 22
313 |
314 | if block
315 | "default register is of block type, assign a line to each region
316 | let width = getregtype(s:v.use_register)[1:]
317 | let content = split(getreg(s:v.use_register), "\n")
318 |
319 | "ensure all regions have the same width, fill the rest with spaces
320 | if a:as_block
321 | for t in range(len(content))
322 | while len(content[t]) < width | let content[t] .= ' ' | endwhile
323 | endfor
324 | endif
325 |
326 | call s:fix_regions_text(content)
327 |
328 | for n in range(len(s:R()))
329 | call add(text, content[n])
330 | endfor
331 | else
332 | for n in range(len(s:R())) | call add(text, getreg(s:v.use_register)) | endfor
333 | endif
334 | return text
335 | endfun " }}}
336 |
337 |
338 | fun! s:Edit.store_widths(...) abort
339 | " Build a list that holds the widths(integers) of each region {{{1
340 | " It will be used for various purposes (reselection, paste as block...)
341 |
342 | let W = [] | let x = s:X()
343 | let use_text = 0
344 | let use_list = 0
345 |
346 | if a:0
347 | if type(a:1) == type("") | let text = len(a:1)-1 | let use_text = 1
348 | else | let list = a:1 | let use_list = 1
349 | endif
350 | endif
351 |
352 | "mismatching blocks must be corrected
353 | if use_list | call s:fix_regions_text(list) | endif
354 |
355 | for r in s:R()
356 | "if using list, w must be len[i]-1, but always >= 0, set it to 0 if empty
357 | if use_list | let w = len(list[r.index]) | endif
358 | call add( W, use_text? text : use_list? (w? w-1 : 0) : r.w )
359 | endfor
360 | return W
361 | endfun " }}}
362 |
363 |
364 | fun! s:Edit.fill_register(reg, text, force_ow) abort
365 | " Write custom and possibly vim registers. {{{1
366 |
367 | "if doing a change/deletion, write the VM - register
368 | if s:v.deleting
369 | let g:Vm.registers['-'] = a:text
370 | let s:v.deleting = 0
371 | endif
372 |
373 | if a:reg == "_" | return | endif
374 |
375 | let text = a:text
376 | let reg = empty(a:reg) ? '"' : a:reg
377 | let temp_reg = reg == '§'
378 | let overwrite = reg ==# s:v.def_reg || reg == '+' || ( a:force_ow && !temp_reg )
379 | let maxw = max(map(copy(text), 'len(v:val)'))
380 | let type = s:v.multiline? 'V' : ( len(s:R())>1? 'b'.maxw : 'v' )
381 |
382 | " set VM register, overwrite backup register unless temporary
383 | if !temp_reg
384 | let g:Vm.registers[s:v.def_reg] = text
385 | let s:v.oldreg = [s:v.def_reg, join(text, "\n"), type]
386 | endif
387 | " don't store the system register
388 | if reg != '+'
389 | let g:Vm.registers[reg] = text
390 | endif
391 |
392 | "vim register is overwritten if unnamed, or if forced
393 | if overwrite
394 | call setreg(reg, join(text, "\n"), type)
395 | endif
396 |
397 | return [text, type]
398 | endfun " }}}
399 |
400 |
401 | fun! s:Edit.replace_regions_with_text(text, ...) abort
402 | " Paste a custom list of strings into current regions. {{{1
403 | call self.fill_register('"', a:text, 0)
404 | let before = !a:0 || !a:1
405 | call self.paste(before, 0, s:X(), '"')
406 | endfun " }}}
407 |
408 |
409 | " vim: et sw=4 ts=4 sts=4 fdm=marker
410 |
--------------------------------------------------------------------------------
/autoload/vm/ecmds2.vim:
--------------------------------------------------------------------------------
1 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
2 | " Edit commands #2 (special commands)
3 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
4 |
5 | let s:Edit = {}
6 |
7 | fun! vm#ecmds2#init() abort
8 | let s:V = b:VM_Selection
9 | let s:v = s:V.Vars
10 | let s:G = s:V.Global
11 | let s:F = s:V.Funcs
12 | return s:Edit
13 | endfun
14 |
15 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
16 | " Lambdas
17 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
18 |
19 | let s:R = { -> s:V.Regions }
20 | let s:X = { -> g:Vm.extend_mode }
21 |
22 |
23 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
24 | " Duplicate
25 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
26 |
27 | fun! s:Edit.duplicate() abort
28 | if !s:min(1) | return | endif
29 |
30 | call self.yank('§', 1)
31 | call s:G.change_mode()
32 | call self.paste(1, 0, 1, '§')
33 | endfun
34 |
35 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
36 | " Change
37 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
38 |
39 | fun! s:Edit.change(X, count, reg, smart_case) abort
40 | if !len(s:R()) | return | endif
41 | if !s:v.direction | call vm#commands#invert_direction() | endif
42 | if a:smart_case && !exists('s:v.smart_case_change')
43 | let s:v.smart_case_change = 1
44 | endif
45 | if a:X
46 | "delete existing region contents and leave the cursors
47 | let reg = a:reg != s:v.def_reg? a:reg : "_"
48 | let s:v.changed_text = self.delete(1, reg, 1, 0)
49 | call s:V.Insert.key('i')
50 | else
51 | call vm#cursors#operation('c', a:count, a:reg)
52 | endif
53 | endfun
54 |
55 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
56 | " Non-live edit mode
57 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
58 |
59 | fun! s:Edit.apply_change() abort
60 | call s:V.Insert.auto_end()
61 | let self.skip_index = s:v.index
62 | call self.process('normal! .')
63 | "reset index to skip
64 | let self.skip_index = -1
65 | endfun
66 |
67 |
68 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
69 | " Special commands
70 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
71 |
72 | fun! s:Edit.surround() abort
73 | if !len(s:R()) | return | endif
74 | if !s:X() | call vm#operators#select(1, 'iw') | endif
75 |
76 | if !s:v.direction | call vm#commands#invert_direction() | endif
77 | let s:v.W = self.store_widths()
78 | let reselect = 1
79 |
80 | let c = nr2char(getchar())
81 |
82 | if c == '<' || c ==# 't'
83 | let reselect = 0
84 | let c = self.surround_tags()
85 | if c == ''
86 | redraw
87 | echo
88 | return
89 | endif
90 | endif
91 |
92 | let S = g:Vm.maps.surround
93 |
94 | exe 'silent! nunmap ' . S
95 |
96 | call self.run_visual(S . c, 1)
97 |
98 | if index(['[', '{', '('], c) >= 0
99 | call map(s:v.W, 'v:val + 3')
100 | else
101 | call map(s:v.W, 'v:val + 1')
102 | endif
103 |
104 | if reselect
105 | call self.post_process(1, 0)
106 | else
107 | call self.post_process(0)
108 | endif
109 |
110 | exe 'nmap ' . S . ' (VM-Surround)'
111 | endfun
112 |
113 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
114 |
115 | fun! s:Edit.surround_tags() abort
116 | let c = '<'
117 | echo c
118 |
119 | while v:true
120 | let ch = getchar()
121 | if ch == 27 "esc
122 | return ''
123 | endif
124 | if ch == "\"
125 | if strlen(c) > 1
126 | let c = c[:-2]
127 | else
128 | "no more chars
129 | return ''
130 | endif
131 | else
132 | let c .= nr2char(ch)
133 | if ch == 62 || ch == 13 "> or CR
134 | break
135 | endif
136 | endif
137 | redraw
138 | echo c
139 | endwhile
140 |
141 | return c
142 | endfun
143 |
144 |
145 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
146 |
147 | fun! s:Edit.rotate() abort
148 | """Non-inline transposition.
149 | if !s:min(2) | return | endif
150 |
151 | call self.yank('"', 1)
152 |
153 | let t = remove(g:Vm.registers[s:v.def_reg], 0)
154 | call add(g:Vm.registers[s:v.def_reg], t)
155 | call self.paste(1, 0, 1, '"')
156 | endfun
157 |
158 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
159 |
160 | fun! s:Edit.transpose() abort
161 | if !s:min(2) | return | endif
162 | let rlines = s:G.lines_with_regions(0)
163 | let klines = sort(keys(rlines), 'n')
164 |
165 | "check if there is the same nr of regions in each line
166 | let inline = len(klines) > 1
167 | if inline
168 | let n = 0
169 | for l in klines
170 | let nr = len(rlines[l])
171 |
172 | if nr == 1 | let inline = 0 | break "line with 1 region
173 | elseif !n | let n = nr "set required n regions x line
174 | elseif nr != n | let inline = 0 | break "different number of regions
175 | endif
176 | endfor
177 | endif
178 |
179 | "non-inline transposition
180 | if !inline
181 | return self.rotate()
182 | endif
183 |
184 | call self.yank('"', 1)
185 |
186 | "inline transpositions
187 | for l in klines
188 | let t = remove(g:Vm.registers[s:v.def_reg], rlines[l][-1])
189 | call insert(g:Vm.registers[s:v.def_reg], t, rlines[l][0])
190 | endfor
191 | call self.delete(1, "_", 0, 0)
192 | call self.paste(1, 0, 1, '"')
193 | endfun
194 |
195 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
196 |
197 | fun! s:Edit.align() abort
198 | if s:v.multiline
199 | return s:F.msg('Not possible, multiline is enabled.')
200 | endif
201 | call s:G.cursor_mode()
202 |
203 | call self.run_normal('D', {'store': '§'})
204 | let max = max(map(copy(s:R()), 'virtcol([v:val.l, v:val.a])'))
205 | let reg = g:Vm.registers['§']
206 | for r in s:R()
207 | let spaces = ''
208 | let L = getline(r.l)
209 | if empty(L)
210 | while len(spaces) < max | let spaces .= ' ' | endwhile
211 | call setline(r.l, L[:r.a-1] . spaces . L[r.a:] . reg[r.index])
212 | call r.update_cursor([r.l, r.a + len(spaces) - 1])
213 | else
214 | while len(spaces) < (max - virtcol([r.l, r.a])) | let spaces .= ' ' | endwhile
215 | call setline(r.l, L[:r.a-1] . spaces . L[r.a:] . reg[r.index])
216 | call r.update_cursor([r.l, r.a + len(spaces)])
217 | endif
218 | endfor
219 | call s:G.update_and_select_region()
220 | call vm#commands#motion('l', 1, 0, 0)
221 | endfun
222 |
223 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
224 |
225 | fun! s:Edit.shift(dir) abort
226 | if !s:min(1) | return | endif
227 |
228 | call self.yank('"', 1)
229 | if a:dir
230 | let s:v.dont_move_cursors = 1
231 | call self.paste(0, 0, 1, '"')
232 | else
233 | call self.delete(1, "_", 0, 0)
234 | call vm#commands#motion('h', 1, 0, 0)
235 | call self.paste(1, 0, 1, '"')
236 | endif
237 | endfun
238 |
239 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
240 | " Insert numbers
241 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
242 |
243 | fun! s:Edit._numbers(start, step, separator, append) abort
244 | let start = str2nr(a:start)
245 | let step = str2nr(a:step)
246 |
247 | "------------------------------------------------
248 | " build string from expression
249 |
250 | let text = []
251 | for n in range(len(s:R()))
252 | if a:append
253 | let t = a:separator . string(start + step * n)
254 | else
255 | let t = string(start + step * n) . a:separator
256 | endif
257 | call add(text, t)
258 | endfor
259 |
260 | "------------------------------------------------
261 | " paste string before/after the cursor/selection
262 |
263 | if s:X()
264 | let text = map(copy(s:R()), a:append
265 | \ ? '(v:val.txt).text[v:key]'
266 | \ : 'text[v:key].(v:val.txt)')
267 | call self.replace_regions_with_text(text)
268 | else
269 | call self.replace_regions_with_text(text, a:append)
270 | endif
271 | endfun
272 |
273 | fun! s:Edit.numbers(start, app) abort
274 | if !len(s:R()) | return | endif
275 |
276 | " fill the command line with [count]/default_step
277 | let x = input('Expression > ', a:start . '/1/')
278 |
279 | if empty(x) | return s:F.msg('Canceled') | endif
280 |
281 | "first char must be a digit or a negative sign
282 | if match(x, '^\d') < 0 && match(x, '^\-') < 0
283 | return s:F.msg('Invalid expression')
284 | endif
285 |
286 | "evaluate terms of the expression
287 | "/ is the separator, it must be escaped \/ to be used
288 | let x = split(x, '/', 1)
289 | let i = 0
290 | while i < len(x)-1
291 | if x[i][-1:-1] == '\'
292 | let x[i] = x[i][:-2].'/'.remove(x, i+1)
293 | else
294 | let i += 1
295 | endif
296 | endwhile
297 | call filter(x, '!empty(v:val)')
298 | let n = len(x)
299 |
300 | " true for a number, false for a separator
301 | let l:Num = { x -> match(x, '^\d') >= 0 || match(x, '^\-\d') >= 0 }
302 |
303 | "------------------------------------------- start step separ. append?
304 | if n == 1 | call self._numbers ( x[0], 1, '', a:app )
305 |
306 | elseif n == 2
307 |
308 | if l:Num(x[1]) | call self._numbers ( x[0], x[1], '', a:app )
309 | else | call self._numbers ( x[0], 1, x[1], a:app )
310 | endif
311 |
312 | elseif n == 3 | call self._numbers ( x[0], x[1], x[2], a:app )
313 | endif
314 | endfun
315 |
316 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
317 |
318 | fun! s:min(n) abort
319 | return s:X() && len(s:R()) >= a:n
320 | endfun
321 |
322 | " vim: et ts=4 sw=4 sts=4 :
323 |
--------------------------------------------------------------------------------
/autoload/vm/icmds.vim:
--------------------------------------------------------------------------------
1 | "script to handle several insert mode commands
2 |
3 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
4 |
5 | fun! vm#icmds#init() abort
6 | let s:V = b:VM_Selection
7 | let s:v = s:V.Vars
8 | let s:G = s:V.Global
9 | let s:F = s:V.Funcs
10 | endfun
11 |
12 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
13 | " Lambdas
14 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
15 |
16 | let s:R = { -> s:V.Regions }
17 | let s:X = { -> g:Vm.extend_mode }
18 |
19 |
20 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
21 |
22 | fun! vm#icmds#x(cmd) abort
23 | let size = s:F.size()
24 | let change = 0 | let s:v.eco = 1
25 | if empty(s:v.storepos) | let s:v.storepos = getpos('.')[1:2] | endif
26 | let active = s:R()[s:V.Insert.index]
27 |
28 | for r in s:R()
29 | if s:v.single_region && r isnot active
30 | if r.l == active.l
31 | call r.shift(change, change)
32 | endif
33 | continue
34 | endif
35 |
36 | call r.shift(change, change)
37 | call s:F.Cursor(r.A)
38 |
39 | " we want to emulate the behaviour that and have in insert
40 | " mode, but implemented as normal mode commands
41 |
42 | if s:V.Insert.replace
43 | " in replace mode, we don't allow line joining
44 | if a:cmd ==# 'X' && r.a > 1
45 | let original = s:V.Insert._lines[r.l] " the original line
46 | if strpart(getline(r.l), r.a) =~ '\s*$' " at EOL
47 | call search('\s*$', '', r.l)
48 | endif
49 | "FIXME this part is bugged with multibyte chars
50 | call r.shift(-1,-1)
51 | if r.a > 1
52 | let t1 = strpart(getline('.'), 0, r.a - 1)
53 | let wd = strwidth(t1)
54 | let tc = strcharpart(original, wd, 1)
55 | let t2 = strcharpart(original, wd + 1)
56 | call setline(r.l, t1 . tc . t2)
57 | else
58 | let pre = ''
59 | let post = original
60 | call setline(r.l, pre . post)
61 | endif
62 | endif
63 | elseif a:cmd ==# 'x' && s:eol(r) "at eol, join lines
64 | keepjumps normal! gJ
65 | elseif a:cmd ==# 'x' "normal delete
66 | keepjumps normal! x
67 | elseif a:cmd ==# 'X' && r.a == 1 "at bol, go up and join lines
68 | keepjumps normal! kgJ
69 | call r.shift(-1,-1)
70 | else "normal backspace
71 | keepjumps normal! X
72 | let w = strlen(@-)
73 | call r.shift(-w, -w)
74 | endif
75 |
76 | "update changed size
77 | let change = s:F.size() - size
78 | endfor
79 |
80 | call s:G.merge_regions()
81 | call s:G.select_region(s:V.Insert.index)
82 | endfun
83 |
84 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
85 |
86 | fun! vm#icmds#cw(ctrlu) abort
87 | let size = s:F.size()
88 | let change = 0 | let s:v.eco = 1
89 | let s:v.storepos = getpos('.')[1:2]
90 | let keep_line = get(g:, 'VM_icw_keeps_line', 1)
91 |
92 | for r in s:R()
93 | call r.shift(change, change)
94 |
95 | "TODO: deletion to line above can be bugged for now
96 | if keep_line && r.a == 1 | continue | endif
97 |
98 | call s:F.Cursor(r.A)
99 |
100 | if r.a > 1 && s:eol(r) "add extra space and move right
101 | call s:V.Edit.extra_spaces.add(r)
102 | call r.move('l')
103 | endif
104 |
105 | let L = getline(r.l)
106 | let ws_only = r.a > 1 && match(L[:(r.a-2)], '[^ \t]') < 0
107 |
108 | if a:ctrlu "ctrl-u
109 | keepjumps normal! d^
110 | elseif r.a == 1 "at bol, go up and join lines
111 | keepjumps normal! kgJ
112 | elseif ws_only "whitespace only before, delete it
113 | keepjumps normal! d0
114 | else "normal deletion
115 | keepjumps normal! db
116 | endif
117 | call r.update_cursor_pos()
118 |
119 | "update changed size
120 | let change = s:F.size() - size
121 | endfor
122 | call s:V.Insert.start(1)
123 | endfun
124 |
125 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
126 |
127 | fun! vm#icmds#paste() abort
128 | call s:G.select_region(-1)
129 | call s:V.Edit.paste(1, 0, 1, '"')
130 | call s:G.select_region(s:V.Insert.index)
131 | endfun
132 |
133 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
134 |
135 | fun! vm#icmds#return() abort
136 | "invert regions order, so that they are processed from bottom to top
137 | let s:V.Regions = reverse(s:R())
138 |
139 | for r in s:R()
140 | call cursor(r.l, r.a)
141 | let rline = getline('.')
142 |
143 | "we also consider at EOL cursors that have trailing spaces after them
144 | "if not at EOL, CR will cut the line and carry over the remaining text
145 | let at_eol = match(strpart(rline, r.a-1, len(rline)), '\s*$') == 0
146 |
147 | "if carrying over some text, delete it now, for better indentexpr
148 | "otherwise delete the trailing spaces that would be left at EOL
149 | if !at_eol | keepjumps normal! d$
150 | else | keepjumps normal! "_d$
151 | endif
152 |
153 | "append a line and get the indent
154 | noautocmd exe "silent keepjumps normal! o\=get_indent()\"
155 |
156 | "fill the line with tabs or spaces, according to the found indent
157 | "an extra space must be added, if not carrying over any text
158 | "also keep the indent whitespace only, removing any non-space character
159 | "such as comments, and everything after them
160 | let extra_space = at_eol ? ' ' : ''
161 | let indent = substitute(g:Vm.indent, '\S\+.*', '', 'g')
162 | call setline('.', indent . extra_space)
163 |
164 | "if carrying over some text, paste it after the indent
165 | "but strip preceding whitespace found in the text
166 | if !at_eol
167 | let @" = substitute(@", '^\s*', '', '')
168 | keepjumps normal! $p
169 | endif
170 |
171 | "cursor line will be moved down by the next cursors
172 | call r.update_cursor([line('.') + r.index, len(indent) + 1])
173 | endfor
174 |
175 | "reorder regions
176 | let s:V.Regions = reverse(s:R())
177 |
178 | "ensure cursors are at indent level
179 | keepjumps normal ^
180 | endfun
181 |
182 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
183 |
184 | fun! vm#icmds#insert_line(above) abort
185 | "invert regions order, so that they are processed from bottom to top
186 | let s:V.Regions = reverse(s:R())
187 |
188 | for r in s:R()
189 | "append a line below or above
190 | call cursor(r.l, r.a)
191 | noautocmd exe "silent keepjumps normal!" (a:above ? 'O' : 'o')."\=get_indent()\"
192 |
193 | "remove comment or other chars, fill the line with tabs or spaces
194 | let indent = substitute(g:Vm.indent, '[^ \t].*', '', 'g')
195 | call setline('.', indent . ' ')
196 |
197 | "cursor line will be moved down by the next cursors
198 | call r.update_cursor([line('.') + r.index, len(indent) + 1])
199 | call add(s:v.extra_spaces, r.index)
200 | endfor
201 |
202 | "reorder regions
203 | let s:V.Regions = reverse(s:R())
204 |
205 | "ensure cursors are at indent level
206 | keepjumps normal ^
207 | endfun
208 |
209 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
210 |
211 | fun! vm#icmds#goto(next) abort
212 | """Used in single region mode.
213 | let s:v.single_mode_running = 1
214 | let t = ":call b:VM_Selection.Insert.key('".s:V.Insert.type."')\"
215 | if a:next
216 | return "\:call vm#commands#find_next(0,1)\".t
217 | else
218 | return "\:call vm#commands#find_prev(0,1)\".t
219 | endif
220 | endfun
221 |
222 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
223 |
224 | fun! s:get_indent() abort
225 | let g:Vm.indent = getline('.')
226 | return ''
227 | endfun
228 |
229 | fun! s:eol(r)
230 | return a:r.a == (col([a:r.l, '$']) - 1)
231 | endfun
232 | " vim: et ts=4 sw=4 sts=4 :
233 |
--------------------------------------------------------------------------------
/autoload/vm/maps.vim:
--------------------------------------------------------------------------------
1 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
2 | "Initialize
3 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
4 |
5 | let s:Maps = {}
6 |
7 | let g:VM_custom_noremaps = get(g:, 'VM_custom_noremaps', {})
8 | let g:VM_custom_remaps = get(g:, 'VM_custom_remaps', {})
9 | let g:VM_custom_motions = get(g:, 'VM_custom_motions', {})
10 | let g:VM_check_mappings = get(g:, 'VM_check_mappings', 1)
11 | let g:VM_default_mappings = get(g:, 'VM_default_mappings', 1)
12 | let g:VM_mouse_mappings = get(g:, 'VM_mouse_mappings', 0)
13 |
14 |
15 | fun! vm#maps#default() abort
16 | " At vim start, permanent mappings are generated and applied.
17 | call s:build_permanent_maps()
18 | for m in g:Vm.maps.permanent | exe m | endfor
19 | endfun
20 |
21 |
22 | fun! vm#maps#init() abort
23 | " At VM start, buffer mappings are generated (once per buffer) and applied.
24 | let s:V = b:VM_Selection
25 | if !exists('b:VM_maps') | call s:build_buffer_maps() | endif
26 |
27 | call s:Maps.map_esc_and_toggle()
28 | call s:check_warnings()
29 | return s:Maps
30 | endfun
31 |
32 |
33 | fun! vm#maps#reset() abort
34 | " At VM reset, last buffer mappings are reset, and permanent maps are restored.
35 | call s:Maps.unmap_esc_and_toggle()
36 | for m in g:Vm.maps.permanent | exe m | endfor
37 | endfun
38 |
39 |
40 |
41 |
42 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
43 | " Mappings activation/deactivation
44 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
45 |
46 | fun! s:Maps.enable() abort
47 | " Enable mappings in current buffer.
48 | if !g:Vm.mappings_enabled
49 | let g:Vm.mappings_enabled = 1
50 | call self.start()
51 | endif
52 | endfun
53 |
54 |
55 | fun! s:Maps.disable(keep_permanent) abort
56 | " Disable mappings in current buffer.
57 | if g:Vm.mappings_enabled
58 | let g:Vm.mappings_enabled = 0
59 | call self.end(a:keep_permanent)
60 | endif
61 | endfun
62 |
63 |
64 | fun! s:Maps.mappings_toggle() abort
65 | " Toggle mappings in current buffer.
66 | if g:Vm.mappings_enabled
67 | call self.disable(1)
68 | else
69 | call self.enable()
70 | endif
71 | endfun
72 |
73 |
74 |
75 |
76 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
77 | " Apply mappings
78 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
79 |
80 | fun! s:Maps.start() abort
81 | " Apply mappings in current buffer.
82 | for m in g:Vm.maps.permanent | exe m | endfor
83 | for m in b:VM_maps | exe m | endfor
84 |
85 | nmap : (VM-:)
86 | nmap / (VM-/)
87 | nmap ? (VM-?)
88 |
89 | " user autocommand after mappings have been set
90 | silent doautocmd User visual_multi_mappings
91 | endfun
92 |
93 |
94 | fun! s:Maps.map_esc_and_toggle() abort
95 | " Esc and 'toggle' keys are handled separately.
96 | if !has('nvim') && !has('gui_running')
97 | nnoremap
98 | endif
99 | exe 'nmap ' g:Vm.maps.exit '(VM-Exit)'
100 | exe 'nmap ' g:Vm.maps.toggle '(VM-Toggle-Mappings)'
101 | endfun
102 |
103 |
104 |
105 |
106 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
107 | " Remove mappings
108 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
109 |
110 | fun! s:Maps.end(keep_permanent) abort
111 | " Remove mappings in current buffer.
112 | for m in g:Vm.unmaps | exe m | endfor
113 | for m in b:VM_unmaps | exe m | endfor
114 |
115 | nunmap :
116 | nunmap /
117 | nunmap ?
118 | silent! cunmap
119 | silent! cunmap
120 |
121 | " restore permanent mappings
122 | if a:keep_permanent
123 | for m in g:Vm.maps.permanent | exe m | endfor
124 | endif
125 | endfun
126 |
127 |
128 | fun! s:Maps.unmap_esc_and_toggle() abort
129 | " Esc and 'toggle' keys are handled separately.
130 | silent! exe 'nunmap ' g:Vm.maps.toggle
131 | silent! exe 'nunmap ' g:Vm.maps.exit
132 | if !has('nvim') && !has('gui_running')
133 | silent! nunmap
134 | endif
135 | endfun
136 |
137 |
138 |
139 |
140 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
141 | " Map helper functions
142 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
143 |
144 | fun! s:build_permanent_maps() abort
145 | " Run at vim start. Generate permanent mappings and integrate custom ones.
146 |
147 | "set default VM leader
148 | let ldr = get(g:, 'VM_leader', '\\')
149 | let g:Vm.leader = type(ldr) == v:t_string
150 | \ ? {'default': ldr, 'visual': ldr, 'buffer': ldr}
151 | \ : extend({'default':'\\', 'visual':'\\', 'buffer':'\\'}, ldr)
152 |
153 | "init vars and generate base permanent maps
154 | let g:VM_maps = get(g:, 'VM_maps', {})
155 | let g:Vm.maps = {'permanent': []}
156 | let g:Vm.unmaps = []
157 | let maps = vm#maps#all#permanent()
158 |
159 | "integrate custom maps
160 | for key in keys(g:VM_maps)
161 | silent! let maps[key][0] = g:VM_maps[key]
162 | endfor
163 |
164 | "generate list of 'exe' commands for map assignment
165 | for key in keys(maps)
166 | let mapping = s:assign(key, maps[key], 0)
167 | if !empty(mapping)
168 | call add(g:Vm.maps.permanent, mapping)
169 | endif
170 | endfor
171 |
172 | "generate list of 'exe' commands for unmappings
173 | for key in keys(maps)
174 | call add(g:Vm.unmaps, s:unmap(maps[key], 0))
175 | endfor
176 |
177 | "store some mappings that need special handling
178 | let g:Vm.maps.toggle = get(g:VM_maps, 'Toggle Mappings', g:Vm.leader.buffer . '')
179 | let g:Vm.maps.exit = get(g:VM_maps, 'Exit', '')
180 | let g:Vm.maps.surround = get(g:VM_maps, 'Surround', 'S')
181 | endfun
182 |
183 |
184 | fun! s:build_buffer_maps() abort
185 | " Run once per buffer. Generate buffer mappings and integrate custom ones.
186 | let b:VM_maps = []
187 | let b:VM_unmaps = []
188 | let check_maps = get(b:, 'VM_check_mappings', g:VM_check_mappings)
189 | let force_maps = get(b:, 'VM_force_maps', get(g:, 'VM_force_maps', []))
190 |
191 | "generate base buffer maps
192 | let maps = vm#maps#all#buffer()
193 |
194 | "integrate motions
195 | for m in (g:Vm.motions + g:Vm.find_motions)
196 | let maps['Motion ' . m] = [m, 'n']
197 | endfor
198 | for m in keys(g:Vm.tobj_motions)
199 | let maps['Motion ' . g:Vm.tobj_motions[m]] = [m, 'n']
200 | endfor
201 | for op in keys(g:Vm.user_ops)
202 | " don't map the operator if it starts with a key that would interfere
203 | " with VM operations in extend mode, eg. if 'cx' gets mapped, then 'c'
204 | " will not work as it should (it would have a delay in extend mode)
205 | if index(['y', 'c', 'd'], op[:0]) == -1
206 | let maps['User Operator ' . op] = [op, 'n']
207 | endif
208 | endfor
209 |
210 | "integrate custom motions and commands
211 | for m in keys(g:VM_custom_motions)
212 | let maps['Motion ' . g:VM_custom_motions[m]] = [m, 'n']
213 | endfor
214 | for m in keys(g:VM_custom_noremaps)
215 | let maps['Normal! ' . g:VM_custom_noremaps[m]] = [m, 'n']
216 | endfor
217 | for m in keys(g:VM_custom_remaps)
218 | let maps['Remap ' . g:VM_custom_remaps[m]] = [m, 'n']
219 | endfor
220 | for m in keys(g:VM_custom_commands)
221 | let maps[m] = [m, 'n']
222 | endfor
223 |
224 | "integrate custom remappings
225 | for key in keys(g:VM_maps)
226 | silent! let maps[key][0] = g:VM_maps[key]
227 | endfor
228 |
229 | "generate list of 'exe' commands for map assignment
230 | for key in keys(maps)
231 | let mapping = s:assign(key, maps[key], 1, check_maps, force_maps)
232 | if !empty(mapping)
233 | call add(b:VM_maps, mapping)
234 | else
235 | " remove the mapping, so that it won't be unmapped either
236 | unlet maps[key]
237 | endif
238 | endfor
239 |
240 | "generate list of 'exe' commands for unmappings
241 | for key in keys(maps)
242 | call add(b:VM_unmaps, s:unmap(maps[key], 1))
243 | endfor
244 | endfun
245 |
246 |
247 | fun! s:assign(plug, key, buffer, ...) abort
248 | " Create a map command that will be executed.
249 | let k = a:key[0] | if empty(k) | return '' | endif
250 | let m = a:key[1]
251 |
252 | "check if the mapping can be applied: this only runs for buffer mappings
253 | "a:1 is a bool that is true if mappings must be checked
254 | "a:2 can contain a list of mappings that will be applied anyway (forced)
255 | "otherwise, if a buffer mapping already exists, the remapping fails, and
256 | "a debug line is added
257 | if a:0 && a:1 && index(a:2, k) < 0
258 | let K = maparg(k, m, 0, 1)
259 | if !empty(K) && K.buffer
260 | let b = 'b'.bufnr('%').': '
261 | " Handle Neovim mappings with Lua functions as rhs
262 | let rhs = has_key(K, 'rhs') ? K.rhs : ''
263 | if m != 'i'
264 | let s = b.'Could not map: '.k.' ('.a:plug.') -> ' . rhs
265 | call add(b:VM_Debug.lines, s)
266 | return ''
267 | else
268 | let s = b.'Overwritten imap: '.k.' ('.a:plug.') -> ' . rhs
269 | call add(b:VM_Debug.lines, s)
270 | endif
271 | endif
272 | endif
273 |
274 | let p = substitute(a:plug, ' ', '-', 'g')
275 | let _ = a:buffer? ' ' : ' '
276 | return m."map "._.k.' (VM-'.p.")"
277 | endfun
278 |
279 |
280 | fun! s:unmap(key, buffer) abort
281 | " Create an unmap command that will be executed.
282 | let k = a:key[0]
283 | if empty(k) | return '' | endif
284 | let m = a:key[1]
285 | let b = a:buffer? ' ' : ' '
286 | return "silent! ".m."unmap".b.k
287 | endfun
288 |
289 |
290 | fun! s:check_warnings() abort
291 | " Notify once per buffer if errors have happened.
292 | if get(g:, 'VM_show_warnings', 1) && !empty(b:VM_Debug.lines)
293 | \ && !has_key(b:VM_Debug, 'maps_warning')
294 | let b:VM_Debug.maps_warning = 1
295 | call s:V.Funcs.msg('VM has started with warnings. :VMDebug for more info')
296 | endif
297 | endfun
298 |
299 | " vim: et ts=4 sw=4 sts=4 fdm=indent fdn=1 :
300 |
--------------------------------------------------------------------------------
/autoload/vm/maps/all.vim:
--------------------------------------------------------------------------------
1 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
2 | "Key -> plug:
3 | " 'Select Operator' -> (VM-Select-Operator)
4 |
5 | "Contents of lists:
6 | " [0]: mapping
7 | " [1]: mode
8 | "
9 | " When adding a new mapping, the following is required:
10 | " 1. add a with a command
11 | " 2. add the reformatted plug name in this file (permanent or buffer section)
12 |
13 | let s:base = {
14 | \"Reselect Last": ['', 'n'],
15 | \"Add Cursor At Pos": ['', 'n'],
16 | \"Add Cursor At Word": ['', 'n'],
17 | \"Start Regex Search": ['', 'n'],
18 | \"Select All": ['', 'n'],
19 | \"Add Cursor Down": ['', 'n'],
20 | \"Add Cursor Up": ['', 'n'],
21 | \"Visual Regex": ['', 'x'],
22 | \"Visual All": ['', 'x'],
23 | \"Visual Add": ['', 'x'],
24 | \"Visual Find": ['', 'x'],
25 | \"Visual Cursors": ['', 'x'],
26 | \"Find Under": ['', 'n'],
27 | \"Find Subword Under": ['', 'x'],
28 | \"Select Cursor Down": ['', 'n'],
29 | \"Select Cursor Up": ['', 'n'],
30 | \"Select j": ['', 'n'],
31 | \"Select k": ['', 'n'],
32 | \"Select l": ['', 'n'],
33 | \"Select h": ['', 'n'],
34 | \"Select w": ['', 'n'],
35 | \"Select b": ['', 'n'],
36 | \"Select E": ['', 'n'],
37 | \"Select BBW": ['', 'n'],
38 | \"Mouse Cursor": ['', 'n'],
39 | \"Mouse Word": ['', 'n'],
40 | \"Mouse Column": ['', 'n'],
41 | \}
42 |
43 | fun! vm#maps#all#permanent() abort
44 | """Default permanent mappings dictionary."""
45 | let maps = s:base
46 | let leader = g:Vm.leader.default
47 | let visual = g:Vm.leader.visual
48 |
49 | " map in any case
50 | let maps["Find Under"][0] = ''
51 | let maps["Find Subword Under"][0] = ''
52 |
53 | if g:VM_default_mappings
54 | let maps["Reselect Last"][0] = leader.'gS'
55 | let maps["Add Cursor At Pos"][0] = leader.'\'
56 | let maps["Start Regex Search"][0] = leader.'/'
57 | let maps["Select All"][0] = leader.'A'
58 | let maps["Add Cursor Down"][0] = ''
59 | let maps["Add Cursor Up"][0] = ''
60 | let maps["Select l"][0] = ''
61 | let maps["Select h"][0] = ''
62 | let maps["Visual Regex"][0] = visual.'/'
63 | let maps["Visual All"][0] = visual.'A'
64 | let maps["Visual Add"][0] = visual.'a'
65 | let maps["Visual Find"][0] = visual.'f'
66 | let maps["Visual Cursors"][0] = visual.'c'
67 | endif
68 |
69 | if g:VM_mouse_mappings
70 | let maps["Mouse Cursor"][0] = ''
71 | let maps["Mouse Word"][0] = ''
72 | let maps["Mouse Column"][0] = ''
73 | endif
74 |
75 | return maps
76 | endfun
77 |
78 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
79 |
80 | fun! vm#maps#all#buffer() abort
81 | """Default buffer mappings dictionary."""
82 |
83 | let maps = {}
84 | let leader = g:Vm.leader.buffer
85 | let visual = g:Vm.leader.visual
86 |
87 | "basic
88 | call extend(maps, {
89 | \"Switch Mode": ['', 'n'],
90 | \"Toggle Single Region": [leader.'', 'n'],
91 | \})
92 |
93 | "select
94 | call extend(maps, {
95 | \"Find Next": ['n', 'n'],
96 | \"Find Prev": ['N', 'n'],
97 | \"Goto Next": [']', 'n'],
98 | \"Goto Prev": ['[', 'n'],
99 | \"Seek Up": ['', 'n'],
100 | \"Seek Down": ['', 'n'],
101 | \"Skip Region": ['q', 'n'],
102 | \"Remove Region": ['Q', 'n'],
103 | \"Remove Last Region": [leader.'q', 'n'],
104 | \"Remove Every n Regions": [leader.'R', 'n'],
105 | \"Select Operator": ['s', 'n'],
106 | \"Find Operator": ['m', 'n'],
107 | \})
108 |
109 | "utility
110 | call extend(maps, {
111 | \"Tools Menu": [leader.'`', 'n'],
112 | \"Show Registers": [leader.'"', 'n'],
113 | \"Case Setting": [leader.'c', 'n'],
114 | \"Toggle Whole Word": [leader.'w', 'n'],
115 | \"Case Conversion Menu": [leader.'C', 'n'],
116 | \"Search Menu": [leader.'S', 'n'],
117 | \"Rewrite Last Search": [leader.'r', 'n'],
118 | \"Show Infoline": [leader.'l', 'n'],
119 | \"One Per Line": [leader.'L', 'n'],
120 | \"Filter Regions": [leader.'f', 'n'],
121 | \"Toggle Multiline": ['M', 'n'],
122 | \})
123 |
124 | "commands
125 | call extend(maps, {
126 | \"Undo": ['', 'n'],
127 | \"Redo": ['', 'n'],
128 | \"Surround": ['S', 'n'],
129 | \"Merge Regions": [leader.'m', 'n'],
130 | \"Transpose": [leader.'t', 'n'],
131 | \"Rotate": ['', 'n'],
132 | \"Duplicate": [leader.'d', 'n'],
133 | \"Align": [leader.'a', 'n'],
134 | \"Split Regions": [leader.'s', 'n'],
135 | \"Visual Subtract": [visual.'s', 'x'],
136 | \"Visual Reduce": [visual.'r', 'x'],
137 | \"Run Normal": [leader.'z', 'n'],
138 | \"Run Last Normal": [leader.'Z', 'n'],
139 | \"Run Visual": [leader.'v', 'n'],
140 | \"Run Last Visual": [leader.'V', 'n'],
141 | \"Run Ex": [leader.'x', 'n'],
142 | \"Run Last Ex": [leader.'X', 'n'],
143 | \"Run Macro": [leader.'@', 'n'],
144 | \"Run Dot": [leader.'.', 'n'],
145 | \"Align Char": [leader.'<', 'n'],
146 | \"Align Regex": [leader.'>', 'n'],
147 | \"Numbers": [leader.'N', 'n'],
148 | \"Numbers Append": [leader.'n', 'n'],
149 | \"Zero Numbers": [leader.'0N', 'n'],
150 | \"Zero Numbers Append": [leader.'0n', 'n'],
151 | \"Shrink": [leader.'-', 'n'],
152 | \"Enlarge": [leader.'+', 'n'],
153 | \"Goto Regex": [leader.'g', 'n'],
154 | \"Goto Regex!": [leader.'G', 'n'],
155 | \"Slash Search": ['g/', 'n'],
156 | \})
157 |
158 | "arrows
159 | call extend(maps, {
160 | \"Select Cursor Down": ['', 'n'],
161 | \"Select Cursor Up": ['', 'n'],
162 | \"Add Cursor Down": ['', 'n'],
163 | \"Add Cursor Up": ['', 'n'],
164 | \"Select j": ['', 'n'],
165 | \"Select k": ['', 'n'],
166 | \"Select l": ['', 'n'],
167 | \"Select h": ['', 'n'],
168 | \"Single Select l": ['', 'n'],
169 | \"Single Select h": ['', 'n'],
170 | \"Select e": ['', 'n'],
171 | \"Select ge": ['', 'n'],
172 | \"Select w": ['', 'n'],
173 | \"Select b": ['', 'n'],
174 | \"Select E": ['', 'n'],
175 | \"Select BBW": ['', 'n'],
176 | \"Move Right": ['', 'n'],
177 | \"Move Left": ['', 'n'],
178 | \})
179 |
180 | "insert
181 | call extend(maps, {
182 | \"I Arrow w": ['', 'i'],
183 | \"I Arrow b": ['', 'i'],
184 | \"I Arrow W": ['', 'i'],
185 | \"I Arrow B": ['', 'i'],
186 | \"I Arrow ge": ['', 'i'],
187 | \"I Arrow e": ['', 'i'],
188 | \"I Arrow gE": ['', 'i'],
189 | \"I Arrow E": ['', 'i'],
190 | \"I Left Arrow": ['', 'i'],
191 | \"I Right Arrow": ['', 'i'],
192 | \"I Up Arrow": ['', 'i'],
193 | \"I Down Arrow": ['', 'i'],
194 | \"I Return": ['', 'i'],
195 | \"I BS": ['', 'i'],
196 | \"I CtrlW": ['', 'i'],
197 | \"I CtrlU": ['', 'i'],
198 | \"I CtrlD": ['', 'i'],
199 | \"I Ctrl^": ['', 'i'],
200 | \"I Del": ['', 'i'],
201 | \"I Home": ['', 'i'],
202 | \"I End": ['', 'i'],
203 | \"I CtrlB": ['', 'i'],
204 | \"I CtrlF": ['', 'i'],
205 | \"I CtrlC": ['', 'i'],
206 | \"I CtrlO": ['', 'i'],
207 | \"I Replace": ['', 'i'],
208 | \})
209 |
210 | let insert_keys = get(g:, 'VM_insert_special_keys', ['c-v'])
211 | if index(insert_keys, 'c-a') >= 0
212 | let maps["I CtrlA"] = ['', 'i']
213 | endif
214 | if index(insert_keys, 'c-e') >= 0
215 | let maps["I CtrlE"] = ['', 'i']
216 | endif
217 | if index(insert_keys, 'c-v') >= 0
218 | let maps["I Paste"] = ['', 'i']
219 | endif
220 |
221 | "edit
222 | call extend(maps, {
223 | \"D": ['D', 'n'],
224 | \"Y": ['Y', 'n'],
225 | \"x": ['x', 'n'],
226 | \"X": ['X', 'n'],
227 | \"J": ['J', 'n'],
228 | \"~": ['~', 'n'],
229 | \"&": ['&', 'n'],
230 | \"Del": ['', 'n'],
231 | \"Dot": ['.', 'n'],
232 | \"Increase": ['', 'n'],
233 | \"Decrease": ['', 'n'],
234 | \"gIncrease": ['g', 'n'],
235 | \"gDecrease": ['g', 'n'],
236 | \"Alpha Increase": [leader.'','n'],
237 | \"Alpha Decrease": [leader.'','n'],
238 | \"a": ['a', 'n'],
239 | \"A": ['A', 'n'],
240 | \"i": ['i', 'n'],
241 | \"I": ['I', 'n'],
242 | \"o": ['o', 'n'],
243 | \"O": ['O', 'n'],
244 | \"c": ['c', 'n'],
245 | \"gc": ['gc', 'n'],
246 | \"gu": ['gu', 'n'],
247 | \"gU": ['gU', 'n'],
248 | \"C": ['C', 'n'],
249 | \"Delete": ['d', 'n'],
250 | \"Replace Characters": ['r', 'n'],
251 | \"Replace": ['R', 'n'],
252 | \"Transform Regions": [leader.'e', 'n'],
253 | \"p Paste": ['p', 'n'],
254 | \"P Paste": ['P', 'n'],
255 | \"Yank": ['y', 'n'],
256 | \})
257 |
258 | return maps
259 | endfun
260 |
261 | " vim: et ts=2 sw=2 sts=2 :
262 |
--------------------------------------------------------------------------------
/autoload/vm/operators.vim:
--------------------------------------------------------------------------------
1 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
2 | " Select operator
3 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
4 |
5 | fun! vm#operators#init() abort
6 | let s:V = b:VM_Selection
7 | let s:v = s:V.Vars
8 | let s:G = s:V.Global
9 | let s:F = s:V.Funcs
10 | endfun
11 |
12 | fun! s:init() abort
13 | let g:Vm.extend_mode = 1
14 | if !g:Vm.buffer | call vm#init_buffer(0) | endif
15 | endfun
16 |
17 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
18 | " Lambdas
19 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
20 |
21 | let s:R = { -> s:V.Regions }
22 | let s:single = { c -> index(split('hljkwebWEB$^0{}()%nN', '\zs'), c) >= 0 }
23 | let s:double = { c -> index(split('iafFtTg', '\zs'), c) >= 0 }
24 |
25 |
26 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
27 |
28 | fun! vm#operators#select(count, ...) abort
29 | call s:init()
30 | let pos = getpos('.')[1:2]
31 | call s:F.Scroll.get(1)
32 |
33 | if a:0 | return s:select(a:1) | endif
34 |
35 | let [ abort, s, n ] = [ 0, '', '' ]
36 | let x = a:count>1? a:count : 1
37 | echo "Selecting: ".(x>1? x : '')
38 |
39 | while 1
40 | let c = getchar()
41 |
42 | if c == 27 | let abort = 1 | break
43 | else | let c = nr2char(c) | endif
44 |
45 | if str2nr(c) > 0
46 | let n .= c | echon c
47 |
48 | elseif s:single(c)
49 | let s .= c | echon c | break
50 |
51 | elseif s:double(c) || len(s)
52 | let s .= c | echon c
53 | if len(s) > 1 | break | endif
54 |
55 | else
56 | let abort = 1 | break
57 | endif
58 | endwhile
59 |
60 | if abort | return | endif
61 |
62 | " change $ in g_
63 | let s = substitute(s, '\$', 'g_', 'g')
64 |
65 | let n = n<1? 1 : n
66 | let n = n*x>1? n*x : ''
67 | call s:select(n . s)
68 | call s:G.update_and_select_region(pos)
69 | call s:F.Scroll.restore()
70 | endfun
71 |
72 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
73 |
74 | fun! s:select(obj) abort
75 | call s:updatetime()
76 | call s:V.Maps.disable(1)
77 |
78 | if a:obj =~ '"' || a:obj =~ "'"
79 | let cmd = 'v' . a:obj . 'y'
80 | else
81 | let cmd = 'y' . a:obj
82 | endif
83 |
84 | silent! nunmap y
85 |
86 | let Rs = map(copy(s:R()), '[v:val.l, v:val.a]')
87 | call s:G.erase_regions()
88 |
89 | for r in Rs
90 | call cursor(r[0], r[1])
91 | exe "normal" cmd
92 | call s:get_region(0)
93 | endfor
94 |
95 | call s:V.Maps.enable()
96 | call s:G.check_mutliline(1)
97 |
98 | nmap y (VM-Yank)
99 |
100 | if empty(s:v.search) | let @/ = '' | endif
101 | call s:old_updatetime()
102 | endfun
103 |
104 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
105 |
106 | fun! vm#operators#after_yank() abort
107 | "find operator
108 | if g:Vm.finding
109 | let g:Vm.finding = 0
110 | call vm#operators#find(0, s:v.visual_regex)
111 | let s:v.visual_regex = 0
112 | call s:old_updatetime()
113 | nmap y (VM-Yank)
114 | endif
115 | endfun
116 |
117 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
118 |
119 | fun! s:get_region(add_pattern) abort
120 | """Create region with select operator.
121 | let R = s:G.region_at_pos()
122 | if !empty(R) | return R | endif
123 |
124 | let R = vm#region#new(0)
125 | "R.txt can be different because yank != visual yank
126 | call R.update_content()
127 | if a:add_pattern
128 | call s:V.Search.add_if_empty()
129 | endif
130 | call s:F.restore_reg()
131 | return R
132 | endfun
133 |
134 |
135 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
136 | " Find operator
137 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
138 |
139 | fun! vm#operators#find(start, visual, ...) abort
140 | if a:start
141 | if !g:Vm.buffer
142 | call s:backup_map_find()
143 | if a:visual
144 | "use search register if just starting from visual mode
145 | call s:V.Search.get_slash_reg(s:v.oldsearch[0])
146 | endif
147 | else
148 | call s:V.Search.ensure_is_set()
149 | call s:backup_map_find()
150 | endif
151 |
152 | call s:updatetime()
153 | let g:Vm.finding = 1
154 | let s:vblock = a:visual && mode() == "\"
155 | silent! nunmap y
156 | return 'y'
157 | endif
158 |
159 | "set the cursor to the start of the yanked region, then find occurrences until end mark is met
160 | let [endline, endcol] = getpos("']")[1:2]
161 | keepjumps normal! `[
162 | let [startline, startcol] = getpos('.')[1:2]
163 |
164 | if !search(join(s:v.search, '\|'), 'znp', endline)
165 | call s:merge_find()
166 | if !len(s:R())
167 | call vm#reset(1)
168 | endif
169 | return
170 | endif
171 |
172 | let ows = &wrapscan
173 | set nowrapscan
174 | silent keepjumps normal! ygn
175 | if s:vblock
176 | let R = getpos('.')[2]
177 | if !( R < startcol || R > endcol )
178 | call s:G.new_region()
179 | endif
180 | else
181 | call s:G.new_region()
182 | endif
183 |
184 | while 1
185 | if !search(join(s:v.search, '\|'), 'znp', endline) | break | endif
186 | silent keepjumps normal! nygn
187 | if getpos("'[")[1] > endline
188 | break
189 | elseif s:vblock
190 | let R = getpos('.')[2]
191 | if ( R < startcol || R > endcol )
192 | continue
193 | endif
194 | endif
195 | call s:G.new_region()
196 | endwhile
197 | let &wrapscan = ows
198 | call s:merge_find()
199 | endfun
200 |
201 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
202 | " Helpers
203 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
204 |
205 | fun! s:updatetime() abort
206 | """If not using TextYankPost, use CursorHold and reduce &updatetime.
207 | if g:Vm.oldupdate
208 | let &updatetime = 100
209 | endif
210 | endfun
211 |
212 | fun! s:old_updatetime() abort
213 | """Restore old &updatetime value.
214 | if g:Vm.oldupdate
215 | let &updatetime = g:Vm.oldupdate
216 | endif
217 | endfun
218 |
219 | fun! s:backup_map_find() abort
220 | "use temporary regions, they will be merged later
221 | call s:init()
222 | let s:Bytes = copy(s:V.Bytes)
223 | let s:V.Regions = []
224 | let s:V.Bytes = {}
225 | let s:v.index = -1
226 | let s:v.no_search = 1
227 | let s:v.eco = 1
228 | endfun
229 |
230 | fun! s:merge_find() abort
231 | let new_map = copy(s:V.Bytes)
232 | let s:V.Bytes = s:Bytes
233 | call s:G.merge_maps(new_map)
234 | unlet new_map
235 | endfun
236 |
237 | " vim: et ts=4 sw=4 sts=4 :
238 |
--------------------------------------------------------------------------------
/autoload/vm/search.vim:
--------------------------------------------------------------------------------
1 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
2 | " Initialize
3 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
4 |
5 | fun! vm#search#init() abort
6 | let s:V = b:VM_Selection
7 | let s:v = s:V.Vars
8 | let s:F = s:V.Funcs
9 | let s:G = s:V.Global
10 | return s:Search
11 | endfun
12 |
13 |
14 |
15 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
16 | " Search
17 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
18 |
19 | let s:Search = {}
20 | let s:R = { -> s:V.Regions }
21 | let s:no_visual = { p -> substitute(p, '\\%V', '', 'g') }
22 |
23 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
24 |
25 |
26 | fun! s:update_search(p) abort
27 | " Update search patterns, unless s:v.no_search is set.
28 | if s:v.no_search | return | endif
29 |
30 | if !empty(a:p) && index(s:v.search, a:p) < 0 "not in list
31 | call insert(s:v.search, a:p)
32 | endif
33 |
34 | if s:v.eco | let @/ = s:v.search[0]
35 | else | call s:Search.join()
36 | endif
37 | endfun
38 |
39 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
40 |
41 |
42 | fun! s:Search.get_pattern(register) abort
43 | let t = getreg(a:register)
44 | let t = self.escape_pattern(t)
45 | let p = s:v.whole_word ? '\<'.t.'\>' : t
46 | "if whole word, ensure pattern can be found
47 | let p = search(p, 'ncw')? p : t
48 | return p
49 | endfun
50 |
51 |
52 | fun! s:Search.add(...) abort
53 | " Add a new search pattern.
54 | let pat = a:0? a:1 : self.get_pattern(s:v.def_reg)
55 | call s:update_search(pat)
56 | endfun
57 |
58 |
59 | fun! s:Search.add_if_empty(...) abort
60 | " Add a new search pattern, only if no pattern is set.
61 | if empty(s:v.search)
62 | if a:0 | call self.add(a:1)
63 | else | call self.add(s:R()[s:v.index].pat)
64 | endif
65 | endif
66 | endfun
67 |
68 |
69 | fun! s:Search.ensure_is_set(...) abort
70 | " Ensure there is an active search.
71 | if empty(s:v.search)
72 | if !len(s:R()) || empty(s:R()[0].txt)
73 | call self.get_slash_reg()
74 | else
75 | call self.add(self.escape_pattern(s:R()[0].txt))
76 | endif
77 | endif
78 | endfun
79 |
80 |
81 | fun! s:Search.get_from_region() abort
82 | " Get a new search pattern from the selected region, with a fallback.
83 | let r = s:G.region_at_pos()
84 | if !empty(r)
85 | let pat = self.escape_pattern(r.txt)
86 | call s:update_search(pat) | return
87 | endif
88 |
89 | "fallback to first region.txt or @/, if no active search
90 | if empty(s:v.search) | call self.ensure_is_set() | endif
91 | endfun
92 |
93 |
94 | fun! s:Search.get_slash_reg(...) abort
95 | " Get pattern from current "/" register. Use backup register if empty.
96 | if a:0 | let @/ = a:1 | endif
97 | call s:update_search(s:no_visual(getreg('/')))
98 | if empty(s:v.search)
99 | call s:update_search(s:no_visual(s:v.oldsearch[0]))
100 | endif
101 | endfun
102 |
103 |
104 | fun! s:Search.validate() abort
105 | " Check whether the current search is valid, if not, clear the search.
106 | if s:v.eco || empty(s:v.search) | return v:false | endif
107 |
108 | call self.join()
109 |
110 | "pattern found, ok
111 | if search(@/, 'cnw') | return v:true | endif
112 |
113 | while 1
114 | let i = 0
115 | for p in s:v.search
116 | if !search(@/, 'cnw') | call remove(s:v.search, i) | break | endif
117 | let i += 1
118 | endfor
119 | break
120 | endwhile
121 | call self.join()
122 | return v:true
123 | endfun
124 |
125 |
126 | fun! s:Search.update_patterns(...) abort
127 | " Update the search patterns if the active search isn't listed.
128 | let current = a:0? [a:1] : split(@/, '\\|')
129 | for p in current
130 | if index(s:v.search, p) >= 0 | return | endif
131 | endfor
132 | if a:0 | call self.get_from_region()
133 | else | call self.get_slash_reg()
134 | endif
135 | endfun
136 |
137 |
138 | fun! s:Search.escape_pattern(t) abort
139 | return substitute(escape(a:t, '\/.*$^~[]'), "\n", '\\n', "g")
140 | endfun
141 |
142 |
143 | fun! s:Search.join(...) abort
144 | " Join current patterns, optionally replacing them.
145 | if a:0 | let s:v.search = a:1 | endif
146 | let @/ = join(s:v.search, '\|')
147 | endfun
148 |
149 |
150 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
151 | " Search menu and options
152 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
153 |
154 |
155 | fun! s:pattern_rewritten(t, i) abort
156 | " Return true if a pattern has been rewritten.
157 | if @/ == '' | return | endif
158 |
159 | let p = s:v.search[a:i]
160 | if a:t =~ p || p =~ a:t
161 | let old = s:v.search[a:i]
162 | let s:v.search[a:i] = a:t
163 | call s:G.update_region_patterns(a:t)
164 | call s:Search.join()
165 | let [ wm, L ] = [ 'WarningMsg', 'Label' ]
166 | call s:F.msg([['Pattern updated: [', wm ], [old, L],
167 | \ ['] -> [', wm], [a:t, L],
168 | \ ["]\n", wm]])
169 | return 1
170 | endif
171 | endfun
172 |
173 |
174 | fun! s:Search.rewrite(last) abort
175 | " Rewrite patterns, if substrings of the selected text.
176 | let r = s:G.region_at_pos() | if empty(r) | return | endif
177 |
178 | let t = self.escape_pattern(r.txt)
179 |
180 | if a:last
181 | "add a new pattern if not found
182 | if !s:pattern_rewritten(t, 0)
183 | call self.add(t)
184 | endif
185 | else
186 | "rewrite if found among any pattern, else do nothing
187 | for i in range ( len(s:v.search) )
188 | if s:pattern_rewritten(t, i) | break | endif
189 | endfor
190 | endif
191 | endfun
192 |
193 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
194 |
195 |
196 | fun! s:update_current(...) abort
197 | " Update current search pattern to index 0 or .
198 |
199 | if empty(s:v.search) | let @/ = ''
200 | elseif !a:0 | let @/ = s:v.search[0]
201 | elseif a:1 < 0 | let @/ = s:v.search[0]
202 | elseif a:1 >= len(s:v.search) | let @/ = s:v.search[a:1-1]
203 | else | let @/ = s:v.search[a:1]
204 | endif
205 | endfun
206 |
207 |
208 | fun! s:Search.remove(also_regions) abort
209 | " Remove a search pattern, and optionally its associated regions.
210 | let pats = s:v.search
211 |
212 | if !empty(pats)
213 | let s1 = ['Which index? ', 'WarningMsg']
214 | let s2 = [string(s:v.search), 'Type']
215 | call s:F.msg([s1,s2])
216 | let i = nr2char(getchar())
217 | if ( i == "\" ) | return s:F.msg("\tCanceled.\n") | endif
218 | if ( i < 0 || i >= len(pats) ) | return s:F.msg("\tWrong index\n") | endif
219 | call s:F.msg("\n")
220 | let pat = pats[i]
221 | call remove(pats, i)
222 | call s:update_current()
223 | else
224 | return s:F.msg('No search patters yet.')
225 | endif
226 |
227 | if a:also_regions
228 | let i = len(s:R()) - 1 | let removed = 0
229 | while i>=0
230 | if s:R()[i].pat ==# pat
231 | call s:R()[i].remove()
232 | let removed += 1
233 | endif
234 | let i -= 1
235 | endwhile
236 |
237 | if removed | call s:G.update_and_select_region() | endif
238 | endif
239 | endfun
240 |
241 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
242 |
243 |
244 | fun! s:Search.case() abort
245 | " Cycle case settings.
246 | if &smartcase "smartcase -> case sensitive
247 | set nosmartcase
248 | set noignorecase
249 | call s:F.msg([['Search -> ', 'WarningMsg'], [' case sensitive', 'Label']])
250 |
251 | elseif !&ignorecase "case sensitive -> ignorecase
252 | set ignorecase
253 | call s:F.msg([['Search -> ', 'WarningMsg'], [' ignore case', 'Label']])
254 |
255 | else "ignore case -> smartcase
256 | set smartcase
257 | set ignorecase
258 | call s:F.msg([['Search -> ', 'WarningMsg'], [' smartcase', 'Label']])
259 | endif
260 | endfun
261 |
262 |
263 | fun! s:Search.menu() abort
264 | echohl WarningMsg | echo "1 - " | echohl Type | echon "Rewrite Last Search" | echohl None
265 | echohl WarningMsg | echo "2 - " | echohl Type | echon "Rewrite All Search" | echohl None
266 | echohl WarningMsg | echo "3 - " | echohl Type | echon "Read From Search" | echohl None
267 | echohl WarningMsg | echo "4 - " | echohl Type | echon "Add To Search" | echohl None
268 | echohl WarningMsg | echo "5 - " | echohl Type | echon "Remove Search" | echohl None
269 | echohl WarningMsg | echo "6 - " | echohl Type | echon "Remove Search Regions" | echohl None
270 | echohl Directory | echo "Enter an option: " | echohl None
271 | let c = nr2char(getchar())
272 | echon c "\t"
273 | if c == 1
274 | call self.rewrite(1)
275 | elseif c == 2
276 | call self.rewrite(0)
277 | elseif c == 3
278 | call self.get_slash_reg()
279 | elseif c == 4
280 | call self.get_from_region()
281 | elseif c == 5
282 | call self.remove(0)
283 | elseif c == 6
284 | call self.remove(1)
285 | endif
286 | call feedkeys("\", 'n')
287 | endfun
288 |
289 |
290 | " vim: et sw=4 ts=4 sts=4 fdm=indent fdn=1
291 |
--------------------------------------------------------------------------------
/autoload/vm/special/case.vim:
--------------------------------------------------------------------------------
1 | "mostly from abolish.vim, with some minor additions
2 | "abolish.vim by Tim Pope
3 | "https://github.com/tpope/vim-abolish
4 |
5 | fun! vm#special#case#init() abort
6 | let s:V = b:VM_Selection
7 | return s:Case
8 | endfun
9 |
10 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
11 |
12 | let s:X = { -> g:Vm.extend_mode }
13 | let s:R = { -> s:V.Regions }
14 |
15 |
16 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
17 |
18 | let s:Case = {}
19 |
20 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
21 |
22 | fun! s:Case.pascal(word) abort
23 | return substitute(self.camel(a:word),'^.','\u&','')
24 | endfun
25 |
26 | fun! s:Case.camel(word) abort
27 | let word = substitute(a:word,'[.-]','_','g')
28 | let word = substitute(word,' ','_','g')
29 | if word !~# '_' && word =~# '\l'
30 | return substitute(word,'^.','\l&','')
31 | else
32 | return substitute(word,'\C\(_\)\=\(.\)','\=submatch(1)==""?tolower(submatch(2)) : toupper(submatch(2))','g')
33 | endif
34 | endfun
35 |
36 | fun! s:Case.snake(word) abort
37 | let word = substitute(a:word,'::','/','g')
38 | let word = substitute(word,'\(\u\+\)\(\u\l\)','\1_\2','g')
39 | let word = substitute(word,'\(\l\|\d\)\(\u\)','\1_\2','g')
40 | let word = substitute(word,'[.-]','_','g')
41 | let word = substitute(word,' ','_','g')
42 | let word = tolower(word)
43 | return word
44 | endfun
45 |
46 | fun! s:Case.snake_upper(word) abort
47 | return toupper(self.snake(a:word))
48 | endfun
49 |
50 | fun! s:Case.dash(word) abort
51 | return substitute(self.snake(a:word),'_','-','g')
52 | endfun
53 |
54 | fun! s:Case.space(word) abort
55 | return substitute(self.snake(a:word),'_',' ','g')
56 | endfun
57 |
58 | fun! s:Case.dot(word) abort
59 | return substitute(self.snake(a:word),'_','.','g')
60 | endfun
61 |
62 | fun! s:Case.title(word) abort
63 | return substitute(self.space(a:word), '\(\<\w\)','\=toupper(submatch(1))','g')
64 | endfun
65 |
66 | fun! s:Case.lower(word) abort
67 | return tolower(a:word)
68 | endfun
69 |
70 | fun! s:Case.upper(word) abort
71 | return toupper(a:word)
72 | endfun
73 |
74 | fun! s:Case.capitalize(word) abort
75 | return toupper(a:word[0:0]) . tolower(a:word[1:])
76 | endfun
77 |
78 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
79 |
80 | fun! s:Case.menu() abort
81 | if get(g:, 'VM_verbose_commands', 0)
82 | echohl WarningMsg | echo "\tCase Conversion\n---------------------------------"
83 | echohl WarningMsg | echo "u " | echohl Type | echon "lowercase" | echohl None
84 | echohl WarningMsg | echo "U " | echohl Type | echon "UPPERCASE" | echohl None
85 | echohl WarningMsg | echo "C " | echohl Type | echon "Captialize" | echohl None
86 | echohl WarningMsg | echo "t " | echohl Type | echon "Title Case" | echohl None
87 | echohl WarningMsg | echo "c " | echohl Type | echon "camelCase" | echohl None
88 | echohl WarningMsg | echo "P " | echohl Type | echon "PascalCase" | echohl None
89 | echohl WarningMsg | echo "s " | echohl Type | echon "snake_case" | echohl None
90 | echohl WarningMsg | echo "S " | echohl Type | echon "SNAKE_UPPERCASE" | echohl None
91 | echohl WarningMsg | echo "- " | echohl Type | echon "dash-case" | echohl None
92 | echohl WarningMsg | echo ". " | echohl Type | echon "dot.case" | echohl None
93 | echohl WarningMsg | echo " " | echohl Type | echon "space case" | echohl None
94 | echohl WarningMsg | echo "---------------------------------"
95 | echohl Directory | echo "Enter an option: " | echohl None
96 | else
97 | echohl Constant | echo "Case conversion: " | echohl None | echon '(u/U/C/t/c/P/s/S/-/./ )'
98 | endif
99 | let c = nr2char(getchar())
100 | let case = {
101 | \ "u": 'lower', "U": 'upper',
102 | \ "C": 'capitalize', "t": 'title',
103 | \ "c": 'camel', "P": 'pascal',
104 | \ "s": 'snake', "S": 'snake_upper',
105 | \ "-": 'dash', "k": 'remove',
106 | \ ".": 'dot', " ": 'space',
107 | \}
108 | if has_key(case, c)
109 | call self.convert(case[c])
110 | endif
111 | if get(g:, 'VM_verbose_commands', 0)
112 | call feedkeys("\", 'n')
113 | endif
114 | endfun
115 |
116 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
117 |
118 | fun! s:Case.convert(type) abort
119 | if !len(s:R()) | return | endif
120 | if !s:X()
121 | call vm#operators#select(1, 'iw')
122 | endif
123 |
124 | let text = [] | let g:Vm.registers['"'] = text
125 | for r in s:R()
126 | call add(text, eval("self.".a:type."(r.txt)"))
127 | endfor
128 | call b:VM_Selection.Edit.paste(1, 0, 1, '"')
129 | endfun
130 | " vim: et ts=2 sw=2 sts=2 :
131 |
--------------------------------------------------------------------------------
/autoload/vm/themes.vim:
--------------------------------------------------------------------------------
1 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
2 | "Set up highlighting
3 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
4 |
5 | let s:Themes = {}
6 |
7 | augroup VM_reset_theme
8 | au!
9 | au ColorScheme * call vm#themes#init()
10 | augroup END
11 |
12 |
13 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
14 |
15 | fun! vm#themes#init() abort
16 | if !exists('g:Vm') | return | endif
17 |
18 | if !empty(g:VM_highlight_matches)
19 | let out = execute('highlight Search')
20 | if match(out, ' links to ') >= 0
21 | let hi = substitute(out, '^.*links to ', '', '')
22 | let g:Vm.search_hi = "link Search " . hi
23 | else
24 | let hi = strtrans(substitute(out, '^.*xxx ', '', ''))
25 | let hi = substitute(hi, '\^.', '', 'g')
26 | let g:Vm.search_hi = "Search " . hi
27 | endif
28 |
29 | call vm#themes#search_highlight()
30 | endif
31 |
32 | let theme = get(g:, 'VM_theme', '')
33 |
34 | if theme == 'default'
35 | hi! link VM_Mono ErrorMsg
36 | hi! link VM_Cursor Visual
37 | hi! link VM_Extend PmenuSel
38 | hi! link VM_Insert DiffChange
39 | hi! link MultiCursor VM_Cursor
40 |
41 | elseif has_key(s:Themes, theme)
42 | call s:Themes[theme]()
43 | endif
44 | endfun
45 |
46 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
47 |
48 | fun! vm#themes#search_highlight() abort
49 | " Init Search highlight.
50 | let hl = g:VM_highlight_matches
51 | let g:Vm.Search = hl == 'underline' ? 'Search term=underline cterm=underline gui=underline' :
52 | \ hl == 'red' ? 'Search ctermfg=196 guifg=#ff0000' :
53 | \ hl =~ '^hi!\? ' ? substitute(g:VM_highlight_matches, '^hi!\?', '', '')
54 | \ : 'Search term=underline cterm=underline gui=underline'
55 | endfun
56 |
57 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
58 |
59 | fun! vm#themes#load(theme) abort
60 | " Load a theme or set default.
61 | if empty(a:theme) || a:theme == 'default'
62 | let g:VM_theme = 'default'
63 | elseif index(keys(s:Themes), a:theme) < 0
64 | echo "No such theme."
65 | return
66 | else
67 | let g:VM_theme = a:theme
68 | endif
69 | call vm#themes#init()
70 | endfun
71 |
72 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
73 |
74 | fun! vm#themes#complete(A, L, P) abort
75 | let valid = &background == 'light' ? s:Themes._light : s:Themes._dark
76 | return filter(sort(copy(valid)), 'v:val=~#a:A')
77 | endfun
78 |
79 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
80 |
81 | fun! vm#themes#statusline() abort
82 | if !exists('b:visual_multi')
83 | return ''
84 | endif
85 | let v = b:VM_Selection.Vars
86 | let vm = VMInfos()
87 | let color = '%#VM_Extend#'
88 | let single = b:VM_Selection.Vars.single_region ? '%#VM_Mono# SINGLE ' : ''
89 | try
90 | if v.insert
91 | if b:VM_Selection.Insert.replace
92 | let [ mode, color ] = [ 'V-R', '%#VM_Mono#' ]
93 | else
94 | let [ mode, color ] = [ 'V-I', '%#VM_Cursor#' ]
95 | endif
96 | else
97 | let mode = { 'n': 'V-M', 'v': 'V', 'V': 'V-L', "\": 'V-B' }[mode()]
98 | endif
99 | catch
100 | let mode = 'V-M'
101 | endtry
102 | let mode = exists('v.statusline_mode') ? v.statusline_mode : mode
103 | let patterns = string(vm.patterns)[:(winwidth(0)-30)]
104 | return printf("%s %s %s %s %s%s %s %%=%%l:%%c %s %s",
105 | \ color, mode, '%#VM_Insert#', vm.ratio, single, '%#TabLine#',
106 | \ patterns, color, vm.status . ' ')
107 | endfun
108 |
109 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
110 |
111 | let s:Themes._light = ['sand', 'paper', 'lightblue1', 'lightblue2', 'lightpurple1', 'lightpurple2']
112 | let s:Themes._dark = ['iceblue', 'ocean', 'neon', 'purplegray', 'nord', 'codedark', 'spacegray', 'olive', 'sand']
113 |
114 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
115 |
116 | fun! s:Themes.iceblue()
117 | hi! VM_Extend ctermbg=24 guibg=#005f87
118 | hi! VM_Cursor ctermbg=31 ctermfg=237 guibg=#0087af guifg=#87dfff
119 | hi! VM_Insert ctermbg=239 guibg=#4c4e50
120 | hi! VM_Mono ctermbg=180 ctermfg=235 guibg=#dfaf87 guifg=#262626
121 | endfun
122 |
123 | fun! s:Themes.ocean()
124 | hi! VM_Extend ctermbg=25 guibg=#005faf
125 | hi! VM_Cursor ctermbg=39 ctermfg=239 guibg=#87afff guifg=#4e4e4e
126 | hi! VM_Insert ctermbg=239 guibg=#4c4e50
127 | hi! VM_Mono ctermbg=186 ctermfg=239 guibg=#dfdf87 guifg=#4e4e4e
128 | endfun
129 |
130 | fun! s:Themes.neon()
131 | hi! VM_Extend ctermbg=26 ctermfg=109 guibg=#005fdf guifg=#89afaf
132 | hi! VM_Cursor ctermbg=39 ctermfg=239 guibg=#00afff guifg=#4e4e4e
133 | hi! VM_Insert ctermbg=239 guibg=#4c4e50
134 | hi! VM_Mono ctermbg=221 ctermfg=239 guibg=#ffdf5f guifg=#4e4e4e
135 | endfun
136 |
137 | fun! s:Themes.lightblue1()
138 | hi! VM_Extend ctermbg=153 guibg=#afdfff
139 | hi! VM_Cursor ctermbg=111 ctermfg=239 guibg=#87afff guifg=#4e4e4e
140 | hi! VM_Insert ctermbg=180 ctermfg=235 guibg=#dfaf87 guifg=#262626
141 | hi! VM_Mono ctermbg=167 ctermfg=253 guibg=#df5f5f guifg=#dadada cterm=bold term=bold gui=bold
142 | endfun
143 |
144 | fun! s:Themes.lightblue2()
145 | hi! VM_Extend ctermbg=117 guibg=#87dfff
146 | hi! VM_Cursor ctermbg=111 ctermfg=239 guibg=#87afff guifg=#4e4e4e
147 | hi! VM_Insert ctermbg=180 ctermfg=235 guibg=#dfaf87 guifg=#262626
148 | hi! VM_Mono ctermbg=167 ctermfg=253 guibg=#df5f5f guifg=#dadada cterm=bold term=bold gui=bold
149 | endfun
150 |
151 | fun! s:Themes.purplegray()
152 | hi! VM_Extend ctermbg=60 guibg=#544a65
153 | hi! VM_Cursor ctermbg=103 ctermfg=54 guibg=#8787af guifg=#5f0087
154 | hi! VM_Insert ctermbg=239 guibg=#4c4e50
155 | hi! VM_Mono ctermbg=141 ctermfg=235 guibg=#af87ff guifg=#262626
156 | endfun
157 |
158 | fun! s:Themes.nord()
159 | hi! VM_Extend ctermbg=239 guibg=#434C5E
160 | hi! VM_Cursor ctermbg=245 ctermfg=24 guibg=#8a8a8a guifg=#005f87
161 | hi! VM_Insert ctermbg=239 guibg=#4c4e50
162 | hi! VM_Mono ctermbg=131 ctermfg=235 guibg=#AF5F5F guifg=#262626
163 | endfun
164 |
165 | fun! s:Themes.codedark()
166 | hi! VM_Extend ctermbg=242 guibg=#264F78
167 | hi! VM_Cursor ctermbg=239 ctermfg=252 guibg=#6A7D89 guifg=#C5D4DD
168 | hi! VM_Insert ctermbg=239 guibg=#4c4e50
169 | hi! VM_Mono ctermbg=131 ctermfg=235 guibg=#AF5F5F guifg=#262626
170 | endfun
171 |
172 | fun! s:Themes.spacegray()
173 | hi! VM_Extend ctermbg=237 guibg=#404040
174 | hi! VM_Cursor ctermbg=242 ctermfg=239 guibg=Grey50 guifg=#4e4e4e
175 | hi! VM_Insert ctermbg=239 guibg=#4c4e50
176 | hi! VM_Mono ctermbg=131 ctermfg=235 guibg=#AF5F5F guifg=#262626
177 | endfun
178 |
179 | fun! s:Themes.sand()
180 | hi! VM_Extend ctermbg=143 ctermfg=0 guibg=darkkhaki guifg=black
181 | hi! VM_Cursor ctermbg=64 ctermfg=186 guibg=olivedrab guifg=khaki
182 | hi! VM_Insert ctermbg=239 guibg=#4c4e50
183 | hi! VM_Mono ctermbg=131 ctermfg=235 guibg=#AF5F5F guifg=#262626
184 | endfun
185 |
186 | fun! s:Themes.paper()
187 | hi! VM_Extend ctermbg=250 ctermfg=16 guibg=#bfbcaf guifg=black
188 | hi! VM_Cursor ctermbg=239 ctermfg=188 guibg=#4c4e50 guifg=#d8d5c7
189 | hi! VM_Insert ctermbg=167 ctermfg=253 guibg=#df5f5f guifg=#dadada cterm=bold term=bold gui=bold
190 | hi! VM_Mono ctermbg=16 ctermfg=188 guibg=#000000 guifg=#d8d5c7
191 | endfun
192 |
193 | fun! s:Themes.olive()
194 | hi! VM_Extend ctermbg=3 ctermfg=0 guibg=olive guifg=black
195 | hi! VM_Cursor ctermbg=64 ctermfg=186 guibg=olivedrab guifg=khaki
196 | hi! VM_Insert ctermbg=239 guibg=#4c4e50
197 | hi! VM_Mono ctermbg=131 ctermfg=235 guibg=#AF5F5F guifg=#262626
198 | endfun
199 |
200 | fun! s:Themes.lightpurple1()
201 | hi! VM_Extend ctermbg=225 guibg=#ffdfff
202 | hi! VM_Cursor ctermbg=183 ctermfg=54 guibg=#dfafff guifg=#5f0087 cterm=bold term=bold gui=bold
203 | hi! VM_Insert ctermbg=146 ctermfg=235 guibg=#afafdf guifg=#262626
204 | hi! VM_Mono ctermbg=135 ctermfg=225 guibg=#af5fff guifg=#ffdfff cterm=bold term=bold gui=bold
205 | endfun
206 |
207 | fun! s:Themes.lightpurple2()
208 | hi! VM_Extend ctermbg=189 guibg=#dfdfff
209 | hi! VM_Cursor ctermbg=183 ctermfg=54 guibg=#dfafff guifg=#5f0087 cterm=bold term=bold gui=bold
210 | hi! VM_Insert ctermbg=225 ctermfg=235 guibg=#ffdfff guifg=#262626
211 | hi! VM_Mono ctermbg=135 ctermfg=225 guibg=#af5fff guifg=#ffdfff cterm=bold term=bold gui=bold
212 | endfun
213 |
214 | " vim: et ts=2 sw=2 sts=2 :
215 |
--------------------------------------------------------------------------------
/autoload/vm/variables.vim:
--------------------------------------------------------------------------------
1 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
2 | " Set vim variable to VM compatible values
3 |
4 | fun! vm#variables#set() abort
5 | let F = b:VM_Selection.Funcs
6 | let v = b:VM_Selection.Vars
7 |
8 | " disable folding, but keep winline
9 | if &foldenable
10 | call F.Scroll.get(1)
11 | let v.oldfold = 1
12 | set nofoldenable
13 | call F.Scroll.restore()
14 | endif
15 |
16 | if g:VM_case_setting ==? 'smart'
17 | set smartcase
18 | set ignorecase
19 | elseif g:VM_case_setting ==? 'sensitive'
20 | set nosmartcase
21 | set noignorecase
22 | elseif g:VM_case_setting ==? 'ignore'
23 | set nosmartcase
24 | set ignorecase
25 | endif
26 |
27 | "force default register
28 | set clipboard=
29 |
30 | "disable conceal
31 | let &l:conceallevel = vm#comp#conceallevel()
32 | set concealcursor=
33 |
34 | set virtualedit=onemore
35 | set ww=h,l,<,>
36 | set lz
37 |
38 | if get(g:, 'VM_cmdheight', 1) > 1
39 | let &ch = g:VM_cmdheight
40 | endif
41 | endfun
42 |
43 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
44 | " Init VM variables
45 |
46 | fun! vm#variables#init() abort
47 | let F = b:VM_Selection.Funcs
48 | let v = b:VM_Selection.Vars
49 |
50 | "init search
51 | let v.def_reg = F.default_reg()
52 | let v.oldreg = F.get_reg()
53 | let v.oldregs_1_9 = F.get_regs_1_9()
54 | let v.oldsearch = [getreg("/"), getregtype("/")]
55 | let v.noh = !v:hlsearch ? 'noh|' : ''
56 |
57 | "store old vars
58 | let v.oldhls = &hlsearch
59 | let v.oldvirtual = &virtualedit
60 | let v.oldwhichwrap = &whichwrap
61 | let v.oldlz = &lz
62 | let v.oldch = &ch
63 | let v.oldcase = [&smartcase, &ignorecase]
64 | let v.indentkeys = &indentkeys
65 | let v.cinkeys = &cinkeys
66 | let v.synmaxcol = &synmaxcol
67 | let v.oldmatches = getmatches()
68 | let v.clipboard = &clipboard
69 | let v.textwidth = &textwidth
70 | let v.conceallevel = &conceallevel
71 | let v.concealcursor = &concealcursor
72 | let v.softtabstop = &softtabstop
73 | let v.statusline = &statusline
74 |
75 | "init new vars
76 |
77 | let v.search = []
78 | let v.IDs_list = []
79 | let v.ID = 0
80 | let v.index = -1
81 | let v.direction = 1
82 | let v.nav_direction = 1
83 | let v.auto = 0
84 | let v.silence = 0
85 | let v.eco = 0
86 | let v.single_region = 0
87 | let v.using_regex = 0
88 | let v.multiline = 0
89 | let v.yanked = 0
90 | let v.merge = 0
91 | let v.insert = 0
92 | let v.whole_word = 0
93 | let v.winline = 0
94 | let v.restore_scroll = 0
95 | let v.find_all_overlap = 0
96 | let v.dot = ''
97 | let v.no_search = 0
98 | let v.visual_regex = 0
99 | let v.use_register = v.def_reg
100 | let v.deleting = 0
101 | let v.vmarks = [getpos("'<"), getpos("'>")]
102 | endfun
103 |
104 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
105 | " Reset vim variables to previous values
106 |
107 | fun! vm#variables#reset() abort
108 | let v = b:VM_Selection.Vars
109 |
110 | if !v.oldhls
111 | set nohlsearch
112 | endif
113 |
114 | let &virtualedit = v.oldvirtual
115 | let &whichwrap = v.oldwhichwrap
116 | let &smartcase = v.oldcase[0]
117 | let &ignorecase = v.oldcase[1]
118 | let &lz = v.oldlz
119 | let &cmdheight = v.oldch
120 | let &clipboard = v.clipboard
121 |
122 | let &l:indentkeys = v.indentkeys
123 | let &l:cinkeys = v.cinkeys
124 | let &l:synmaxcol = v.synmaxcol
125 | let &l:textwidth = v.textwidth
126 | let &l:softtabstop = v.softtabstop
127 | let &l:conceallevel = v.conceallevel
128 | let &l:concealcursor = v.concealcursor
129 |
130 | if get(g:, 'VM_set_statusline', 2)
131 | let &l:statusline = v.statusline
132 | endif
133 |
134 | silent! unlet b:VM_skip_reset_once_on_bufleave
135 | endfun
136 |
137 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
138 | " Reset VM global variables
139 |
140 | fun! vm#variables#reset_globals()
141 | let b:VM_Backup = {}
142 | let b:VM_Selection = {}
143 | let g:Vm.buffer = 0
144 | let g:Vm.extend_mode = 0
145 | let g:Vm.finding = 0
146 | endfun
147 |
148 | " vim: et ts=2 sw=2 sts=2 tw=79 :
149 |
--------------------------------------------------------------------------------
/autoload/vm/visual.vim:
--------------------------------------------------------------------------------
1 | "commands to add/subtract regions with visual selection
2 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
3 |
4 | fun! vm#visual#add(mode) abort
5 | " Add visually selected region to current regions.
6 | call s:backup_map()
7 | let pos = getpos('.')[1:2]
8 |
9 | if a:mode ==# 'v' | call s:vchar()
10 | elseif a:mode ==# 'V' | call s:vline()
11 | else | let s:v.direction = s:vblock(1)
12 | endif
13 |
14 | call s:visual_merge()
15 |
16 | if a:mode ==# 'V'
17 | call s:G.split_lines()
18 | call s:G.remove_empty_lines()
19 | elseif a:mode ==# 'v'
20 | for r in s:R()
21 | if r.h | let s:v.multiline = 1 | break | endif
22 | endfor
23 | endif
24 |
25 | call s:G.update_and_select_region(pos)
26 | endfun
27 |
28 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
29 |
30 | fun! vm#visual#subtract(mode) abort
31 | " Subtract visually selected region from current regions.
32 | let X = s:backup_map()
33 |
34 | if a:mode ==# 'v' | call s:vchar()
35 | elseif a:mode ==# 'V' | call s:vline()
36 | else | call s:vblock(1)
37 | endif
38 |
39 | call s:visual_subtract()
40 | call s:G.update_and_select_region({'id': s:v.IDs_list[-1]})
41 | if X | call s:G.cursor_mode() | endif
42 | endfun
43 |
44 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
45 |
46 | fun! vm#visual#reduce() abort
47 | " Remove regions outside of visual selection.
48 | let X = s:backup_map()
49 | call s:G.rebuild_from_map(s:Bytes, [s:F.pos2byte("'<"), s:F.pos2byte("'>")])
50 | if X | call s:G.cursor_mode() | endif
51 | call s:G.update_and_select_region()
52 | endfun
53 |
54 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
55 |
56 | fun! vm#visual#cursors(mode) abort
57 | " Create cursors, one for each line of the visual selection.
58 | call s:init()
59 | let [pos, start, end] = [getpos('.')[1:2],
60 | \ getpos("'<")[1:2], getpos("'>")[1:2]]
61 |
62 | call s:create_cursors(start, end)
63 |
64 | if a:mode ==# 'V' && get(g:, 'VM_autoremove_empty_lines', 1)
65 | call s:G.remove_empty_lines()
66 | endif
67 |
68 | call s:G.update_and_select_region(pos)
69 | endfun
70 |
71 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
72 |
73 | fun! vm#visual#split() abort
74 | " Split regions with regex pattern.
75 | call s:init()
76 | if !len(s:R()) | return
77 | elseif !s:X() | return s:F.msg('Not in cursor mode.') | endif
78 |
79 | echohl Type | let pat = input('Pattern to remove > ') | echohl None
80 | if empty(pat) | return s:F.msg('Command aborted.') | endif
81 |
82 | let start = s:R()[0] "first region
83 | let stop = s:R()[-1] "last region
84 |
85 | call s:F.Cursor(start.A) "check for a match first
86 | if !search(pat, 'nczW', stop.L) "search method: accept at cursor position
87 | call s:F.msg("\t\tPattern not found")
88 | return s:G.select_region(s:v.index)
89 | endif
90 |
91 | call s:backup_map()
92 |
93 | "backup old patterns and create new regions
94 | let oldsearch = copy(s:v.search)
95 | call s:V.Search.get_slash_reg(pat)
96 |
97 | call s:G.get_all_regions(start.A, stop.B)
98 |
99 | "subtract regions and rebuild from map
100 | call s:visual_subtract()
101 | call s:V.Search.join(oldsearch)
102 | call s:G.update_and_select_region()
103 | endfun
104 |
105 |
106 |
107 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
108 | " Helpers
109 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
110 |
111 | fun! s:vchar() abort
112 | "characterwise selection
113 | silent keepjumps normal! ``]
114 | call s:G.check_mutliline(0, s:G.new_region())
115 | endfun
116 |
117 |
118 | fun! s:vline() abort
119 | "linewise selection
120 | silent keepjumps normal! '`]
121 | call s:G.new_region()
122 | endfun
123 |
124 |
125 | fun! s:vblock(extend) abort
126 | "blockwise selection
127 | let start = getpos("'<")[1:2]
128 | let end = getpos("'>")[1:2]
129 |
130 | if ( start[1] > end[1] )
131 | " swap columns because top-right or bottom-left corner is selected
132 | let temp = start[1]
133 | let start[1] = end[1]
134 | let end[1] = temp
135 | let inverted = line(".") == line("'>")
136 | else
137 | let inverted = line(".") == line("'<")
138 | endif
139 |
140 | let block_width = abs(virtcol("'>") - virtcol("'<"))
141 |
142 | call s:create_cursors(start, end)
143 |
144 | if a:extend && block_width
145 | call vm#commands#motion('l', block_width, 1, 0)
146 | endif
147 | return !inverted
148 | endfun
149 |
150 |
151 | fun! s:backup_map() abort
152 | "use temporary regions, they will be merged later
153 | call s:init()
154 | let X = s:G.extend_mode()
155 | let s:Bytes = copy(s:V.Bytes)
156 | call s:G.erase_regions()
157 | let s:v.no_search = 1
158 | let s:v.eco = 1
159 | return X
160 | endfun
161 |
162 |
163 | fun! s:visual_merge() abort
164 | "merge regions
165 | let new_map = copy(s:V.Bytes)
166 | let s:V.Bytes = s:Bytes
167 | call s:G.merge_maps(new_map)
168 | unlet new_map
169 | endfun
170 |
171 |
172 | fun! s:visual_subtract() abort
173 | "subtract regions
174 | let new_map = copy(s:V.Bytes)
175 | let s:V.Bytes = s:Bytes
176 | call s:G.subtract_maps(new_map)
177 | unlet new_map
178 | endfun
179 |
180 |
181 | fun! s:init() abort
182 | "init script vars
183 | let s:V = b:VM_Selection
184 | let s:v = s:V.Vars
185 | let s:G = s:V.Global
186 | let s:F = s:V.Funcs
187 | endfun
188 |
189 |
190 | fun! s:create_cursors(start, end) abort
191 | "create cursors that span over visual selection
192 | call cursor(a:start)
193 |
194 | if ( a:end[0] > a:start[0] )
195 | while line('.') < a:end[0]
196 | call vm#commands#add_cursor_down(0, 1)
197 | endwhile
198 |
199 | elseif empty(s:G.region_at_pos())
200 | " ensure there's at least a cursor
201 | call s:G.new_cursor()
202 | endif
203 | endfun
204 |
205 |
206 | let s:R = { -> s:V.Regions }
207 | let s:X = { -> g:Vm.extend_mode }
208 |
209 |
210 | " vim: et sw=4 ts=4 sts=4 fdm=indent fdn=1
211 |
--------------------------------------------------------------------------------
/doc/vm-ex-commands.txt:
--------------------------------------------------------------------------------
1 | *vm-ex-commands.txt* Version 0.5.0 Last change: September 27 2019
2 |
3 | EX COMMANDS *vm-ex-commands*
4 | ==============================================================================
5 |
6 | The following Ex commands can be run at any time:
7 |
8 | |:VMDebug|
9 | |:VMClear|
10 | |:VMRegisters|
11 | |:VMSearch|
12 | |:VMLive|
13 |
14 | The following are only available inside a VM session:
15 |
16 | |:VMSort|
17 | |:VMQfix|
18 | |:VMFilterRegions|
19 | |:VMFilterLines|
20 | |:VMRegionsToBuffer|
21 | |:VMMassTranspose|
22 |
23 | Some of these commands are available in the `tools menu` (default mapping: `\\``).
24 |
25 | ==============================================================================
26 |
27 | VMDebug *:VMDebug*
28 |
29 | Print in the command line any error relative to the current buffer (if some
30 | mappings could not be applied).
31 |
32 | ------------------------------------------------------------------------------
33 |
34 | VMClear *:VMClear*
35 |
36 | Clear any remnant of VM-specific highlight matches in the current buffer. It
37 | can be necessary if VM exited with errors for some reason.
38 |
39 | ------------------------------------------------------------------------------
40 |
41 | [range] VMSearch[!] [pattern] *:VMSearch*
42 |
43 | Select all regions in [range] that matches the current search register
44 | |quote/|, or the given [pattern]. If is used, ignore [range], and just
45 | select the next occurrence. Examples:
46 | >
47 | :VMSearch! -> select the next occurrence of @/
48 | :VMSearch! \ -> select the next occurrence of \
49 | :VMSearch -> select occurrences of @/ in current line
50 | :%VMSearch \ -> select all occurrences of \
51 | :'<,'>VMSearch \ -> range is visual selection
52 |
53 | ------------------------------------------------------------------------------
54 |
55 | VMLive *:VMLive*
56 |
57 | Toggle |g:VM_live_editing|.
58 |
59 | ------------------------------------------------------------------------------
60 |
61 | VMRegisters[!] [register] *:VMRegisters*
62 |
63 | Show VM registers, or single [register]. With , delete a [register], or
64 | all of them if no register is specified.
65 |
66 | ------------------------------------------------------------------------------
67 |
68 | VMSort [arg] *:VMSort*
69 |
70 | Sort regions. Optional [arg] is the same as {func} of the |sort()| function.
71 |
72 | ------------------------------------------------------------------------------
73 |
74 | VMQfix[!] *:VMQfix*
75 |
76 | Populate the |quickfix| window with the regions lines.
77 | If run with , use positions and contents instead. If run from cursor
78 | mode, the text will be empty, but the positions will be valid.
79 |
80 | ------------------------------------------------------------------------------
81 |
82 | VMFilterRegions[!] [pattern] *:VMFilterRegions*
83 |
84 | Filter regions so that you keep only the ones match [pattern].
85 | If a is used, keep the regions that don't match [pattern].
86 | If no [pattern] is given, prompt for a pattern.
87 |
88 | In prompt mode, `Ctrl-x` can change the filter type, cycling among:
89 | >
90 | pattern: keep regions that match pattern
91 | !pattern: keep regions that don't match pattern
92 | expression: keep the regions that match the vim expression
93 | <
94 | ------------------------------------------------------------------------------
95 |
96 | VMFilterLines *:VMFilterLines*
97 |
98 | Open a new buffer, filling it with all lines that contain at least a region.
99 | When saving (|:write| or |:update|) this temporary buffer, the lines in the
100 | original buffer are replaced with the corresponding lines in this buffer.
101 | If the number of lines doesn't match for some reason, the command fails.
102 | Use this feature only if you know what you're doing.
103 |
104 | ------------------------------------------------------------------------------
105 |
106 | VMRegionsToBuffer *:VMRegionsToBuffer*
107 |
108 | Open a new buffer, filling it with the contents of each region, one per line.
109 | This works similarly to |:VMFilterLines|, in that it can update the original
110 | buffer on save. Use with caution.
111 | If the number of lines doesn't match the number of regions, the command fails.
112 |
113 | ------------------------------------------------------------------------------
114 |
115 | VMMassTranspose *:VMMassTranspose*
116 |
117 | If you select two patterns, replace all occurrences of the first pattern with
118 | the second one, and viceversa.
119 |
120 |
121 |
122 |
123 | vim: ft=help et sw=2 ts=2 sts=2 tw=78
124 |
125 |
--------------------------------------------------------------------------------
/doc/vm-faq.txt:
--------------------------------------------------------------------------------
1 | *vm-faq.txt* Version 0.3.0 Last change: June 15 2019
2 |
3 | FREQUENTLY ASKED QUESTIONS *vm-faq*
4 | ===============================================================================
5 |
6 | *vm-faq-errors*
7 | VM has quit with errors and I can't clear VM highlight matches.~
8 |
9 | Run |:VMClear|.
10 |
11 | -------------------------------------------------------------------------------
12 | *vm-faq-search*
13 | How to start VM looking for a pattern? ~
14 |
15 | Run |:VMSearch|. It also accepts a pattern and a |range| (% finds all matches).
16 |
17 | -------------------------------------------------------------------------------
18 | *vm-faq-mappings*
19 | I want to remap the main VM mappings.~
20 |
21 | You have to initialize the mappings dictionary and replace individual mappings
22 | with your own. Below there are some examples.
23 |
24 | For a full list of mappings and other informations, see |vm-mappings.txt|.
25 | >
26 | let g:VM_maps = {}
27 | let g:VM_maps["Exit"] = '' " quit VM
28 | let g:VM_maps['Find Under'] = '' " replace C-n
29 | let g:VM_maps['Find Subword Under'] = '' " replace visual C-n
30 | let g:VM_maps["Add Cursor Down"] = '' " new cursor down
31 | let g:VM_maps["Add Cursor Up"] = '' " new cursor up
32 | let g:VM_maps["Toggle Mappings"] = '' " toggle VM buffer mappings
33 |
34 | To disable a specific mapping, set it to an empty string:
35 | >
36 | let g:VM_maps["Select Operator"] = ''
37 | <
38 | -------------------------------------------------------------------------------
39 | *vm-faq-disable-mappings*
40 | How can I temporarily disable VM mappings? ~
41 |
42 | VM has a for that. Normally it is bound to |VM_leader| + , but
43 | unless you use as |VM_leader|, you may prefer an easier mapping:
44 | >
45 | let g:VM_maps["Toggle Mappings"] = ''
46 | <
47 |
48 | -------------------------------------------------------------------------------
49 | *vm-faq-functions*
50 | How can I do something before VM starts, or after VM ends? ~
51 |
52 | You can create these functions: >
53 | function! VM_Start()
54 | function! VM_Exit()
55 | <
56 | Or you can rely on a user autocommand: >
57 | autocmd User visual_multi_start call MyVmStart()
58 | autocmd User visual_multi_exit call MyVmExit()
59 | <
60 | If you want to override some VM mapping (and you know what you're doing), this
61 | autocommand is triggered after mappings have been set:
62 | >
63 | autocmd User visual_multi_mappings call MyVmMappings()
64 |
65 | If you need to perform some action before/after `each` VM command is run: >
66 | autocmd User visual_multi_before_cmd call MyFunc1()
67 | autocmd User visual_multi_after_cmd call MyFunc2()
68 |
69 | -------------------------------------------------------------------------------
70 | *vm-faq-remap*
71 | How can I remap x in VM? ~
72 |
73 | There are several ways: either do a remap using the functions described above,
74 | e.g.:
75 | >
76 | function! VM_Start()
77 | nmap
78 | imap
79 | endfunction
80 |
81 | function! VM_Exit()
82 | nunmap
83 | iunmap
84 | endfunction
85 | <
86 | or check one of the following settings:
87 |
88 | |g:VM_custom_remaps|
89 | |g:VM_custom_noremaps|
90 | |g:VM_custom_motions|
91 |
92 |
93 | -------------------------------------------------------------------------------
94 | *vm-faq-custom-mappings*
95 | Can I have a mapping for...? ~
96 |
97 | If you find yourself repeating a certain action, you can create mappings like:
98 | >
99 | nmap cp vip(VM-Visual-Cursors)
100 | <
101 | This would create a column of cursors that spans over the current inner
102 | paragraph.
103 | >
104 | Obviously you can just use keys rather than plugs:
105 | >
106 | nmap cp vip\\c
107 | <
108 |
109 | vim: ft=help et sw=2 ts=2 sts=2 tw=79
110 |
--------------------------------------------------------------------------------
/doc/vm-mappings.txt:
--------------------------------------------------------------------------------
1 | *vm-mappings.txt* Version 0.3.0 Last change: June 11 2019
2 |
3 | MAPPINGS *vm-mappings-all*
4 | ===============================================================================
5 | *g:VM_maps*
6 | To change any mapping you must first initialize the variable:
7 | >
8 | let g:VM_maps = {}
9 | <
10 | Then you can assign it to a new key:
11 | >
12 | let g:VM_maps["Select Operator"] = 'gs'
13 | <
14 | To disable a specific mapping, set it to an empty string:
15 | >
16 | let g:VM_maps["Select Operator"] = ''
17 | <
18 | To enable undo/redo (still experimental):
19 | >
20 | let g:VM_maps["Undo"] = 'u'
21 | let g:VM_maps["Redo"] = ''
22 |
23 | Example of SublimeText-like mappings:
24 | >
25 | let g:VM_maps['Find Under'] = ''
26 | let g:VM_maps['Find Subword Under'] = ''
27 | let g:VM_maps["Select Cursor Down"] = ''
28 | let g:VM_maps["Select Cursor Up"] = ''
29 | <
30 | Example of |vim-multiple-cursors| -like mappings:
31 | >
32 | let g:VM_maps['Select All'] = ''
33 | let g:VM_maps['Visual All'] = ''
34 | let g:VM_maps['Skip Region'] = ''
35 | let g:VM_maps['Increase'] = '+'
36 | let g:VM_maps['Decrease'] = '-'
37 | <
38 | For Colemak users, see |vm-colemak|.
39 |
40 |
41 | -------------------------------------------------------------------------------
42 | DEFAULT MAPPINGS *vm-mappings-default*
43 | *g:VM_default_mappings*
44 |
45 | Default mappings are `permanent`, that is, always available, and applied when
46 | Vim starts. Buffer mappings instead are applied per-buffer, when VM is started.
47 |
48 | Permanent mappings, except , can be disabled with:
49 | >
50 | let g:VM_default_mappings = 0
51 | < *g:VM_mouse_mappings*
52 | Mouse mappings (also permanent) can be enabled with:
53 | >
54 | let g:VM_mouse_mappings = 1
55 | <
56 | *g:VM_leader-dict*
57 | Mappings preceded by \\ are meant prefixed with |g:VM_leader|.
58 |
59 | Some of the permanent/visual mappings use the |g:VM_leader| as well, and you
60 | could want to use a different one for them. In this case you can define the
61 | leader as a dictionary:
62 | >
63 | let g:VM_leader = {'default': '\', 'visual': '\', 'buffer': 'z'}
64 |
65 | That is, the VM leader used for default (permanent) normal mode mappings,
66 | visual mappings, and buffer mappings.
67 |
68 | -------------------------------------------------------------------------------
69 | Name Keys Notes ~
70 | -------------------------------------------------------------------------------
71 | *vm-mappings-qr*
72 | Exit quit VM
73 | Find Under select the word under cursor
74 | Find Subword Under from visual mode, without word boundaries
75 | Add Cursor Down create cursors vertically
76 | Add Cursor Up ,, ,, ,,
77 | Select All \\A select all occurrences of a word
78 | Start Regex Search \\/ create a selection with regex search
79 | Add Cursor At Pos \\\ add a single cursor at current position
80 | Reselect Last \\gS reselect set of regions of last VM session
81 |
82 | Mouse Cursor create a cursor where clicked
83 | Mouse Word select a word where clicked
84 | Mouse Column create a column, from current cursor to
85 | clicked position
86 |
87 |
88 | -------------------------------------------------------------------------------
89 | VISUAL MODE MAPPINGS *vm-mappings-visual*
90 |
91 | Of these, `Visual Subtract` and `Visual Reduce` are buffer mappings.
92 |
93 | -------------------------------------------------------------------------------
94 | Name Keys Notes ~
95 | -------------------------------------------------------------------------------
96 |
97 | Visual All \\A select all occurrences of visual selection
98 | Visual Regex \\/ find a pattern in visual selection
99 | Visual Find \\f find all patterns ( or @/ ) from visual mode
100 | Visual Cursors \\c create a column of cursors from visual mode
101 | Visual Add \\a create a region from visual mode
102 | Visual Subtract \\s remove regions in current visual selection
103 | Visual Reduce \\r remove regions outside of visual selection
104 |
105 |
106 |
107 | -------------------------------------------------------------------------------
108 | BUFFER MAPPINGS *vm-mappings-buffer*
109 |
110 | Not included are the mappings that mimic default vim commands (motions, several
111 | text objects), unless specified.
112 |
113 | -------------------------------------------------------------------------------
114 | Name Keys Notes ~
115 | -------------------------------------------------------------------------------
116 |
117 | Find Next n find next occurrence
118 | Find Prev N find previous occurrence
119 | Goto Next ] go to next selected region
120 | Goto Prev [ go to previous selected region
121 | Seek Next fast go to next (from next page)
122 | Seek Prev fast go to previous (from previous page)
123 | Skip Region q skip and find to next
124 | Remove Region Q remove region under cursor
125 | Slash Search g/ extend/move cursors with /
126 | Replace R replace in regions, or start replace mode
127 | Toggle Multiline M see |vm-multiline-mode|
128 |
129 | The following are valid in extend-mode: ~
130 |
131 | Surround S requires |vim-surround| plugin
132 | Move Right move all selections to the right
133 | Move Left ,, ,, to the left
134 |
135 | The following are valid in insert-mode and single-region-mode: ~
136 |
137 | I Next move to next cursor
138 | I Prev move to previous cursor
139 |
140 | Operators: ~
141 |
142 | Select Operator s see |vm-select-operator|
143 | Find Operator m see |vm-find-operator|
144 |
145 | Special commands: ~
146 |
147 | Increase increase numbers (same as vim)
148 | Decrease decrease numbers (same as vim)
149 | gIncrease g progressively increase numbers (like |v_g_CTRL-A|)
150 | gDecrease g progressively decrease numbers (like |v_g_CTRL-X|)
151 | Alpha-Increase \\ same but +alpha (see |'nrformats'|)
152 | Alpha-Decrease \\ ,,
153 |
154 | Commands:~
155 |
156 | Transpose \\t transpose
157 | Align \\a align regions
158 | Align Char \\< align by character
159 | Align Regex \\> align by regex
160 | Split Regions \\s subtract pattern from regions
161 | Filter Regions \\f filter regions by pattern/expression
162 | Transform Regions \\e transform regions with expression
163 | Rewrite Last Search \\r rewrite last pattern to match current region
164 | Merge Regions \\m merge overlapping regions
165 | Duplicate \\d duplicate regions
166 | Shrink \\- reduce regions from the sides
167 | Enlarge \\+ enlarge regions from the sides
168 | One Per Line \\L keep at most one region per line
169 | Numbers \\n see |vm-numbering|
170 | Numbers Append \\N ,,
171 |
172 | Run Normal \\z Run Normal
173 | Run Visual \\v Run Visual
174 | Run Ex \\x Run Ex
175 | Run Last Normal \\Z Run Last Normal
176 | Run Last Visual \\V Run Last Visual
177 | Run Last Ex \\X Run Last Ex
178 | Run Macro \\@ Run Macro
179 |
180 | Options and menus: ~
181 |
182 | Tools Menu \\` filter lines to buffer, etc
183 | Case Conversion Menu \\C works better in extend mode
184 | Show Registers \\" show VM registers in the command line
185 | Toggle Whole Word \\w toggle whole word search
186 | Case Setting \\c cycle case setting ('scs' -> 'noic' -> 'ic')
187 | Toggle Mappings \\ toggle VM mappings
188 | Toggle Single Region \\ toggle single region mode
189 |
190 |
191 | -------------------------------------------------------------------------------
192 | COLEMAK MAPPINGS *vm-colemak*
193 |
194 | For Colemak users or more extensive remappings: this mapping system wasn't
195 | really designed for that. Take into account that when you assign keys this way,
196 | you assign a `VM ` to a key, you aren't remapping a key to another key.
197 | For example if you do:
198 | >
199 | let g:VM_maps['i'] = 'a'
200 | let g:VM_maps['I'] = 'A'
201 | let g:VM_maps['a'] = 'o'
202 | let g:VM_maps['A'] = 'O'
203 |
204 | You aren't remapping `i` to `a`, but the other way around. This translates to:
205 | >
206 | nmap a (VM-i)
207 | nmap A (VM-I)
208 | nmap o (VM-a)
209 | nmap O (VM-A)
210 |
211 | And the VM plugs have a fixed meaning: if you remapped `i`, it's not that
212 | `(VM-i)` means something different, it still means "start insert mode
213 | before the cursor".
214 |
215 | This means that you have to take other VM mappings into account, that could
216 | overwrite your mappings. In this case you would also need to map something to
217 | overtake `o` and `O` functions, so this would work:
218 | >
219 | let g:VM_maps['i'] = 'a'
220 | let g:VM_maps['I'] = 'A'
221 | let g:VM_maps['a'] = 'o'
222 | let g:VM_maps['A'] = 'O'
223 | let g:VM_maps['o'] = 'i'
224 | let g:VM_maps['O'] = 'I'
225 | <
226 | In general if you switch `A` and `B`, writing
227 | >
228 | map[A] = B
229 | map[B] = A
230 |
231 | is enough, but if you want to change more keys, you have to remap all
232 | combinations to avoid that some of the mappings won't work.
233 |
234 |
235 | vim: ft=help et sw=2 ts=2 sts=2 tw=79
236 |
--------------------------------------------------------------------------------
/doc/vm-settings.txt:
--------------------------------------------------------------------------------
1 | *vm-settings.txt* Version 0.3.0 Last change: June 13 2019
2 |
3 | Settings |vm-settings|
4 | Highlight |vm-highlight|
5 |
6 |
7 |
8 | SETTINGS *vm-settings*
9 | ===============================================================================
10 |
11 | Hopefully you won't need to alter any of these.
12 |
13 |
14 | *g:VM_highlight_matches* Default: 'underline'
15 |
16 | Controls VM default highlighting style for patterns matched, but not
17 | selected. Possible useful values are 'underline' and 'red'.
18 | Otherwise an empty string if you want the normal |Search| highlight, or
19 | a full highlight command (help |:hi|), e.g.: >
20 | let g:VM_highlight_matches = 'hi Search ctermfg=228 cterm=underline'
21 | let g:VM_highlight_matches = 'hi! link Search PmenuSel'
22 | <
23 |
24 | *g:VM_set_statusline* Default: 2
25 |
26 | Enable statusline when VM is active.
27 | With a value of 1, the statusline will be set once, on VM start.
28 | With a value of 2, the statusline will be refreshed on |CursorHold| event.
29 | With a value of 3, also on |CursorMoved| event.
30 |
31 |
32 | *g:VM_silent_exit* Default: 0
33 |
34 | Don't display a message when exiting VM. You may prefer it if you already set
35 | up statusline integration.
36 |
37 |
38 | *g:VM_quit_after_leaving_insert_mode* Default: 0
39 |
40 | So that you don't have to type twice. If you set this to 1, maybe you
41 | can remap `Reselect Last`, so that you can quickly restart VM with your last
42 | selection. See |vm-quick-reference|.
43 |
44 |
45 | *g:VM_add_cursor_at_pos_no_mappings* Default: 0
46 |
47 | When starting VM by adding a single cursor at position, don't enable buffer
48 | mappings, so that you can keep moving freely the cursor to add more cursors
49 | elsewhere.
50 |
51 | *g:VM_show_warnings* Default: 1
52 |
53 | When entering VM and there are mapping conflicts, a warning is displayed. Set
54 | to 0 to disable this warning. You can still run |:VMDebug| to see if there
55 | are conflicts.
56 |
57 |
58 | *g:VM_verbose_commands* Default: 0
59 |
60 | Set to 1 if you want command prompts to be more informative, rather than as
61 | minimal as possible.
62 |
63 |
64 | *g:VM_skip_shorter_lines* Default: 1
65 |
66 | When adding cursors up/down, skip shorter lines.
67 |
68 |
69 | *g:VM_skip_empty_lines* Default: 0
70 |
71 | When adding cursors up/down, skip empty lines.
72 |
73 |
74 | *g:VM_live_editing* Default: 1
75 |
76 | Controls how often text is updated in insert mode.
77 |
78 |
79 | *g:VM_reselect_first* Default: 0
80 |
81 | The first region will be reselected after most commands, if set to 1.
82 |
83 |
84 | *g:VM_case_setting* Default: ''
85 |
86 | Possible values: 'smart', 'sensitive', 'ignore'.
87 | Starting case matching for patterns. Can be switched inside VM.
88 | Leave empty to use your current setting.
89 |
90 |
91 | *g:VM_disable_syntax_in_imode* Default: 0
92 |
93 | You could want to do it for performance reasons.
94 |
95 |
96 | *VM_recursive_operations_at_cursors* Default: 1
97 |
98 | When executing normal commands in cursor mode (`dw` and similar), by default
99 | recursive mappings are used, so that user text object can be used as well.
100 | Set to 0 if you always want commands in cursor mode to be non-recursive.
101 |
102 |
103 | *g:VM_custom_remaps* Default: {}
104 |
105 | To remap VM mappings to other VM mappings.
106 | Example:
107 | >
108 | " also use to select previous and to skip current
109 | let g:VM_custom_remaps = {'': 'N', '': 'q'}
110 | <
111 |
112 | *g:VM_custom_noremaps* Default: {}
113 |
114 | To remap any key to normal! commands. Example:
115 | >
116 | let g:VM_custom_noremaps = {'==': '==', '<<': '<<', '>>': '>>'}
117 | <
118 | would create commands that will work on all cursors inside VM, for each
119 | key-value pair. It's a way to remap built-in vim commands, without having
120 | too many default mappings that can cause conflicts with other plugins. They
121 | only work in (and enforce) cursor mode.
122 |
123 |
124 | *g:VM_custom_motions* Default: {}
125 |
126 | To remap any standard motion (h,j,k,l,f...) commands. For example this
127 | inverts 'h' and 'l' motions:
128 | >
129 | let g:VM_custom_motions = {'h': 'l', 'l': 'h'}
130 | <
131 | It can be useful if you use keyboard layouts other than QWERTY. Valid motions
132 | that you can remap are:
133 | >
134 | h j k l w W b B e E ge gE , ; $ 0 ^ % \| f F t T
135 | <
136 |
137 | *g:VM_user_operators* Default: []
138 |
139 | Cursor mode only. The elements of the list can be simple strings (then
140 | any text object can be accepted) or a dictionary with the operator as key,
141 | and the number of characters to be typed as value. Example:
142 | >
143 | let VM_user_operators = ['yd', 'cx', {'cs': 2}]
144 | <
145 | This will accept commands like `ydE`, `cxiw`, or `cs` followed by two
146 | characters, for example `cs{[`. These operators can be either vim or
147 | plugin operators, mappings are passed recursively.
148 |
149 | Note: |vim-surround| and |vim-abolish| have built-in support, this isn't
150 | needed for them to work.
151 |
152 |
153 | *g:VM_use_first_cursor_in_line* Default: 0
154 |
155 | In insert mode, the active cursor is normally the last selected one. Set this
156 | option to `1` to always use the first cursor in the line.
157 |
158 |
159 | *g:VM_insert_special_keys* Default: ['c-v']
160 |
161 | Some keys in insert mode can have a different behaviour, compared to vim
162 | defaults. Possible values:
163 |
164 | `c-a` go to the beginning of the line, at indent level
165 | `c-e` go to the end of the line
166 | `c-v` paste from VM unnamed register
167 |
168 |
169 | *g:VM_single_mode_maps* Default: 1
170 |
171 | Set to 0 to disable entirely insert mode mappings to cycle cursors in
172 | |vm-single-mode|. If you only want to change the default mappings, see
173 | |vm-mappings-buffer|.
174 |
175 |
176 | *g:VM_single_mode_auto_reset* Default: 1
177 |
178 | If insert mode is entered while |vm-single-mode| is enabled, it will be reset
179 | automatically when exiting insert mode, unless this value is 0.
180 |
181 |
182 | *g:VM_filesize_limit* Default: 0 (disabled)
183 |
184 | VM won't start if buffer size is greater than this.
185 |
186 |
187 | *g:VM_persistent_registers* Default: 0
188 |
189 | If true VM registers will be stored in the |viminfo|. The 'viminfo' option
190 | must include !, for this to work. Also see |:VMRegisters|.
191 |
192 |
193 | *g:VM_reindent_filetypes* Default: []
194 |
195 | Autoindentation (via |indentkeys|) is temporarily disabled in insert mode,
196 | and you have to reindent edited lines yoursef. For filetypes included in this
197 | list, edited lines are automatically reindented when exiting insert mode.
198 |
199 |
200 | *g:VM_plugins_compatibilty* Default: {}
201 |
202 | Used for plugins compatibility, see |vm-compatibility|.
203 |
204 |
205 |
206 |
207 | HIGHLIGHT *vm-highlight*
208 | ===============================================================================
209 |
210 |
211 | VM default theme is based on your color scheme, if you don't like it you can:
212 |
213 | * select a theme
214 | * relink highlight groups
215 | *g:VM_theme*
216 | You can load a theme by default by defining: >
217 | let g:VM_theme = 'your_chosen_theme'
218 | < *:VMTheme*
219 | If you want to change theme, run: >
220 | :VMTheme
221 | <
222 | These are the default VM highlight groups: >
223 |
224 | hi! link VM_Mono DiffText
225 | hi! link VM_Extend DiffAdd
226 | hi! link VM_Cursor Visual
227 | hi! link VM_Insert DiffChange
228 | <
229 | *VM_Mono* is the highlight in cursor mode
230 | *VM_Extend* ,, in extend mode (the selections)
231 | *VM_Cursor* ,, in extend mode (the cursors)
232 | *VM_Insert* ,, in insert mode (the virtual cursors)
233 |
234 | *vm-colorschemes*
235 |
236 | If you want to load a theme inside a colorscheme, just put in the colorscheme
237 | script:
238 | >
239 | silent! VMTheme theme_name
240 | <
241 | You can also link (or redefine) the highlight groups directly.
242 |
243 |
244 |
245 | vim: ft=help et sw=2 ts=2 sts=2 tw=79
246 |
--------------------------------------------------------------------------------
/doc/vm-troubleshooting.txt:
--------------------------------------------------------------------------------
1 | *vm-troubleshooting.txt* Version 0.3.0 Last change: June 13 2019
2 |
3 | Known issues |vm-bugs|
4 | Plugins compatibility |vm-compatibility|
5 |
6 |
7 |
8 | TROUBLESHOOTING *vm-troubleshooting*
9 | ===============================================================================
10 |
11 | First stop for troubleshooting is |vm-faq|.
12 |
13 |
14 |
15 |
16 |
17 | KNOWN ISSUES *vm-bugs*
18 | ==============================================================================
19 |
20 | VM relies mostly on autocommands, especially in insert mode. Plugins and
21 | autocommands defined by the user could cause issues, if they do something
22 | intrusive while VM is updating text. Snippets plugins won't work for this
23 | reason, but |abbreviations| work. For example this won't work either:
24 | >
25 | au InsertLeave * silent! update
26 | < *b:visual_multi*
27 | In this case you may want to check that VM is not active, before performing an
28 | action, you could write it as:
29 | >
30 | au InsertLeave * if !exists('b:visual_multi') | silent! update | endif
31 | <
32 | {https://github.com/mg979/vim-visual-multi/issues/91}
33 |
34 | ------------------------------------------------------------------------------
35 | *vm-autocompletion*
36 |
37 | Some autocompletion plugins can cause trouble if the active cursor is not the
38 | first cursor in the line. In this case this can help:
39 | >
40 | let g:VM_use_first_cursor_in_line = 1
41 |
42 |
43 | ------------------------------------------------------------------------------
44 |
45 | In |vm-single-region| mode, some insert mode mappings (, , ) are
46 | disabled because they don't behave well. and are not disabled, but
47 | they cause issues when deleting other cursors, so be careful. They are not
48 | disabled because it shouldn't happen in normal use cases for this feature, that
49 | should be used when you want to use cursors as 'fields' to edit sequentially.
50 |
51 |
52 |
53 |
54 |
55 | PLUGINS COMPATIBILITY *vm-compatibility*
56 | ==============================================================================
57 |
58 | As mentioned above, snippets plugins cannot work with VM. Other plugins can
59 | have incompatibilities, especially if they apply buffer mappings. In this
60 | case, VM will not replace the conflicting keys, but this can result in missing
61 | VM functionalities or even render VM unusable. If you notice some mappings
62 | don't work in VM, run |:VMDebug| to see if a plugin is the cause.
63 |
64 | However, VM will always overwrite insert mode mappings. If this happens, you
65 | can also read it in the |:VMDebug| output. In this case, it's not VM that
66 | breaks, it's the conflicting plugin.
67 |
68 | Some plugins have built-in compatibility:
69 |
70 | - jiangmiao/auto-pairs
71 | - AndrewRadev/tagalong.vim
72 | - dyng/ctrlsf.vim
73 | - Shougo/deoplete.nvim
74 | - ncmw/ncm2
75 |
76 | If you want to run some code before/after VM starts/exits, see
77 | |vm-faq-functions|.
78 |
79 | If you make a plugin and want to test if VM is active, perform a check on the
80 | |b:visual_multi| variable.
81 |
82 | If a plugin needs to be deactivated inside VM, you could also use the
83 | |g:VM_plugins_compatibilty| dictionary, with this structure:
84 | >
85 | let g:VM_plugins_compatibilty = {
86 | \'plugin_name': {
87 | \ 'test': { -> exists('plugin_is_enabled') },
88 | \ 'enable': ':PluginEnableCommand',
89 | \ 'disable': ':PluginDisableCommand'},
90 | \}
91 | <
92 | If you would like a plugin to have built-in compatibility, open an issue about
93 | it on the VM GitHub repo and/or make a pull request.
94 |
95 |
96 |
97 |
98 | vim: ft=help et sw=2 ts=2 sts=2 tw=78
99 |
--------------------------------------------------------------------------------
/doc/vm-tutorial:
--------------------------------------------------------------------------------
1 | ===================================================================================
2 | _ _ __ ____ _~
3 | _ __(_)___ ___ _ __(_)______ ______ _/ / ____ ___ __ __ / / /_(_)~
4 | | | / / / __ `__ \_____| | / / / ___/ / / / __ `/ /_____/ __ `__ \/ / / // / __/ /~
5 | | |/ / / / / / / /_____/ |/ / (__ ) /_/ / /_/ / /_____/ / / / / / /_/ // / /_/ / ~
6 | |___/_/_/ /_/ /_/ |___/_/____/\__,_/\__,_/_/ /_/ /_/ /_/\__,_//_/\__/_/ ~
7 |
8 | ===================================================================================
9 | _____ _ _ _~
10 | |_ _| _| |_ ___ _ __(_) __ _| |~
11 | | || | | | __/ _ \| '__| |/ _` | |~
12 | | || |_| | || (_) | | | | (_| | |~
13 | |_| \__,_|\__\___/|_| |_|\__,_|_|~
14 |
15 | ===================================================================================
16 |
17 | Hi and thanks for trying this plugin. Let's start!
18 |
19 | First and foremost, this mini vimrc will use the following non-default settings, so
20 | if you try the plugin with your vimrc, and something doesn't work, this could be the
21 | reason. These settings enable a theme, the mouse mappings, and the Undo/Redo
22 | functionality.
23 | >
24 | let g:VM_mouse_mappings = 1
25 | let g:VM_theme = 'iceblue'
26 |
27 | let g:VM_maps = {}
28 | let g:VM_maps["Undo"] = 'u'
29 | let g:VM_maps["Redo"] = ''
30 | <
31 | Also note that I'm using the default g:VM_leader, that is '\\' (two backslashes).
32 | Where you see two backslashes being used, that should be replaced if you're setting
33 | a different g:VM_leader in your vimrc.
34 |
35 |
36 | Create cursors ~
37 | ------------------------------------------------------------------------------------
38 |
39 | I'll reuse the block above for the first test.
40 |
41 | let g:VM_mouse_mappings = 1
42 | let g:VM_theme = 'iceblue'
43 | let g:VM_highlight_matches = 'underline'
44 |
45 | let g:VM_maps = {}
46 | let g:VM_maps["Undo"] = 'u'
47 | let g:VM_maps["Redo"] = ''
48 |
49 | Move the cursor to the first 'let' word, on the first letter.
50 | Press `mm`, to set a mark, since you'll go back there more than once.
51 | Then press |z| to scroll the window.
52 |
53 | Now press 5 times : you see that you've created cursors vertically, skipping
54 | the empty line. Press to exit VM, then `m to go back to the mark.
55 |
56 | Press 5: see what happened? [count] works just the same.
57 |
58 | You are now in `cursor mode`: you have a bunch of cursors and the commands you issue
59 | will be run as if you were in normal mode.
60 |
61 | What can you do with these cursors?
62 |
63 | - you can move them: press wwbb, then WWBB to get an idea.
64 | - you can issue VM specific commands that you'll learn later.
65 | - you can issue normal mode commands: press `dw`, then `cW`, and start editing.
66 | - you can also enter insert mode with i, a, I, A
67 |
68 |
69 |
70 | Extend selections ~
71 | ------------------------------------------------------------------------------------
72 |
73 | Quit VM, undo changes if you made any, press `m, and 5.
74 |
75 | Now press : what happened? You switched to `extend mode`: you can think of it
76 | as the 'visual' mode in VM. You are not working with cursors anymore, you are
77 | working with selections. As in visual mode, you can extend these selections with
78 | motions, and pressing 'c' will delete their content, and start editing.
79 |
80 | Note: vim registers will be restored after exiting VM. Inside VM, `change/x` commands
81 | are redirected to the black hole register (`_`), differently from vim.
82 |
83 |
84 |
85 | Select words ~
86 | ------------------------------------------------------------------------------------
87 |
88 | Quit VM, undo changes, move below and set a new mark, then |z|.
89 |
90 | let g:VM_moLET_mappings = 1
91 | Let g:VM_theme = 'iceblue'
92 | lEt g:VM_highlight_matches = 'underline'
93 |
94 | Let g:VM_maps = {}
95 | let g:VM_maps["Undo"] = 'u'
96 | let g:VM_maps["Redo"] = ''
97 |
98 | Now press : this is the default mapping to select words under the cursor. If
99 | your search settings are set to 'smartcase', you'll see that all following 'let'
100 | words are underlined, and will be selected if you keep pressing .
101 |
102 | Quit VM and move to the second line.
103 |
104 | Press : if smartcase is active, only one other match will be underlined.
105 | Press `\\c`: this allows you to cycle the case setting of the current pattern. Press
106 | it until it becomes case insensitive.
107 | Press `q`: the current match will be skipped. Alternate