├── LICENSE ├── README.md ├── autoload └── multiedit.vim ├── doc └── multiedit.txt └── plugin └── multiedit.vim /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013-16 Henrik Lissner. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vim-multiedit - Multi-selection and editing in vim 2 | 3 | ## About 4 | 5 | Do you envy Sublime Text 2's multiple selection and editing feature? This plugin 6 | tries to fill that multi-caret shaped gap in your heart by letting you 7 | specify "regions" of text and edit them all from one place. 8 | 9 | *(This plugin is based on https://github.com/felixr/vim-multiedit by Felix 10 | Riedel )* 11 | 12 | ## Usage 13 | 14 | ```vim 15 | " Insert a disposable marker after the cursor 16 | nmap ma :MultieditAddMark a 17 | 18 | " Insert a disposable marker before the cursor 19 | nmap mi :MultieditAddMark i 20 | 21 | " Make a new line and insert a marker 22 | nmap mo o:MultieditAddMark i 23 | nmap mO O:MultieditAddMark i 24 | 25 | " Insert a marker at the end/start of a line 26 | nmap mA $:MultieditAddMark a 27 | nmap mI ^:MultieditAddMark i 28 | 29 | " Make the current selection/word an edit region 30 | vmap m :MultieditAddRegion 31 | nmap mm viw:MultieditAddRegion 32 | 33 | " Restore the regions from a previous edit session 34 | nmap mu :MultieditRestore 35 | 36 | " Move cursor between regions n times 37 | map ]m :MultieditHop 1 38 | map [m :MultieditHop -1 39 | 40 | " Start editing! 41 | nmap M :Multiedit 42 | 43 | " Clear the word and start editing 44 | nmap C :Multiedit! 45 | 46 | " Unset the region under the cursor 47 | nmap md :MultieditClear 48 | 49 | " Unset all regions 50 | nmap mr :MultieditReset 51 | ``` 52 | -------------------------------------------------------------------------------- /autoload/multiedit.vim: -------------------------------------------------------------------------------- 1 | " *multiedit.txt* Multi-editing for Vim 2 | 3 | " addRegion() {{ 4 | func! multiedit#addRegion() 5 | if mode() != 'v' 6 | normal! gv 7 | endif 8 | 9 | " Get region parameters 10 | let line = line('.') 11 | let startcol = col('v') 12 | let endcol = col('.')+1 13 | 14 | " add selection to list 15 | let sel = { 16 | \ 'line': line, 17 | \ 'col': startcol, 18 | \ 'len': endcol-startcol, 19 | \ 'suffix_length': col('$')-endcol 20 | \ } 21 | if !exists("b:regions") 22 | let b:regions = {} 23 | let b:first_region = sel 24 | endif 25 | 26 | if has_key(b:regions, line) 27 | " Check if this overlaps with any other region 28 | let overlapid = multiedit#hasOverlap(sel) 29 | if overlapid == -1 30 | let b:regions[line] = b:regions[line] + [sel] 31 | else 32 | " If so, change this to the 'main' region 33 | let b:first_region = b:regions[line][overlapid] 34 | let new_first = 1 35 | endif 36 | else 37 | let b:regions[line] = [sel] 38 | endif 39 | 40 | " Highlight the region 41 | if exists("new_first") 42 | call multiedit#rehighlight() 43 | else 44 | call multiedit#highlight(line, startcol, endcol) 45 | endif 46 | 47 | " Exit visual mode 48 | normal! v 49 | endfunc 50 | " }} 51 | 52 | " addMark() {{ 53 | func! multiedit#addMark(mode) 54 | let mark = g:multiedit_mark_character 55 | 56 | " Insert the marker character and select it 57 | let line = getline('.') 58 | let col = col('.') 59 | 60 | " Insert the markers, pre or post, depending on the mode 61 | let precol = a:mode ==# "i" ? 2 : 1 62 | let sufcol = a:mode ==# "i" ? 1 : 0 63 | 64 | let prefix = col > 1 ? line[0:col-precol] : '' 65 | let suffix = line[(col-sufcol):] 66 | call setline(line('.'), prefix.mark.suffix) 67 | if a:mode ==# "a" 68 | normal! l 69 | endif 70 | normal! v 71 | 72 | let line = line('.') 73 | if exists("b:regions") && has_key(b:regions, line) 74 | " Check for regions on the same line that follow this region and shift 75 | " them to the right. 76 | let col = col('.') 77 | for region in b:regions[line] 78 | let offset = strlen(mark) 79 | if region.col > col 80 | let region.col += offset 81 | else 82 | let region.suffix_length += offset 83 | endif 84 | endfor 85 | 86 | call multiedit#rehighlight() 87 | endif 88 | 89 | " ...then make it a region 90 | call multiedit#addRegion() 91 | endfunc 92 | " }} 93 | 94 | " start() {{ 95 | func! multiedit#start(bang, ...) 96 | if !exists("b:regions") 97 | if g:multiedit_auto_restore == 0 || !multiedit#again() 98 | return 99 | endif 100 | endif 101 | 102 | call multiedit#triggerStartEvents() 103 | 104 | let lastcol = b:first_region.col + b:first_region.len 105 | 106 | " If bang exists OR the first region is a marker, then clear it before 107 | " editing mode begins. 108 | if a:bang ==# '!' 109 | " Remove the word and update the highlights 110 | let linetext = getline(b:first_region.line) 111 | if b:first_region.col == 1 112 | let newline = g:multiedit_mark_character . linetext[(lastcol - 1):] 113 | else 114 | let newline = linetext[0:(b:first_region.col - 2)] . g:multiedit_mark_character . linetext[(lastcol - 1):] 115 | endif 116 | 117 | call setline(b:first_region.line, newline) 118 | call multiedit#update(0) 119 | 120 | " Refresh the lastcol (update() likely changed things!) 121 | let lastcol = b:first_region.col + b:first_region.len 122 | endif 123 | 124 | " Move cursor to the end of the word 125 | call cursor(b:first_region.line, lastcol) 126 | 127 | " Set up some 'abort' mappings, because they can't be accounted for. They 128 | " will unmap themselves once they're pressed. 129 | call multiedit#maps(0) 130 | 131 | " Start insert mode. Since there's no way to mimic 'a' with :normal, we 132 | " have to do it manually: 133 | if col('$') == lastcol 134 | startinsert! 135 | else 136 | startinsert 137 | endif 138 | 139 | " Where the magic happens 140 | augroup multiedit 141 | au! 142 | " Update the highlights as you edit 143 | au CursorMovedI * call multiedit#update(0) 144 | 145 | " Once you leave INSERT, apply changes and delete this augroup 146 | au InsertLeave * call multiedit#update(1) | call multiedit#maps(0) | call multiedit#triggerEndEvents() | au! multiedit 147 | 148 | if g:multiedit_auto_reset == 1 149 | " Clear all regions once you exit insert mode 150 | au InsertLeave * call multiedit#reset() 151 | endif 152 | augroup END 153 | endfunc 154 | " }} 155 | 156 | " reset() {{ 157 | func! multiedit#reset(...) 158 | if exists("b:regions_last") 159 | unlet b:regions_last 160 | endif 161 | if exists("b:regions") 162 | if a:0 == 0 163 | let b:regions_last = {} 164 | let b:regions_last["regions"] = b:regions 165 | let b:regions_last["first"] = b:first_region 166 | endif 167 | 168 | unlet b:first_region 169 | unlet b:regions 170 | endif 171 | 172 | syn clear MultieditRegions 173 | syn clear MultieditFirstRegion 174 | 175 | call multiedit#maps(1) 176 | silent! au! multiedit 177 | endfunc 178 | " }} 179 | 180 | " clear() {{ 181 | func! multiedit#clear(...) 182 | if !exists("b:regions") 183 | return 184 | endif 185 | 186 | " The region to delete might have been provided as an argument. 187 | if a:0 == 1 && type(a:1) == 4 188 | let sel = a:1 189 | else 190 | let sel = {"col": col('v'), "len": 1, "line":line('.')} 191 | endif 192 | 193 | if !has_key(b:regions, sel.line) 194 | return 195 | endif 196 | 197 | " Iterate through all regions on this line 198 | let i = 0 199 | for region in b:regions[sel.line] 200 | " Does this cursor overlap with this region? If so, delete it. 201 | if multiedit#isOverlapping(sel, region) 202 | 203 | " If you're deleting a main region, we need to pass on the role to 204 | " another region first! 205 | if region == b:first_region 206 | unlet b:first_region 207 | 208 | " Get the last specified region 209 | let keys = keys(b:regions) 210 | if len(keys) 211 | let b:first_region = b:regions[keys(b:regions)[-1]][-1] 212 | endif 213 | endif 214 | 215 | unlet b:regions[sel.line][i] 216 | endif 217 | let i += 1 218 | endfor 219 | 220 | " The regions have changed. Update the highlights. 221 | call multiedit#rehighlight() 222 | endfunc 223 | " }} 224 | 225 | " If Multiple_cursors_after function has been defined in ~/.vimrc, call it 226 | " triggerEndEvents() {{ 227 | func! multiedit#triggerEndEvents() 228 | if exists('*Multiple_cursors_after') 229 | exe "call Multiple_cursors_after()" 230 | endif 231 | endfunc 232 | "}} 233 | 234 | " If Multiple_cursors_after function has been defined in ~/.vimrc, call it 235 | " triggerStartEvents() {{ 236 | func! multiedit#triggerStartEvents() 237 | if exists('*Multiple_cursors_before') 238 | exe "call Multiple_cursors_before()" 239 | endif 240 | endfunc 241 | " }} 242 | 243 | " update() {{ 244 | " Update highlights when changes are made 245 | func! multiedit#update(change_mode) 246 | if !exists('b:regions') 247 | return 248 | endif 249 | 250 | " Column offset from start of main edit region to cursor (relevant when 251 | " restoring cursor location post-edit) 252 | let col = col('.') 253 | let cursor_col = col-b:first_region.col 254 | 255 | " Clear highlights so we can make changes 256 | syn clear MultieditRegions 257 | syn clear MultieditFirstRegion 258 | 259 | " Prepare the new, altered line 260 | let linetext = getline(b:first_region.line) 261 | let lineendlen = (len(linetext) - b:first_region.suffix_length) 262 | if lineendlen == 0 263 | let newtext = "" 264 | else 265 | let newtext = linetext[(b:first_region.col-1): (lineendlen-1)] 266 | endif 267 | 268 | " Iterate through the lines where regions exist. And sort them by 269 | " sequence. 270 | for line in sort(keys(b:regions)) 271 | let regions = copy(b:regions[line]) 272 | let regions = sort(regions, "multiedit#entrySort") 273 | let multiedit#offset = 0 274 | 275 | " Iterate through each region on this line 276 | for region in regions 277 | if a:change_mode 278 | 279 | let region.col += multiedit#offset 280 | if region.line != b:first_region.line || region.col != b:first_region.col 281 | " Get the old line 282 | let oldline = getline(region.line) 283 | 284 | " ...and assemble a new one 285 | let prefix = '' 286 | if region.col > 1 287 | let prefix = oldline[0:region.col-2] 288 | endif 289 | let suffix = oldline[(region.col+region.len-1):] 290 | 291 | " Update the line 292 | call setline(region.line, prefix.newtext.suffix) 293 | endif 294 | 295 | if col >= b:first_region.col 296 | let multiedit#offset = multiedit#offset + len(newtext) - region.len 297 | endif 298 | let region.len = len(newtext) 299 | 300 | else 301 | 302 | if region.line == b:first_region.line 303 | 304 | " ...move the highlight offset of regions after it 305 | if region.col >= b:first_region.col 306 | let region.col += multiedit#offset 307 | let multiedit#offset = multiedit#offset + len(newtext) - b:first_region.len 308 | endif 309 | 310 | " ...and update the length of the first_region. 311 | " Remember, we're only affecting the main region and 312 | " regions following it, on the same line 313 | if region.col == b:first_region.col 314 | if newtext ==# "" 315 | if col < b:first_region.col 316 | call multiedit#reset(1) 317 | return 318 | endif 319 | 320 | " If newtext is blank, just make the len 0 (for 321 | " now) otherwise it'll go crazy! 322 | let region.len = 0 323 | else 324 | let region.len = len(newtext) 325 | endif 326 | endif 327 | 328 | endif 329 | 330 | " Rehighlight the lines 331 | call multiedit#highlight(region.line, region.col, region.col+region.len) 332 | 333 | endif 334 | endfor 335 | endfor 336 | 337 | " Remeasure the strlen 338 | let b:first_region.suffix_length = col([b:first_region.line, '$']) - b:first_region.col - b:first_region.len 339 | 340 | " Restore cursor location 341 | call cursor(b:first_region.line, b:first_region.col + cursor_col) 342 | 343 | endfunc 344 | " }} 345 | 346 | " again() {{ 347 | " Restore last region selections. Returns 1 on success, 0 on failure. 348 | func! multiedit#again() 349 | if !exists("b:regions_last") 350 | echom "No regions to restore!" 351 | return 352 | endif 353 | 354 | let b:first_region = b:regions_last["first"] 355 | let b:regions = b:regions_last["regions"] 356 | 357 | call multiedit#rehighlight() 358 | return 1 359 | endfunc 360 | " }} 361 | 362 | " jump() {{ 363 | " Hop [-/+]N regions 364 | func! multiedit#jump(n) 365 | let n = str2nr(a:n) 366 | if !exists("b:regions") 367 | echom "There are no regions to jump to." 368 | return 369 | elseif n == 0 370 | " n == 0 goes nowhere! 371 | return 372 | endif 373 | 374 | " This is the starting point of the search. 375 | let col = col('.') 376 | let line = line('.') 377 | 378 | " Sort the lines so that we can sequentially access them. If the jump is 379 | " going backwards, reverse the resulting keys. 380 | let region_keys = sort(keys(b:regions)) 381 | if n < 0 382 | call reverse(region_keys) 383 | endif 384 | 385 | let i = 0 386 | for l in region_keys 387 | " Skip over irrelevant lines (before/after the start point, depending 388 | " on the jump direction) 389 | if (n>0 && lline) 390 | continue 391 | endif 392 | 393 | let regions = sort(copy(b:regions[l]), 'multiedit#entrySort') 394 | if n < 0 395 | call reverse(regions) 396 | endif 397 | 398 | for region in regions 399 | " If this is the line we're on, skip irrelevant regions 400 | " (before/after the start point, depending on jump direction) 401 | if l == line 402 | if ((n>0) && (region.col<=col)) || ((n<0) && (region.col>=col)) 403 | continue 404 | endif 405 | endif 406 | 407 | " Skip over n-1 matches, then move the cursor on the nth match 408 | let i += a:n > 0 ? 1 : -1 409 | if n == i 410 | call cursor(l, region.col) 411 | return 1 412 | endif 413 | endfor 414 | endfor 415 | 416 | echom "No more regions!" 417 | return 418 | endfunc 419 | " }} 420 | 421 | """""""""""""""""""""""""" 422 | " isOverlapping(selA, selB) {{ 423 | " Checks to see if selA overlaps with selB 424 | func! multiedit#isOverlapping(selA, selB) 425 | " Check for invalid input 426 | if type(a:selA) != 4 || type(a:selB) != 4 427 | return 428 | endif 429 | 430 | " If they're not on the same line, don't even try. 431 | if a:selA.line != a:selB.line 432 | return 433 | endif 434 | 435 | " Check for overlapping 436 | let selAend = a:selA.col + (a:selA.len - 1) 437 | let selBend = a:selB.col + (a:selB.len - 1) 438 | return a:selA.col == a:selB.col || selAend == selBend 439 | \ || a:selA.col == selBend || selAend == a:selB.col 440 | \ || (a:selA.col > a:selB.col && a:selA.col < selBend) 441 | \ || (selAend < selBend && selAend > a:selB.col) 442 | endfunc 443 | " }} 444 | 445 | " hasOverlap(sel) {{ 446 | " Checks to see if any other regions overlap with this one. Returns -1 if not, 447 | " and the id of it otherwise (e.g. b:regions[line][id]) 448 | func! multiedit#hasOverlap(sel) 449 | if type(a:sel) != 4 || !has_key(b:regions, a:sel.line) 450 | return -1 451 | endif 452 | 453 | for i in range(len(b:regions[a:sel.line])) 454 | if multiedit#isOverlapping(a:sel, b:regions[a:sel.line][i]) 455 | return i 456 | endif 457 | endfor 458 | return -1 459 | endfunc 460 | " }} 461 | 462 | " highlight(line, start, end) {{ 463 | func! multiedit#highlight(line, start, end) 464 | if a:start > a:end || a:end < a:start 465 | return 466 | endif 467 | if !exists("b:first_region") || (b:first_region.line == a:line && b:first_region.col == a:start) 468 | let synid = "MultieditFirstRegion" 469 | else 470 | let synid = "MultieditRegions" 471 | endif 472 | execute "syn match ".synid." '\\%".a:line."l\\%".a:start."c\\_.*\\%".a:line."l\\%".a:end."c' containedin=ALL" 473 | endfunc 474 | " }} 475 | 476 | " rehighlight() {{ 477 | func! multiedit#rehighlight() 478 | syn clear MultieditRegions 479 | syn clear MultieditFirstRegion 480 | 481 | " Go through regions and rehighlight them 482 | for line in keys(b:regions) 483 | for sel in b:regions[line] 484 | call multiedit#highlight(line, sel.col, sel.col + sel.len) 485 | endfor 486 | endfor 487 | endfunc 488 | " }} 489 | 490 | " map() {{ 491 | func! multiedit#maps(unmap) 492 | if a:unmap 493 | silent! iunmap 494 | silent! iunmap 495 | silent! iunmap 496 | else 497 | inoremap :call multiedit#maps(1) 498 | inoremap :call multiedit#maps(1) 499 | inoremap :call multiedit#maps(1) 500 | endif 501 | endfunc 502 | " }} 503 | 504 | " entrySort() {{ 505 | func! multiedit#entrySort(a, b) 506 | return a:a.col == a:b.col ? 0 : a:a.col > a:b.col ? 1 : -1 507 | endfunc 508 | " }} 509 | 510 | " vim: set foldmarker={{,}} foldlevel=0 foldmethod=marker 511 | -------------------------------------------------------------------------------- /doc/multiedit.txt: -------------------------------------------------------------------------------- 1 | *multiedit.txt* Multi-editing for Vim *multiedit* 2 | 3 | Author: Henrik Lissner 4 | License: MIT license 5 | 6 | CONTENTS *multiedit-contents* 7 | 8 | Introduction |multiedit-introduction| 9 | Mappings |multiedit-mappings| 10 | Settings |multiedit-settings| 11 | Known Issues |multiedit-issues| 12 | Changelog |multiedit-changelog| 13 | 14 | ============================================================================== 15 | INTRODUCTION *multiedit-introduction* 16 | 17 | Do you envy Sublime Text 2's multiple selection and editing feature? This plugin 18 | tries to fill that multi-caret shaped gap in your heart by letting you 19 | specify "regions" of text and edit them all from one place. 20 | 21 | NOTE: This plugin was inspired by https://github.com/felixr/vim-multiedit, by 22 | Felix Riedel . 23 | 24 | ============================================================================== 25 | MAPPINGS *multiedit-mappings* 26 | > 27 | " Insert a disposable marker after the cursor 28 | nmap ma :MultieditAddMark a 29 | 30 | " Insert a disposable marker before the cursor 31 | nmap mi :MultieditAddMark i 32 | 33 | " Make a new line and insert a marker 34 | nmap mo o:MultieditAddMark i 35 | nmap mO O:MultieditAddMark i 36 | 37 | " Insert a marker at the end/start of a line 38 | nmap mA $:MultieditAddMark a 39 | nmap mI ^:MultieditAddMark i 40 | 41 | " Make the current selection/word an edit region 42 | vmap m :MultieditAddRegion 43 | nmap mm viw:MultieditAddRegion 44 | 45 | " Restore the regions from a previous edit session 46 | nmap mu :MultieditRestore 47 | 48 | " Move cursor between regions n times 49 | map ]m :MultieditHop 1 50 | map [m :MultieditHop -1 51 | 52 | " Start editing! 53 | nmap M :Multiedit 54 | 55 | " Clear the word and start editing 56 | nmap C :Multiedit! 57 | 58 | " Unset the region under the cursor 59 | nmap md :MultieditClear 60 | 61 | " Unset all regions 62 | nmap mr :MultieditReset 63 | < 64 | Here are some keybinds that aren't included in the plugin but may be 65 | useful/interesting to some people: 66 | > 67 | " Make the word under the cursor a region and jump to the next/previous 68 | " occurance of that word 69 | nmap mn mm/=expand("") 70 | nmap mp mm?=expand("") 71 | < 72 | 73 | ============================================================================== 74 | SETTINGS *multiedit-settings* 75 | > 76 | " Disable all mappings? 77 | let g:multiedit_no_mappings = 0 78 | 79 | " Reset all regions on InsertLeave when finished? 80 | let g:multiedit_auto_reset = 1 81 | 82 | " Disposable marker character (beware characters with strlen > 1 - like 83 | " special symbols). 84 | let g:multiedit_mark_character = '|' 85 | 86 | " If no selections are present and you initiate edit mode, should it 87 | " restore the previous regions? 88 | let g:multiedit_auto_restore = 1 89 | < 90 | 91 | The highlight regions can be customized, these are the defaults: 92 | > 93 | hi default link MultieditRegions Search 94 | hi default link MultieditFirstRegion IncSearch 95 | < 96 | If other plugins cause odd behaviour during multiple region editing, two 97 | function scan be used to disable/re-enable this behaviour at apprpriate 98 | times. An example showing how to disable NeoComplete during multiple 99 | region editing is below: 100 | 101 | function! Multiple_cursors_before() 102 | exe 'NeoCompleteLock' 103 | echo 'Disabled autocomplete' 104 | endfunction 105 | 106 | function! Multiple_cursors_after() 107 | exe 'NeoCompleteUnlock' 108 | echo 'Enabled autocomplete' 109 | endfunction 110 | 111 | ============================================================================== 112 | KNOWN ISSUES *multiedit-issues* 113 | 114 | A few problems exist in this plugin, some that may be addressed in the future 115 | and some that won't. 116 | 117 | 1. Multiediting aren't truly like multiple cursors, they can't account for 118 | newlines or deleting past the starting point of the main region. (This 119 | won't be addressed. Macros can easily make up for this. Heck, with mastery 120 | of macros, this entire plugin could be replaced!) 121 | 2. g:multiedit_mark_character cannot be a unicode/special character with 122 | a strlen > 1. This may be addressed in a future updated, but is low 123 | priority. 124 | 3. It won't make you coffee. This is unacceptable! A THOUSAND YEARS DUNGEON! 125 | 126 | If you encounter any other issues, please report them at 127 | https://github.com/hlissner/vim-multiedit/issues 128 | 129 | Thank you! 130 | 131 | ============================================================================== 132 | CHANGELOG *multiedit-changelog* 133 | 134 | 2014-11-13 [2.0.3] 135 | - New: added conditional calls to Multiple_cursors_after/Multiple_cursors_before 136 | functions defined in your ~/.vimrc. These functions allow disabling of 137 | NeoComplete while editing multiple regions, for example. Implemenation copied 138 | from terryma/vim-multiple-cursors issue #51 139 | 2013-03-28 [2.0.2] 140 | - Fix: when the region goes crazy when the text is at the start of the line (when 141 | len == 0 it would suddenly expand to the length of the line). 142 | - Fix: when the cursor deletes past its boundaries it will leave multiedit 143 | mode and reset your regions. 144 | 145 | 2013-03-26 [2.0.1] 146 | - New: four new keybinds for adding markers to the start/end of a line or on 147 | the next/previous line 148 | - Changed: auto-removing disposable markers where entering insert mode (had 149 | a strange effect when regions were at the start of a line) 150 | 151 | 2013-03-25 [2.0.0] 152 | - New: jump between regions with ]m and [m 153 | - Changed: disposable marker will be automatically deleted when entering 154 | insert mode 155 | - Changed: :MultieditAppendMark and :MultieditPrependMark are merged into 156 | :MultieditAddMark [ia] 157 | - Changed: the default highlight color for MultieditRegion is now linked to 158 | Search' hl group 159 | - Changed: in visual mode, mm is not m (it is still mm 160 | in normal mode) 161 | - Removed: ms (sets the region under the cursor to the new 162 | 'main' region). mm and m will do so implicitly if 163 | a pre-existing region is detected under the cursor 164 | - Removed: mn and mp - however, instructions on how to 165 | restore this functionality can be found in |multiedit-settings| 166 | - Fixed: cursor position after editing 167 | - Fixed: old regions weren't saved and couldn't be restored (would throw an 168 | error) 169 | - Fixed: when deleting the main region, the (automatically) assigned new main 170 | region wasn't chosen based on the order in which they were set 171 | - Fixed: errors caused by overlap detection when deleting regions 172 | - General code clean up and refactoring across the board 173 | 174 | 2013-03-11 [1.1.3] 175 | - Add :MultieditRestore to restore previous regions (if available). Uses 176 | mu 177 | - Add g:multiedit_auto_restore to control :MultieditRestore being called when 178 | edit mode is initiated without regions specified. 179 | 180 | 2013-03-11 [1.1.2] 181 | - Change :Multiedit! functionality to delete word before editing (like 182 | [C]hange command). Use C to initiate this mode. 183 | - Remove mw - now mm does it's job 184 | (viw:MultieditAddRegion) 185 | 186 | 2013-03-11 [1.1.1] 187 | - Add :Multiedit! - will move insert caret to the start of the word, instead 188 | of the end. Kep map for this is I 189 | - Fix automatically adding next/prev occurrence when using :MultieditNextMatch 190 | and :MultieditPreviousMatch 191 | 192 | 2013-03-10 [1.1.0] 193 | - Fix :MultieditClear "Key not present" bug 194 | - Fix bug with editing consecutive regions on the same line 195 | - Fix region reordering issue caused by clearing individual regions 196 | - Refactor multiedit#update() completely 197 | - Add :MultieditSet and ms - allowing you to change the "editable" 198 | region. 199 | 200 | 2013-03-07 [1.0.0] 201 | - Implement next/previous match functionality (like CMD+D in ST2) 202 | - Fix :MultieditAddRegion not accepting ranges 203 | 204 | 2013-03-06 205 | - Move functions to autoload/multiedit.vim 206 | - Remove synchronized editing (caused more problems than helped) 207 | - Highlights keep up with edits 208 | - Remove mouse mappings 209 | - Replace s with :commands, see plugin/multiedit.vim 210 | - Add "abort" keymaps that will disengage edit mode (and insert mode) if 211 | pressed. These are , , - which the plugin can't take into 212 | account. 213 | - Fix bugs with regions on the same line 214 | 215 | 2013-03-03 216 | - New marker system enables you to place disposable markers 217 | - Change setting variable name convention 218 | - Remove g:multedit_auto_update 219 | - Add g:multiedit_mark_character 220 | - Add mouse map 221 | - Highlight first selection differently (though colors need changing) 222 | - Add clear() and addMatch() 223 | - General code cleanup 224 | 225 | 2013-02-23 226 | - Auto-reset on InsertLeave by default (can be disabled) 227 | - Auto-update can be disabled 228 | - Add INSERT, APPEND and CHANGE triggers 229 | - Change default keymaps 230 | - Fix g:multiedit_nomappings setting 231 | 232 | 2012-07-01 233 | - First experimental version 234 | 235 | ============================================================================== 236 | vim:tw=78:ts=8:ft=help:norl:noet:fen:fdl=0: 237 | -------------------------------------------------------------------------------- /plugin/multiedit.vim: -------------------------------------------------------------------------------- 1 | " *multiedit.txt* Multi-editing for Vim 2 | " 3 | " Version: 2.0.2 4 | " Author: Henrik Lissner 5 | " License: MIT license 6 | " 7 | " Inspired by https://github.com/felixr/vim-multiedit, this plugin hopes to 8 | " fill that multi-cursor-shaped gap in your heart that Sublime Text 2 left you 9 | " with. 10 | 11 | if exists('g:loaded_multiedit') || &cp 12 | finish 13 | endif 14 | let g:loaded_multiedit = 1 15 | 16 | 17 | " Settings 18 | if !exists('g:multiedit_no_mappings') 19 | let g:multiedit_no_mappings = 0 20 | endif 21 | 22 | if !exists('g:multiedit_auto_reset') 23 | let g:multiedit_auto_reset = 1 24 | endif 25 | 26 | if !exists('g:multiedit_mark_character') 27 | let g:multiedit_mark_character = '|' 28 | endif 29 | 30 | if !exists('g:multiedit_auto_restore') 31 | let g:multiedit_auto_restore = 1 32 | endif 33 | 34 | 35 | " Color highlights 36 | hi default link MultieditRegions Search 37 | hi default link MultieditFirstRegion IncSearch 38 | 39 | 40 | " Mappings 41 | com! -bar -range MultieditAddRegion call multiedit#addRegion() 42 | com! -bar -nargs=1 MultieditAddMark call multiedit#addMark() 43 | com! -bar -bang Multiedit call multiedit#start() 44 | com! -bar MultieditClear call multiedit#clear() 45 | com! -bar MultieditReset call multiedit#reset() 46 | com! -bar MultieditRestore call multiedit#again() 47 | com! -bar -nargs=1 MultieditHop call multiedit#jump() 48 | 49 | if g:multiedit_no_mappings != 1 50 | " Insert a disposable marker after the cursor 51 | nmap ma :MultieditAddMark a 52 | " Insert a disposable marker before the cursor 53 | nmap mi :MultieditAddMark i 54 | " Make a new line and insert a marker 55 | nmap mo o:MultieditAddMark i 56 | nmap mO O:MultieditAddMark i 57 | " Insert a marker at the end/start of a line 58 | nmap mA $:MultieditAddMark a 59 | nmap mI ^:MultieditAddMark i 60 | " Make the current selection/word an edit region 61 | vmap m :MultieditAddRegion 62 | nmap mm viw:MultieditAddRegion 63 | " Restore the regions from a previous edit session 64 | nmap mu :MultieditRestore 65 | " Move cursor between regions n times 66 | map ]m :MultieditHop 1 67 | map [m :MultieditHop -1 68 | " Start editing! 69 | nmap M :Multiedit 70 | " Clear the word and start editing 71 | nmap C :Multiedit! 72 | " Unset the region under the cursor 73 | nmap md :MultieditClear 74 | " Unset all regions 75 | nmap mr :MultieditReset 76 | endif 77 | 78 | " vim: set foldmarker={{,}} foldlevel=0 foldmethod=marker 79 | --------------------------------------------------------------------------------