settings

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

commit 811d613fcd430d4a218ea7e4ca398ef586d1bf6e
parent 43c30cb259f89d2f23c3359b322229be18d1a51e
Author: Paul Longtine <paullongtine@gmail.com>
Date:   Sat Aug 30 14:31:10 2014

wooo vi woooooooo

Diffstat:
 vim/.vim/.netrwhist                    |    8 +-
 vim/.vim/.vimrc                        |   21 +-
 vim/.vim/autoload/multiple_cursors.vim | 1023 +++++++++++++++++++++++++++++++++-
 vim/.vim/autoload/pathogen.vim         |  347 +++++++++++-
 vim/.vim/plugin/emacs-bindings.vim     |   56 ++-
 vim/.vim/plugin/multiple_cursors.vim   |   66 ++-
 vim/.vimrc                             |   21 +-
 7 files changed, 1542 insertions(+), 0 deletions(-)

diff --git a/vim/.vim/.netrwhist b/vim/.vim/.netrwhist @@ -0,0 +1,8 @@ +let g:netrw_dirhistmax =10 +let g:netrw_dirhist_cnt =6 +let g:netrw_dirhist_1='/home/paul/.vim' +let g:netrw_dirhist_2='/home/paul/.vim/autoload' +let g:netrw_dirhist_3='/home/paul/.vim' +let g:netrw_dirhist_4='/home/paul/.vim/autoload' +let g:netrw_dirhist_5='/home/paul/.vim' +let g:netrw_dirhist_6='/home/paul/.vim/plugin' diff --git a/vim/.vim/.vimrc b/vim/.vim/.vimrc @@ -0,0 +1,21 @@ +set tabstop=4 + +set shiftwidth=4 + +set smarttab + +set showcmd + +set ruler + +set autoindent + +imap <M-Space> <Esc> + +" Default mapping +let g:multi_cursor_use_default_mapping=0 +let g:multi_cursor_start_key='<C-l>' +let g:multi_cursor_next_key='<C-l>' +let g:multi_cursor_prev_key='<C-p>' +let g:multi_cursor_skip_key='<C-x>' +let g:multi_cursor_quit_key='<Esc>' diff --git a/vim/.vim/autoload/multiple_cursors.vim b/vim/.vim/autoload/multiple_cursors.vim @@ -0,0 +1,1023 @@ +"=============================================================================== +" Initialization +"=============================================================================== + +" Tweak key settings. If the key is set using 'expr-quote' (h: expr-quote), then +" there's nothing that we need to do. If it's set using raw strings, then we +" need to convert it. We need to resort to such voodoo exec magic here to get +" it to work the way we like. '<C-n>' is converted to '\<C-n>' by the end and +" the global vars are replaced by their new value. This is ok since the mapping +" using '<C-n>' should already have completed in the plugin file. +for key in [ 'g:multi_cursor_next_key', + \ 'g:multi_cursor_prev_key', + \ 'g:multi_cursor_skip_key', + \ 'g:multi_cursor_quit_key' ] + if exists(key) + " Translate raw strings like "<C-n>" into key code like "\<C-n>" + exec 'let temp = '.key + if temp =~ '^<.*>$' + exec 'let '.key.' = "\'.temp.'"' + endif + else + " If the user didn't define it, initialize it to an empty string so the + " logic later don't break + exec 'let '.key.' = ""' + endif +endfor + +" These keys will not be replicated at every cursor location. Make sure that +" this assignment happens AFTER the key tweak setting above +let s:special_keys = { + \ 'v': [ g:multi_cursor_next_key, g:multi_cursor_prev_key, g:multi_cursor_skip_key ], + \ 'n': [ g:multi_cursor_next_key ], + \ } + +" The highlight group we use for all the cursors +let s:hi_group_cursor = 'multiple_cursors_cursor' + +" The highlight group we use for all the visual selection +let s:hi_group_visual = 'multiple_cursors_visual' + +" Set up highlighting +if !hlexists(s:hi_group_cursor) + exec "highlight ".s:hi_group_cursor." term=reverse cterm=reverse gui=reverse" +endif +if !hlexists(s:hi_group_visual) + exec "highlight link ".s:hi_group_visual." Visual" +endif + +"=============================================================================== +" Internal Mappings +"=============================================================================== + +inoremap <silent> <Plug>(i) <C-o>:call <SID>process_user_input()<CR> +nnoremap <silent> <Plug>(i) :call <SID>process_user_input()<CR> +xnoremap <silent> <Plug>(i) :<C-u>call <SID>process_user_input()<CR> + +inoremap <silent> <Plug>(a) <C-o>:call <SID>apply_user_input_next('i')<CR> +nnoremap <silent> <Plug>(a) :call <SID>apply_user_input_next('n')<CR> +xnoremap <silent> <Plug>(a) :<C-u>call <SID>apply_user_input_next('v')<CR> + +inoremap <silent> <Plug>(d) <C-o>:call <SID>detect_bad_input()<CR> +nnoremap <silent> <Plug>(d) :call <SID>detect_bad_input()<CR> +xnoremap <silent> <Plug>(d) :<C-u>call <SID>detect_bad_input()<CR> + +inoremap <silent> <Plug>(w) <C-o>:call <SID>wait_for_user_input('')<CR> +nnoremap <silent> <Plug>(w) :call <SID>wait_for_user_input('')<CR> +xnoremap <silent> <Plug>(w) :<C-u>call <SID>wait_for_user_input('')<CR> + +" Note that although these mappings are seemingly triggerd from Visual mode, +" they are in fact triggered from Normal mode. We quit visual mode to allow the +" virtual highlighting to take over +nnoremap <silent> <Plug>(p) :<C-u>call multiple_cursors#prev()<CR> +nnoremap <silent> <Plug>(s) :<C-u>call multiple_cursors#skip()<CR> +nnoremap <silent> <Plug>(n) :<C-u>call multiple_cursors#new('v')<CR> + +"=============================================================================== +" Public Functions +"=============================================================================== + +" Print some debugging info +function! multiple_cursors#debug() + call s:cm.debug() +endfunction + +function! multiple_cursors#get_latency_debug_file() + return s:latency_debug_file +endfunction + +" Creates a new cursor. Different logic applies depending on the mode the user +" is in and the current state of the buffer. +" 1. In normal mode, a new cursor is created at the end of the word under Vim's +" normal cursor +" 2. In visual mode, if the visual selection covers more than one line, a new +" cursor is created at the beginning of each line +" 3. In visual mode, if the visual selection covers a single line, a new cursor +" is created at the end of the visual selection. Another cursor will be +" attempted to be created at the next occurrence of the visual selection +function! multiple_cursors#new(mode) + if a:mode ==# 'n' + " Reset all existing cursors, don't restore view and setting + call s:cm.reset(0, 0) + + " Select the word under cursor to set the '< and '> marks + exec "normal! viw" + call s:exit_visual_mode() + + " Add cursor with the current visual selection + call s:cm.add(s:pos("'>"), s:region("'<", "'>")) + call s:wait_for_user_input('v') + elseif a:mode ==# 'v' + " If the visual area covers the same line, then do a search for next + " occurrence + let start = line("'<") + let finish = line("'>") + if start != finish + call s:cm.reset(0, 0) + let col = col("'<") + for line in range(line("'<"), line("'>")) + let pos = [line, col] + call s:cm.add(pos) + endfor + " Start in normal mode + call s:wait_for_user_input('n') + else + " Came directly from visual mode + if s:cm.is_empty() + call s:cm.reset(0, 0) + + if visualmode() ==# 'V' + let left = [line('.'), 1] + let right = [line('.'), col('$')-1] + if right[1] == 0 " empty line + return + endif + call s:cm.add(right, [left, right]) + else + call s:cm.add(s:pos("'>"), s:region("'<", "'>")) + endif + endif + let content = s:get_text(s:region("'<", "'>")) + let next = s:find_next(content) + if s:cm.add(next[1], next) + call s:update_visual_markers(next) + else + call cursor(s:cm.get_current().position) + echohl WarningMsg | echo 'No more matches' | echohl None + endif + call s:wait_for_user_input('v') + endif + endif +endfunction + +" Delete the current cursor. If there's no more cursors, stop the loop +function! multiple_cursors#prev() + call s:cm.delete_current() + if !s:cm.is_empty() + call s:update_visual_markers(s:cm.get_current().visual) + call cursor(s:cm.get_current().position) + call s:wait_for_user_input('v') + endif +endfunction + +" Skip the current cursor and move to the next cursor +function! multiple_cursors#skip() + call s:cm.delete_current() + let content = s:get_text(s:region("'<", "'>")) + let next = s:find_next(content) + call s:cm.add(next[1], next) + call s:update_visual_markers(next) + call s:wait_for_user_input('v') +endfunction + +" Search for pattern between the start and end line number. For each match, add +" a virtual cursor at the end and start multicursor mode +" This function is called from a command. User commands in Vim do not support +" passing in column ranges. If the user selects a block of text in visual mode, +" but not visual line mode, we only want to match patterns within the actual +" visual selection. We get around this by checking the last visual selection and +" see if its start and end lines match the input. If so, we assume that the user +" did a normal visual selection and we use the '< and '> marks to define the +" region instead of start and end from the method parameter. +function! multiple_cursors#find(start, end, pattern) + let s:cm.saved_winview = winsaveview() + let s:cm.start_from_find = 1 + if visualmode() ==# 'v' && a:start == line("'<") && a:end == line("'>") + let pos1 = s:pos("'<") + let pos2 = s:pos("'>") + else + let pos1 = [a:start, 1] + let pos2 = [a:end, col([a:end, '$'])] + endif + call cursor(pos1) + let first = 1 + while 1 + if first + " First search starts from the current position + let match = search(a:pattern, 'cW') + let first = 0 + else + let match = search(a:pattern, 'W') + endif + if !match + break + endif + let left = s:pos('.') + call search(a:pattern, 'ceW') + let right = s:pos('.') + if s:compare_pos(right, pos2) > 0 + break + endif + call s:cm.add(right, [left, right]) + " Redraw here forces the cursor movement to be updated. This prevents the + " jerky behavior when doing any action once the cursors are added. But it + " also slows down adding the cursors dramatically. We need to a better + " solution here + " redraw + endwhile + if s:cm.is_empty() + call winrestview(s:cm.saved_winview) + echohl ErrorMsg | echo 'No match found' | echohl None + return + else + echohl Normal | echo 'Added '.s:cm.size().' cursor'.(s:cm.size()>1?'s':'') | echohl None + call s:wait_for_user_input('v') + endif +endfunction + +"=============================================================================== +" Cursor class +"=============================================================================== +let s:Cursor = {} + +" Create a new cursor. Highlight it and save the current line length +function! s:Cursor.new(position) + let obj = copy(self) + let obj.position = copy(a:position) + let obj.visual = [] + let obj.cursor_hi_id = s:highlight_cursor(a:position) + let obj.visual_hi_id = 0 + let obj.line_length = col([a:position[0], '$']) + return obj +endfunction + +" Return the line the cursor is on +function! s:Cursor.line() dict + return self.position[0] +endfunction + +" Return the column the cursor is on +function! s:Cursor.column() dict + return self.position[1] +endfunction + +" Move the cursor location by the number of lines and columns specified in the +" input. The input can be negative. +function! s:Cursor.move(line, column) dict + let self.position[0] += a:line + let self.position[1] += a:column + if !empty(self.visual) + let self.visual[0][0] += a:line + let self.visual[0][1] += a:column + let self.visual[1][0] += a:line + let self.visual[1][1] += a:column + endif + call self.update_highlight() +endfunction + +" Update the current position of the cursor +function! s:Cursor.update_position(pos) dict + let self.position[0] = a:pos[0] + let self.position[1] = a:pos[1] + call self.update_highlight() +endfunction + +" Reapply the highlight on the cursor +function! s:Cursor.update_highlight() dict + call s:cm.remove_highlight(self.cursor_hi_id) + let self.cursor_hi_id = s:highlight_cursor(self.position) +endfunction + +" Refresh the length of the line the cursor is on. This could change from +" underneath +function! s:Cursor.update_line_length() dict + let self.line_length = col([self.line(), '$']) +endfunction + +" Update the visual selection and its highlight +function! s:Cursor.update_visual_selection(region) dict + let self.visual = deepcopy(a:region) + call s:cm.remove_highlight(self.visual_hi_id) + let self.visual_hi_id = s:highlight_region(a:region) +endfunction + +" Remove the visual selection and its highlight +function! s:Cursor.remove_visual_selection() dict + let self.visual = [] + " TODO(terryma): Move functionality into separate class + call s:cm.remove_highlight(self.visual_hi_id) + let self.visual_hi_id = 0 +endfunction + +"=============================================================================== +" CursorManager class +"=============================================================================== +let s:CursorManager = {} + +" Constructor +function! s:CursorManager.new() + let obj = copy(self) + " List of Cursors we're managing + let obj.cursors = [] + " Current index into the s:cursors array + let obj.current_index = -1 + " This marks the starting cursor index into the s:cursors array + let obj.starting_index = -1 + " We save some user settings when the plugin loads initially + let obj.saved_settings = { + \ 'virtualedit': &virtualedit, + \ 'cursorline': &cursorline, + \ 'lazyredraw': &lazyredraw, + \ 'paste': &paste, + \ } + " We save the window view when multicursor mode is entered + let obj.saved_winview = [] + " Track whether we started multicursor mode from calling multiple_cursors#find + let obj.start_from_find = 0 + return obj +endfunction + +" Clear all cursors and their highlights +function! s:CursorManager.reset(restore_view, restore_setting) dict + if a:restore_view + " Return the view back to the beginning + if !empty(self.saved_winview) + call winrestview(self.saved_winview) + endif + + " If the cursor moved, just restoring the view could get confusing, let's + " put the cursor at where the user left it. Only do this if we didn't start + " from find mode + if !self.is_empty() && !self.start_from_find + call cursor(self.get(0).position) + endif + endif + + " Delete all cursors and clear their highlights. Don't do clearmatches() as + " that will potentially interfere with other plugins + if !self.is_empty() + for i in range(self.size()) + call self.remove_highlight(self.get(i).cursor_hi_id) + call self.remove_highlight(self.get(i).visual_hi_id) + endfor + endif + + let self.cursors = [] + let self.current_index = -1 + let self.starting_index = -1 + let self.saved_winview = [] + let self.start_from_find = 0 + let s:char = '' + if a:restore_setting + call self.restore_user_settings() + endif +endfunction + +" Returns 0 if it's not managing any cursors at the moment +function! s:CursorManager.is_empty() dict + return self.size() == 0 +endfunction + +" Returns the number of cursors it's managing +function! s:CursorManager.size() dict + return len(self.cursors) +endfunction + +" Returns the current cursor +function! s:CursorManager.get_current() dict + return self.cursors[self.current_index] +endfunction + +" Returns the cursor at index i +function! s:CursorManager.get(i) dict + return self.cursors[a:i] +endfunction + +" Removes the current cursor and all its associated highlighting. Also update +" the current index +function! s:CursorManager.delete_current() dict + call self.remove_highlight(self.get_current().cursor_hi_id) + call self.remove_highlight(self.get_current().visual_hi_id) + call remove(self.cursors, self.current_index) + let self.current_index -= 1 +endfunction + +" Remove the highlighting if its matchid exists +function! s:CursorManager.remove_highlight(hi_id) dict + if a:hi_id + " If the user did a matchdelete or a clearmatches, we don't want to barf if + " the matchid is no longer valid + silent! call matchdelete(a:hi_id) + endif +endfunction + +function! s:CursorManager.debug() dict + let i = 0 + for c in self.cursors + echom 'cursor #'.i.': pos='.string(c.position).' visual='.string(c.visual) + let i+=1 + endfor + echom 'input = '.s:char + echom 'index = '.self.current_index + echom 'pos = '.string(s:pos('.')) + echom '''< = '.string(s:pos("'<")) + echom '''> = '.string(s:pos("'>")) + echom 'to mode = '.s:to_mode + echom 'from mode = '.s:from_mode + " echom 'special keys = '.string(s:special_keys) + echom ' ' +endfunction + +" Sync the current cursor to the current Vim cursor. This includes updating its +" location, its highlight, and potentially its visual region. Return true if the +" position changed, false otherwise +function! s:CursorManager.update_current() dict + let cur = self.get_current() + if s:to_mode ==# 'v' || s:to_mode ==# 'V' + " If we're in visual line mode, we need to go to visual mode before we can + " update the visual region + if s:to_mode ==# 'V' + exec "normal! gvv\<Esc>" + endif + + " Sets the cursor at the right place + exec "normal! gv\<Esc>" + call cur.update_visual_selection(s:get_visual_region(s:pos('.'))) + elseif s:from_mode ==# 'v' || s:from_mode ==# 'V' + call cur.remove_visual_selection() + endif + let vdelta = line('$') - s:saved_linecount + " If the total number of lines changed in the buffer, we need to potentially + " adjust other cursor locations + if vdelta != 0 + if self.current_index != self.size() - 1 + let cur_line_length = len(getline(cur.line())) + let new_line_length = len(getline('.')) + for i in range(self.current_index+1, self.size()-1) + let hdelta = 0 + " Note: some versions of Vim don't like chaining function calls like + " a.b().c(). For compatibility reasons, don't do it + let c = self.get(i) + " If there're other cursors on the same line, we need to adjust their + " columns. This needs to happen before we adjust their line! + if cur.line() == c.line() + if vdelta > 0 + " Added a line + let hdelta = cur_line_length * -1 + else + " Removed a line + let hdelta = new_line_length + endif + endif + call c.move(vdelta, hdelta) + endfor + endif + else + " If the line length changes, for all the other cursors on the same line as + " the current one, update their cursor location as well + let hdelta = col('$') - cur.line_length + " Only do this if we're still on the same line as before + if hdelta != 0 && cur.line() == line('.') + " Update all the cursor's positions that occur after the current cursor on + " the same line + if self.current_index != self.size() - 1 + for i in range(self.current_index+1, self.size()-1) + let c = self.get(i) + " Only do it for cursors on the same line + if cur.line() == c.line() + call c.move(0, hdelta) + else + " Early exit, if we're not on the same line, neither will any cursor + " that come after this + break + endif + endfor + endif + endif + endif + + let pos = s:pos('.') + if cur.position == pos + return 0 + endif + call cur.update_position(pos) + return 1 +endfunction + +" Advance to the next cursor +function! s:CursorManager.next() dict + let self.current_index = (self.current_index + 1) % self.size() +endfunction + +" Start tracking cursor updates +function! s:CursorManager.start_loop() dict + let self.starting_index = self.current_index +endfunction + +" Returns true if we're cycled through all the cursors +function! s:CursorManager.loop_done() dict + return self.current_index == self.starting_index +endfunction + +" Tweak some user settings, and save our current window view. This is called +" every time multicursor mode is entered. +" virtualedit needs to be set to onemore for updates to work correctly +" cursorline needs to be turned off for the cursor highlight to work on the line +" where the real vim cursor is +" lazyredraw needs to be turned on to prevent jerky screen behavior with many +" cursors on screen +" paste mode needs to be switched off since it turns off a bunch of features +" that's critical for the plugin to function +function! s:CursorManager.initialize() dict + let self.saved_settings['virtualedit'] = &virtualedit + let self.saved_settings['cursorline'] = &cursorline + let self.saved_settings['lazyredraw'] = &lazyredraw + let self.saved_settings['paste'] = &paste + let &virtualedit = "onemore" + let &cursorline = 0 + let &lazyredraw = 1 + let &paste = 0 + " We could have already saved the view from multiple_cursors#find + if !self.start_from_find + let self.saved_winview = winsaveview() + endif +endfunction + +" Restore user settings. +function! s:CursorManager.restore_user_settings() dict + if !empty(self.saved_settings) + let &virtualedit = self.saved_settings['virtualedit'] + let &cursorline = self.saved_settings['cursorline'] + let &lazyredraw = self.saved_settings['lazyredraw'] + let &paste = self.saved_settings['paste'] + endif +endfunction + +" Reselect the current cursor's region in visual mode +function! s:CursorManager.reapply_visual_selection() dict + call s:select_in_visual_mode(self.get_current().visual) +endfunction + +" Creates a new virtual cursor as 'pos' +" Optionally a 'region' object can be passed in as second argument. If set, the +" visual region of the cursor will be set to it +" Return true if the cursor has been successfully added, false otherwise +" Mode change: Normal -> Normal +" Cursor change: None (TODO Should we set Vim's cursor to pos?) +function! s:CursorManager.add(pos, ...) dict + " Lazy init + if self.is_empty() + call self.initialize() + endif + + " Don't add duplicates + let i = 0 + for c in self.cursors + if c.position == a:pos + return 0 + endif + let i+=1 + endfor + + let cursor = s:Cursor.new(a:pos) + + " Save the visual selection + if a:0 > 0 + call cursor.update_visual_selection(a:1) + endif + + call add(self.cursors, cursor) + let self.current_index += 1 + return 1 +endfunction + +"=============================================================================== +" Variables +"=============================================================================== + +" This is the last user input that we're going to replicate, in its string form +let s:char = '' +" This is the mode the user is in before s:char +let s:from_mode = '' +" This is the mode the user is in after s:char +let s:to_mode = '' +" This is the total number of lines in the buffer before processing s:char +let s:saved_linecount = -1 +" This is used to apply the highlight fix. See s:apply_highight_fix() +let s:saved_line = 0 +" This is the number of cursor locations where we detected an input that we +" cannot play back +let s:bad_input = 0 +" Singleton cursor manager instance +let s:cm = s:CursorManager.new() + +"=============================================================================== +" Utility functions +"=============================================================================== + +" Return the position of the input marker as a two element array. First element +" is the line number, second element is the column number +function! s:pos(mark) + let pos = getpos(a:mark) + return [pos[1], pos[2]] +endfunction + +" Return the region covered by the input markers as a two element array. First +" element is the position of the start marker, second element is the position of +" the end marker +function! s:region(start_mark, end_mark) + return [s:pos(a:start_mark), s:pos(a:end_mark)] +endfunction + +" Exit visual mode and go back to normal mode +" The reason for the additional gv\<Esc> is that it allows the cursor to stay +" on where it was before exiting +" Mode change: Normal -> Normal or Visual -> Normal +" Cursor change: If in visual mode, changed to exactly where it was on screen in +" visual mode. If in normal mode, changed to where the cursor was when the last +" visual selection ended +function! s:exit_visual_mode() + exec "normal! \<Esc>gv\<Esc>" +endfunction + +" Visually select input region, where region is an array containing the start +" and end position. If start is after end, the selection simply goes backwards. +" Typically m<, m>, and gv would be a simple way of accomplishing this, but on +" some systems, the m< and m> marks are not supported. Note that v`` has random +" behavior if `` is the same location as the cursor location. +" Mode change: Normal -> Visual +" Cursor change: Set to end of region +" TODO: Refactor this and s:update_visual_markers +" FIXME: By using m` we're destroying the user's jumplist. We should use a +" different mark and use :keepjump +function! s:select_in_visual_mode(region) + if a:region[0] == a:region[1] + normal! v + else + call cursor(a:region[1]) + normal! m` + call cursor(a:region[0]) + normal! v`` + endif + + " Unselect and reselect it again to properly set the '< and '> markers + exec "normal! \<Esc>gv" +endfunction + +" Update '< and '> to the input region +" Mode change: Normal -> Normal +" Cursor change: Set to the end of the region +function! s:update_visual_markers(region) + if a:region[0] == a:region[1] + normal! v + else + call cursor(a:region[1]) + normal! m` + call cursor(a:region[0]) + normal! v`` + endif + call s:exit_visual_mode() +endfunction + +" Finds the next occurrence of the input text in the current buffer. +" Search is case sensitive +" Mode change: Normal -> Normal +" Cursor change: Set to the end of the match +function! s:find_next(text) + let pattern = '\V\C'.substitute(escape(a:text, '\'), '\n', '\\n', 'g') + call search(pattern) + let start = s:pos('.') + call search(pattern, 'ce') + let end = s:pos('.') + return [start, end] +endfunction + +" Highlight the position using the cursor highlight group +function! s:highlight_cursor(pos) + " Give cursor highlight high priority, to overrule visual selection + return matchadd(s:hi_group_cursor, '\%'.a:pos[0].'l\%'.a:pos[1].'c', 99999) +endfunction + +" Compare two position arrays. Return a negative value if lhs occurs before rhs, +" positive value if after, and 0 if they are the same. +function! s:compare_pos(l, r) + " If number lines are the same, compare columns + return a:l[0] ==# a:r[0] ? a:l[1] - a:r[1] : a:l[0] - a:r[0] +endfunction + +" Highlight the area bounded by the input region. The logic here really stinks, +" it's frustrating that Vim doesn't have a built in easier way to do this. None +" of the \%V or \%'m solutions work because we need the highlighting to stay for +" multiple places. +function! s:highlight_region(region) + let s = sort(copy(a:region), "s:compare_pos") + if s:to_mode ==# 'V' + let pattern = '\%>'.(s[0][0]-1).'l\%<'.(s[1][0]+1).'l.*\ze.\_$' + else + if (s[0][0] == s[1][0]) + " Same line + let pattern = '\%'.s[0][0].'l\%>'.(s[0][1]-1).'c.*\%<'.(s[1][1]+1).'c.' + else + " Two lines + let s1 = '\%'.s[0][0].'l.\%>'.s[0][1].'c.*' + let s2 = '\%'.s[1][0].'l.*\%<'.s[1][1].'c..' + let pattern = s1.'\|'.s2 + " More than two lines + if (s[1][0] - s[0][0] > 1) + let pattern = pattern.'\|\%>'.s[0][0].'l\%<'.s[1][0].'l.*\ze.\_$' + endif + endif + endif + return matchadd(s:hi_group_visual, pattern) +endfunction + +" Perform the operation that's necessary to revert us from one mode to another +function! s:revert_mode(from, to) + if a:to ==# 'v' + call s:cm.reapply_visual_selection() + endif + if a:to ==# 'V' + call s:cm.reapply_visual_selection() + normal! V + endif + if a:to ==# 'n' && a:from ==# 'i' + stopinsert + endif +endfunction + +" Consume all the additional character the user typed between the last +" getchar() and here, to avoid potential race condition. +" TODO(terryma): This solves the problem of cursors getting out of sync, but +" we're potentially losing user input. We COULD replay these characters as +" well... +function! s:feedkeys(keys) + while 1 + let c = getchar(0) + " Checking type is important, when strings are compared with integers, + " strings are always converted to ints, and all strings are equal to 0 + if type(c) == 0 && c == 0 + break + endif + endwhile + call feedkeys(a:keys) +endfunction + +" Take the user input and apply it at every cursor +function! s:process_user_input() + " Grr this is frustrating. In Insert mode, between the feedkey call and here, + " the current position could actually CHANGE for some odd reason. Forcing a + " position reset here + call cursor(s:cm.get_current().position) + + " Before applying the user input, we need to revert back to the mode the user + " was in when the input was entered + call s:revert_mode(s:to_mode, s:from_mode) + + " Update the line length BEFORE applying any actions. TODO(terryma): Is there + " a better place to do this? + call s:cm.get_current().update_line_length() + let s:saved_linecount = line('$') + + " Apply the user input. Note that the above could potentially change mode, we + " use the mapping below to help us determine what the new mode is + " Note that it's possible that \<Plug>(a) never gets called, we have a + " detection mechanism using \<Plug>(d). See its documentation for more details + + " Assume that input is not valid + let s:valid_input = 0 + + " If we're coming from insert mode or going into insert mode, always chain the + " undos together. + " FIXME(terryma): Undo always places the cursor at the beginning of the line. + " Figure out why. + if s:from_mode ==# 'i' || s:to_mode ==# 'i' + silent! undojoin | call s:feedkeys(s:char."\<Plug>(a)") + else + call s:feedkeys(s:char."\<Plug>(a)") + endif + + " Even when s:char produces invalid input, this method is always called. The + " 't' here is important + call feedkeys("\<Plug>(d)", 't') +endfunction + +" This method is always called during fanout, even when a bad user input causes +" s:apply_user_input_next to not be called. We detect that and force the method +" to be called to continue the fanout process +function! s:detect_bad_input() + if !s:valid_input + " We ignore the bad input and force invoke s:apply_user_input_next + call feedkeys("\<Plug>(a)") + let s:bad_input += 1 + endif +endfunction + +" Apply the user input at the next cursor location +function! s:apply_user_input_next(mode) + let s:valid_input = 1 + + " Save the current mode, only if we haven't already + if empty(s:to_mode) + let s:to_mode = a:mode + if s:to_mode ==# 'v' + if visualmode() ==# 'V' + let s:to_mode = 'V' + endif + endif + endif + + " Update the current cursor's information + let changed = s:cm.update_current() + + " Advance the cursor index + call s:cm.next() + + " We're done if we're made the full round + if s:cm.loop_done() + if s:to_mode ==# 'v' || s:to_mode ==# 'V' + " This is necessary to set the "'<" and "'>" markers properly + call s:update_visual_markers(s:cm.get_current().visual) + endif + call feedkeys("\<Plug>(w)") + else + " Continue to next + call feedkeys("\<Plug>(i)") + endif +endfunction + +" If pos is equal to the left side of the visual selection, the region start +" from end to start +function! s:get_visual_region(pos) + let left = s:pos("'<") + let right = s:pos("'>") + if a:pos == left + let region = [right, left] + else + let region = [left, right] + endif + return region +endfunction + +" Return the content of the buffer between the input region. This is used to +" find the next match in the buffer +" Mode change: Normal -> Normal +" Cursor change: None +function! s:get_text(region) + let lines = getline(a:region[0][0], a:region[1][0]) + let lines[-1] = lines[-1][:a:region[1][1] - 1] + let lines[0] = lines[0][a:region[0][1] - 1:] + return join(lines, "\n") +endfunction + +" Wrapper around getchar() that returns the string representation of the user +" input +function! s:get_char() + let c = getchar() + " If the character is a number, then it's not a special key + if type(c) == 0 + let c = nr2char(c) + endif + return c +endfunction + +" Quits multicursor mode and clears all cursors. Return true if exited +" successfully. +function! s:exit() + if s:char !=# g:multi_cursor_quit_key + return 0 + endif + let exit = 0 + if s:from_mode ==# 'n' + let exit = 1 + elseif (s:from_mode ==# 'v' || s:from_mode ==# 'V') && + \ g:multi_cursor_exit_from_visual_mode + let exit = 1 + elseif s:from_mode ==# 'i' && g:multi_cursor_exit_from_insert_mode + stopinsert + let exit = 1 + endif + if exit + call s:cm.reset(1, 1) + return 1 + endif + return 0 +endfunction + +" These keys don't get faned out to all cursor locations. Instead, they're used +" to add new / remove existing cursors +" Precondition: The function is only called when the keys and mode respect the +" setting in s:special_keys +function! s:handle_special_key(key, mode) + " Use feedkeys here instead of calling the function directly to prevent + " increasing the call stack, since feedkeys execute after the current call + " finishes + if a:key == g:multi_cursor_next_key + call s:feedkeys("\<Plug>(n)") + elseif a:key == g:multi_cursor_prev_key + call s:feedkeys("\<Plug>(p)") + elseif a:key == g:multi_cursor_skip_key + call s:feedkeys("\<Plug>(s)") + endif +endfunction + +" The last line where the normal Vim cursor is always seems to highlighting +" issues if the cursor is on the last column. Vim's cursor seems to override the +" highlight of the virtual cursor. This won't happen if the virtual cursor isn't +" the last character on the line. This is a hack to add an empty space on the +" Vim cursor line right before we do the redraw, we'll revert the change +" immedidately after the redraw so the change should not be intrusive to the +" user's buffer content +function! s:apply_highlight_fix() + " Only do this if we're on the last character of the line + if col('.') == col('$') + let s:saved_line = getline('.') + if s:from_mode ==# 'i' + silent! undojoin | call setline('.', s:saved_line.' ') + else + call setline('.', s:saved_line.' ') + endif + endif +endfunction + +" Revert the fix if it was applied earlier +function! s:revert_highlight_fix() + if type(s:saved_line) == 1 + if s:from_mode ==# 'i' + silent! undojoin | call setline('.', s:saved_line) + else + call setline('.', s:saved_line) + endif + endif + let s:saved_line = 0 +endfunction + +function! s:display_error() + if s:bad_input > 0 + echohl ErrorMsg | + \ echo "Key '".s:char."' cannot be replayed at ". + \ s:bad_input." cursor location".(s:bad_input == 1 ? '' : 's') | + \ echohl Normal + endif + let s:bad_input = 0 +endfunction + +let s:latency_debug_file = '' +function! s:start_latency_measure() + if g:multi_cursor_debug_latency + let s:start_time = reltime() + endif +endfunction + +function! s:skip_latency_measure() + if g:multi_cursor_debug_latency + let s:skip_latency_measure = 1 + endif +endfunction + +function! s:end_latency_measure() + if g:multi_cursor_debug_latency && !empty(s:char) + if empty(s:latency_debug_file) + let s:latency_debug_file = tempname() + exec 'redir >> '.s:latency_debug_file + silent! echom "Starting latency debug at ".reltimestr(reltime()) + redir END + endif + + if !s:skip_latency_measure + exec 'redir >> '.s:latency_debug_file + silent! echom "Processing '".s:char."' took ".string(str2float(reltimestr(reltime(s:start_time)))*1000).' ms in '.s:cm.size().' cursors. mode = '.s:from_mode + redir END + endif + endif + let s:skip_latency_measure = 0 +endfunction + +function! s:wait_for_user_input(mode) + let s:from_mode = a:mode + if empty(a:mode) + let s:from_mode = s:to_mode + endif + let s:to_mode = '' + + call s:display_error() + + " Right before redraw, apply the highlighting bug fix + call s:apply_highlight_fix() + + redraw + + " Immediately revert the change to leave the user's buffer unchanged + call s:revert_highlight_fix() + + call s:end_latency_measure() + + let s:char = s:get_char() + + call s:start_latency_measure() + + " Clears any echoes we might've added + normal! :<Esc> + + if s:exit() + return + endif + + " If the key is a special key and we're in the right mode, handle it + if index(get(s:special_keys, s:from_mode, []), s:char) != -1 + call s:handle_special_key(s:char, s:from_mode) + call s:skip_latency_measure() + else + call s:cm.start_loop() + call s:feedkeys("\<Plug>(i)") + endif +endfunction diff --git a/vim/.vim/autoload/pathogen.vim b/vim/.vim/autoload/pathogen.vim @@ -0,0 +1,347 @@ +" pathogen.vim - path option manipulation +" Maintainer: Tim Pope <http://tpo.pe/> +" Version: 2.3 + +" Install in ~/.vim/autoload (or ~\vimfiles\autoload). +" +" For management of individually installed plugins in ~/.vim/bundle (or +" ~\vimfiles\bundle), adding `execute pathogen#infect()` to the top of your +" .vimrc is the only other setup necessary. +" +" The API is documented inline below. + +if exists("g:loaded_pathogen") || &cp + finish +endif +let g:loaded_pathogen = 1 + +" Point of entry for basic default usage. Give a relative path to invoke +" pathogen#interpose() (defaults to "bundle/{}"), or an absolute path to invoke +" pathogen#surround(). Curly braces are expanded with pathogen#expand(): +" "bundle/{}" finds all subdirectories inside "bundle" inside all directories +" in the runtime path. +function! pathogen#infect(...) abort + for path in a:0 ? filter(reverse(copy(a:000)), 'type(v:val) == type("")') : ['bundle/{}'] + if path =~# '^\%({\=[$~\\/]\|{\=\w:[\\/]\).*[{}*]' + call pathogen#surround(path) + elseif path =~# '^\%([$~\\/]\|\w:[\\/]\)' + call s:warn('Change pathogen#infect('.string(path).') to pathogen#infect('.string(path.'/{}').')') + call pathogen#surround(path . '/{}') + elseif path =~# '[{}*]' + call pathogen#interpose(path) + else + call s:warn('Change pathogen#infect('.string(path).') to pathogen#infect('.string(path.'/{}').')') + call pathogen#interpose(path . '/{}') + endif + endfor + call pathogen#cycle_filetype() + if pathogen#is_disabled($MYVIMRC) + return 'finish' + endif + return '' +endfunction + +" Split a path into a list. +function! pathogen#split(path) abort + if type(a:path) == type([]) | return a:path | endif + if empty(a:path) | return [] | endif + let split = split(a:path,'\\\@<!\%(\\\\\)*\zs,') + return map(split,'substitute(v:val,''\\\([\\,]\)'',''\1'',"g")') +endfunction + +" Convert a list to a path. +function! pathogen#join(...) abort + if type(a:1) == type(1) && a:1 + let i = 1 + let space = ' ' + else + let i = 0 + let space = '' + endif + let path = "" + while i < a:0 + if type(a:000[i]) == type([]) + let list = a:000[i] + let j = 0 + while j < len(list) + let escaped = substitute(list[j],'[,'.space.']\|\\[\,'.space.']\@=','\\&','g') + let path .= ',' . escaped + let j += 1 + endwhile + else + let path .= "," . a:000[i] + endif + let i += 1 + endwhile + return substitute(path,'^,','','') +endfunction + +" Convert a list to a path with escaped spaces for 'path', 'tag', etc. +function! pathogen#legacyjoin(...) abort + return call('pathogen#join',[1] + a:000) +endfunction + +" Turn filetype detection off and back on again if it was already enabled. +function! pathogen#cycle_filetype() abort + if exists('g:did_load_filetypes') + filetype off + filetype on + endif +endfunction + +" Check if a bundle is disabled. A bundle is considered disabled if its +" basename or full name is included in the list g:pathogen_disabled. +function! pathogen#is_disabled(path) abort + if a:path =~# '\~$' + return 1 + endif + let sep = pathogen#slash() + let blacklist = map( + \ get(g:, 'pathogen_blacklist', get(g:, 'pathogen_disabled', [])) + + \ pathogen#split($VIMBLACKLIST), + \ 'substitute(v:val, "[\\/]$", "", "")') + return index(blacklist, fnamemodify(a:path, ':t')) != -1 || index(blacklist, a:path) != -1 +endfunction "}}}1 + +" Prepend the given directory to the runtime path and append its corresponding +" after directory. Curly braces are expanded with pathogen#expand(). +function! pathogen#surround(path) abort + let sep = pathogen#slash() + let rtp = pathogen#split(&rtp) + let path = fnamemodify(a:path, ':p:?[\\/]\=$??') + let before = filter(pathogen#expand(path), '!pathogen#is_disabled(v:val)') + let after = filter(reverse(pathogen#expand(path.sep.'after')), '!pathogen#is_disabled(v:val[0:-7])') + call filter(rtp, 'index(before + after, v:val) == -1') + let &rtp = pathogen#join(before, rtp, after) + return &rtp +endfunction + +" For each directory in the runtime path, add a second entry with the given +" argument appended. Curly braces are expanded with pathogen#expand(). +function! pathogen#interpose(name) abort + let sep = pathogen#slash() + let name = a:name + if has_key(s:done_bundles, name) + return "" + endif + let s:done_bundles[name] = 1 + let list = [] + for dir in pathogen#split(&rtp) + if dir =~# '\<after$' + let list += reverse(filter(pathogen#expand(dir[0:-6].name.sep.'after'), '!pathogen#is_disabled(v:val[0:-7])')) + [dir] + else + let list += [dir] + filter(pathogen#expand(dir.sep.name), '!pathogen#is_disabled(v:val)') + endif + endfor + let &rtp = pathogen#join(pathogen#uniq(list)) + return 1 +endfunction + +let s:done_bundles = {} + +" Invoke :helptags on all non-$VIM doc directories in runtimepath. +function! pathogen#helptags() abort + let sep = pathogen#slash() + for glob in pathogen#split(&rtp) + for dir in map(split(glob(glob), "\n"), 'v:val.sep."/doc/".sep') + if (dir)[0 : strlen($VIMRUNTIME)] !=# $VIMRUNTIME.sep && filewritable(dir) == 2 && !empty(split(glob(dir.'*.txt'))) && (!filereadable(dir.'tags') || filewritable(dir.'tags')) + silent! execute 'helptags' pathogen#fnameescape(dir) + endif + endfor + endfor +endfunction + +command! -bar Helptags :call pathogen#helptags() + +" Execute the given command. This is basically a backdoor for --remote-expr. +function! pathogen#execute(...) abort + for command in a:000 + execute command + endfor + return '' +endfunction + +" Section: Unofficial + +function! pathogen#is_absolute(path) abort + return a:path =~# (has('win32') ? '^\%([\\/]\|\w:\)[\\/]\|^[~$]' : '^[/~$]') +endfunction + +" Given a string, returns all possible permutations of comma delimited braced +" alternatives of that string. pathogen#expand('/{a,b}/{c,d}') yields +" ['/a/c', '/a/d', '/b/c', '/b/d']. Empty braces are treated as a wildcard +" and globbed. Actual globs are preserved. +function! pathogen#expand(pattern) abort + if a:pattern =~# '{[^{}]\+}' + let [pre, pat, post] = split(substitute(a:pattern, '\(.\{-\}\){\([^{}]\+\)}\(.*\)', "\\1\001\\2\001\\3", ''), "\001", 1) + let found = map(split(pat, ',', 1), 'pre.v:val.post') + let results = [] + for pattern in found + call extend(results, pathogen#expand(pattern)) + endfor + return results + elseif a:pattern =~# '{}' + let pat = matchstr(a:pattern, '^.*{}[^*]*\%($\|[\\/]\)') + let post = a:pattern[strlen(pat) : -1] + return map(split(glob(substitute(pat, '{}', '*', 'g')), "\n"), 'v:val.post') + else + return [a:pattern] + endif +endfunction + +" \ on Windows unless shellslash is set, / everywhere else. +function! pathogen#slash() abort + return !exists("+shellslash") || &shellslash ? '/' : '\' +endfunction + +function! pathogen#separator() abort + return pathogen#slash() +endfunction + +" Convenience wrapper around glob() which returns a list. +function! pathogen#glob(pattern) abort + let files = split(glob(a:pattern),"\n") + return map(files,'substitute(v:val,"[".pathogen#slash()."/]$","","")') +endfunction "}}}1 + +" Like pathogen#glob(), only limit the results to directories. +function! pathogen#glob_directories(pattern) abort + return filter(pathogen#glob(a:pattern),'isdirectory(v:val)') +endfunction "}}}1 + +" Remove duplicates from a list. +function! pathogen#uniq(list) abort + let i = 0 + let seen = {} + while i < len(a:list) + if (a:list[i] ==# '' && exists('empty')) || has_key(seen,a:list[i]) + call remove(a:list,i) + elseif a:list[i] ==# '' + let i += 1 + let empty = 1 + else + let seen[a:list[i]] = 1 + let i += 1 + endif + endwhile + return a:list +endfunction + +" Backport of fnameescape(). +function! pathogen#fnameescape(string) abort + if exists('*fnameescape') + return fnameescape(a:string) + elseif a:string ==# '-' + return '\-' + else + return substitute(escape(a:string," \t\n*?[{`$\\%#'\"|!<"),'^[+>]','\\&','') + endif +endfunction + +" Like findfile(), but hardcoded to use the runtimepath. +function! pathogen#runtime_findfile(file,count) abort "{{{1 + let rtp = pathogen#join(1,pathogen#split(&rtp)) + let file = findfile(a:file,rtp,a:count) + if file ==# '' + return '' + else + return fnamemodify(file,':p') + endif +endfunction + +" Section: Deprecated + +function! s:warn(msg) abort + echohl WarningMsg + echomsg a:msg + echohl NONE +endfunction + +" Prepend all subdirectories of path to the rtp, and append all 'after' +" directories in those subdirectories. Deprecated. +function! pathogen#runtime_prepend_subdirectories(path) abort + call s:warn('Change pathogen#runtime_prepend_subdirectories('.string(a:path).') to pathogen#infect('.string(a:path.'/{}').')') + return pathogen#surround(a:path . pathogen#slash() . '{}') +endfunction + +function! pathogen#incubate(...) abort + let name = a:0 ? a:1 : 'bundle/{}' + call s:warn('Change pathogen#incubate('.(a:0 ? string(a:1) : '').') to pathogen#infect('.string(name).')') + return pathogen#interpose(name) +endfunction + +" Deprecated alias for pathogen#interpose(). +function! pathogen#runtime_append_all_bundles(...) abort + if a:0 + call s:warn('Change pathogen#runtime_append_all_bundles('.string(a:1).') to pathogen#infect('.string(a:1.'/{}').')') + else + call s:warn('Change pathogen#runtime_append_all_bundles() to pathogen#infect()') + endif + return pathogen#interpose(a:0 ? a:1 . '/{}' : 'bundle/{}') +endfunction + +if exists(':Vedit') + finish +endif + +let s:vopen_warning = 0 + +function! s:find(count,cmd,file,lcd) + let rtp = pathogen#join(1,pathogen#split(&runtimepath)) + let file = pathogen#runtime_findfile(a:file,a:count) + if file ==# '' + return "echoerr 'E345: Can''t find file \"".a:file."\" in runtimepath'" + endif + if !s:vopen_warning + let s:vopen_warning = 1 + let warning = '|echohl WarningMsg|echo "Install scriptease.vim to continue using :V'.a:cmd.'"|echohl NONE' + else + let warning = '' + endif + if a:lcd + let path = file[0:-strlen(a:file)-2] + execute 'lcd `=path`' + return a:cmd.' '.pathogen#fnameescape(a:file) . warning + else + return a:cmd.' '.pathogen#fnameescape(file) . warning + endif +endfunction + +function! s:Findcomplete(A,L,P) + let sep = pathogen#slash() + let cheats = { + \'a': 'autoload', + \'d': 'doc', + \'f': 'ftplugin', + \'i': 'indent', + \'p': 'plugin', + \'s': 'syntax'} + if a:A =~# '^\w[\\/]' && has_key(cheats,a:A[0]) + let request = cheats[a:A[0]].a:A[1:-1] + else + let request = a:A + endif + let pattern = substitute(request,'/\|\'.sep,'*'.sep,'g').'*' + let found = {} + for path in pathogen#split(&runtimepath) + let path = expand(path, ':p') + let matches = split(glob(path.sep.pattern),"\n") + call map(matches,'isdirectory(v:val) ? v:val.sep : v:val') + call map(matches,'expand(v:val, ":p")[strlen(path)+1:-1]') + for match in matches + let found[match] = 1 + endfor + endfor + return sort(keys(found)) +endfunction + +command! -bar -bang -range=1 -nargs=1 -complete=customlist,s:Findcomplete Ve :execute s:find(<count>,'edit<bang>',<q-args>,0) +command! -bar -bang -range=1 -nargs=1 -complete=customlist,s:Findcomplete Vedit :execute s:find(<count>,'edit<bang>',<q-args>,0) +command! -bar -bang -range=1 -nargs=1 -complete=customlist,s:Findcomplete Vopen :execute s:find(<count>,'edit<bang>',<q-args>,1) +command! -bar -bang -range=1 -nargs=1 -complete=customlist,s:Findcomplete Vsplit :execute s:find(<count>,'split',<q-args>,<bang>1) +command! -bar -bang -range=1 -nargs=1 -complete=customlist,s:Findcomplete Vvsplit :execute s:find(<count>,'vsplit',<q-args>,<bang>1) +command! -bar -bang -range=1 -nargs=1 -complete=customlist,s:Findcomplete Vtabedit :execute s:find(<count>,'tabedit',<q-args>,<bang>1) +command! -bar -bang -range=1 -nargs=1 -complete=customlist,s:Findcomplete Vpedit :execute s:find(<count>,'pedit',<q-args>,<bang>1) +command! -bar -bang -range=1 -nargs=1 -complete=customlist,s:Findcomplete Vread :execute s:find(<count>,'read',<q-args>,<bang>1) + +" vim:set et sw=2 foldmethod=expr foldexpr=getline(v\:lnum)=~'^\"\ Section\:'?'>1'\:getline(v\:lnum)=~#'^fu'?'a1'\:getline(v\:lnum)=~#'^endf'?'s1'\:'=': diff --git a/vim/.vim/plugin/emacs-bindings.vim b/vim/.vim/plugin/emacs-bindings.vim @@ -0,0 +1,56 @@ +" Basic cursor movement and deletion keybindings from emacs, for vim. + +" insert mode +imap <C-g> <ESC> +imap <C-b> <Left> +imap <C-f> <Right> +imap <C-p> <Up> +imap <C-n> <Down> +imap <C-a> <C-o>:call <SID>home()<CR> +imap <C-e> <End> +imap <C-d> <Del> +imap <C-h> <BS> +imap <C-k> <C-r>=<SID>kill_line()<CR> + +" command line mode +cmap <C-p> <Up> +cmap <C-n> <Down> +cmap <C-b> <Left> +cmap <C-f> <Right> +cmap <C-a> <Home> +cmap <C-e> <End> +cnoremap <C-d> <Del> +cnoremap <C-h> <BS> +cnoremap <C-k> <C-f>D<C-c><C-c>:<Up> + +" command-T window +let g:CommandTCursorLeftMap = ['<Left>', '<C-b>'] +let g:CommandTCursorRightMap = ['<Right>', '<C-f>'] +let g:CommandTBackspaceMap = ['<BS>', '<C-h>'] +let g:CommandTDeleteMap = ['<Del>', '<C-d>'] + +function! s:home() + let start_col = col('.') + normal! ^ + if col('.') == start_col + normal! 0 + endif + return '' +endfunction + +function! s:kill_line() + let [text_before_cursor, text_after_cursor] = s:split_line_text_at_cursor() + if len(text_after_cursor) == 0 + normal! J + else + call setline(line('.'), text_before_cursor) + endif + return '' +endfunction + +function! s:split_line_text_at_cursor() + let line_text = getline(line('.')) + let text_after_cursor = line_text[col('.')-1 :] + let text_before_cursor = (col('.') > 1) ? line_text[: col('.')-2] : '' + return [text_before_cursor, text_after_cursor] +endfunction diff --git a/vim/.vim/plugin/multiple_cursors.vim b/vim/.vim/plugin/multiple_cursors.vim @@ -0,0 +1,66 @@ +"=============================================================================== +" File: multiple_cursors.vim +" Author: Terry Ma +" Description: Emulate Sublime Text's multi selection feature +" Potential Features: +" - Create a blinking cursor effect? Good place to do it would be instead of +" waiting for user input, cycle through the highlight +" - Integrate with the status line? Maybe show a special multicursor mode? +" - Support mouse? Ctrl/Cmd click to set cursor? +"=============================================================================== +let s:save_cpo = &cpo +set cpo&vim + +function! s:init_settings(settings) + for [key, value] in items(a:settings) + let sub = '' + if type(value) == 0 + let sub = '%d' + elseif type(value) == 1 + let sub = '"%s"' + endif + let fmt = printf("let g:multi_cursor_%%s=get(g:, 'multi_cursor_%%s', %s)", + \ sub) + exec printf(fmt, key, key, value) + endfor +endfunction + +" Settings +let s:settings = { + \ 'exit_from_visual_mode': 1, + \ 'exit_from_insert_mode': 1, + \ 'use_default_mapping': 1, + \ 'debug_latency': 0, + \ } + +let s:settings_if_default = { + \ 'quit_key': '<Esc>', + \ 'next_key': '<C-n>', + \ 'prev_key': '<C-p>', + \ 'skip_key': '<C-x>', + \ } + +call s:init_settings(s:settings) + +if g:multi_cursor_use_default_mapping + call s:init_settings(s:settings_if_default) +endif + +if !exists('g:multi_cursor_start_key') && exists('g:multi_cursor_next_key') + let g:multi_cursor_start_key = g:multi_cursor_next_key +endif + +" External mappings +if exists('g:multi_cursor_start_key') + exec 'nnoremap <silent> '.g:multi_cursor_start_key. + \' :call multiple_cursors#new("n")<CR>' + exec 'xnoremap <silent> '.g:multi_cursor_start_key. + \' :<C-u>call multiple_cursors#new("v")<CR>' +endif + +" Commands +command! -nargs=1 -range=% MultipleCursorsFind + \ call multiple_cursors#find(<line1>, <line2>, <q-args>) + +let &cpo = s:save_cpo +unlet s:save_cpo diff --git a/vim/.vimrc b/vim/.vimrc @@ -0,0 +1,21 @@ +set tabstop=4 + +set shiftwidth=4 + +set smarttab + +set showcmd + +set ruler + +set autoindent + +imap <M-Space> <Esc> + +" Default mapping +let g:multi_cursor_use_default_mapping=0 +let g:multi_cursor_start_key='<C-l>' +let g:multi_cursor_next_key='<C-l>' +let g:multi_cursor_prev_key='<C-p>' +let g:multi_cursor_skip_key='<C-x>' +let g:multi_cursor_quit_key='<Esc>'