aboutsummaryrefslogtreecommitdiff
path: root/dot_vim/plugin/imaps.vim
diff options
context:
space:
mode:
Diffstat (limited to 'dot_vim/plugin/imaps.vim')
-rw-r--r--dot_vim/plugin/imaps.vim831
1 files changed, 831 insertions, 0 deletions
diff --git a/dot_vim/plugin/imaps.vim b/dot_vim/plugin/imaps.vim
new file mode 100644
index 0000000..d871aa1
--- /dev/null
+++ b/dot_vim/plugin/imaps.vim
@@ -0,0 +1,831 @@
+" File: imaps.vim
+" Authors: Srinath Avadhanula <srinath AT fastmail.fm>
+" Benji Fisher <benji AT member.AMS.org>
+"
+" WWW: http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/vim-latex/vimfiles/plugin/imaps.vim?only_with_tag=MAIN
+"
+" Description: insert mode template expander with cursor placement
+" while preserving filetype indentation.
+"
+" $Id: imaps.vim 997 2006-03-20 09:45:45Z srinathava $
+"
+" Documentation: {{{
+"
+" Motivation:
+" this script provides a way to generate insert mode mappings which do not
+" suffer from some of the problem of mappings and abbreviations while allowing
+" cursor placement after the expansion. It can alternatively be thought of as
+" a template expander.
+"
+" Consider an example. If you do
+"
+" imap lhs something
+"
+" then a mapping is set up. However, there will be the following problems:
+" 1. the 'ttimeout' option will generally limit how easily you can type the
+" lhs. if you type the left hand side too slowly, then the mapping will not
+" be activated.
+" 2. if you mistype one of the letters of the lhs, then the mapping is
+" deactivated as soon as you backspace to correct the mistake.
+"
+" If, in order to take care of the above problems, you do instead
+"
+" iab lhs something
+"
+" then the timeout problem is solved and so is the problem of mistyping.
+" however, abbreviations are only expanded after typing a non-word character.
+" which causes problems of cursor placement after the expansion and invariably
+" spurious spaces are inserted.
+"
+" Usage Example:
+" this script attempts to solve all these problems by providing an emulation
+" of imaps wchich does not suffer from its attendant problems. Because maps
+" are activated without having to press additional characters, therefore
+" cursor placement is possible. furthermore, file-type specific indentation is
+" preserved, because the rhs is expanded as if the rhs is typed in literally
+" by the user.
+"
+" The script already provides some default mappings. each "mapping" is of the
+" form:
+"
+" call IMAP (lhs, rhs, ft)
+"
+" Some characters in the RHS have special meaning which help in cursor
+" placement.
+"
+" Example One:
+"
+" call IMAP ("bit`", "\\begin{itemize}\<cr>\\item <++>\<cr>\\end{itemize}<++>", "tex")
+"
+" This effectively sets up the map for "bit`" whenever you edit a latex file.
+" When you type in this sequence of letters, the following text is inserted:
+"
+" \begin{itemize}
+" \item *
+" \end{itemize}<++>
+"
+" where * shows the cursor position. The cursor position after inserting the
+" text is decided by the position of the first "place-holder". Place holders
+" are special characters which decide cursor placement and movement. In the
+" example above, the place holder characters are <+ and +>. After you have typed
+" in the item, press <C-j> and you will be taken to the next set of <++>'s.
+" Therefore by placing the <++> characters appropriately, you can minimize the
+" use of movement keys.
+"
+" NOTE: Set g:Imap_UsePlaceHolders to 0 to disable placeholders altogether.
+" Set
+" g:Imap_PlaceHolderStart and g:Imap_PlaceHolderEnd
+" to something else if you want different place holder characters.
+" Also, b:Imap_PlaceHolderStart and b:Imap_PlaceHolderEnd override the values
+" of g:Imap_PlaceHolderStart and g:Imap_PlaceHolderEnd respectively. This is
+" useful for setting buffer specific place hoders.
+"
+" Example Two:
+" You can use the <C-r> command to insert dynamic elements such as dates.
+" call IMAP ('date`', "\<c-r>=strftime('%b %d %Y')\<cr>", '')
+"
+" sets up the map for date` to insert the current date.
+"
+"--------------------------------------%<--------------------------------------
+" Bonus: This script also provides a command Snip which puts tearoff strings,
+" '----%<----' above and below the visually selected range of lines. The
+" length of the string is chosen to be equal to the longest line in the range.
+" Recommended Usage:
+" '<,'>Snip
+"--------------------------------------%<--------------------------------------
+" }}}
+
+" line continuation used here.
+let s:save_cpo = &cpo
+set cpo&vim
+
+" ==============================================================================
+" Script Options / Variables
+" ==============================================================================
+" Options {{{
+if !exists('g:Imap_StickyPlaceHolders')
+ let g:Imap_StickyPlaceHolders = 1
+endif
+if !exists('g:Imap_DeleteEmptyPlaceHolders')
+ let g:Imap_DeleteEmptyPlaceHolders = 1
+endif
+" }}}
+" Variables {{{
+" s:LHS_{ft}_{char} will be generated automatically. It will look like
+" s:LHS_tex_o = 'fo\|foo\|boo' and contain all mapped sequences ending in "o".
+" s:Map_{ft}_{lhs} will be generated automatically. It will look like
+" s:Map_c_foo = 'for(<++>; <++>; <++>)', the mapping for "foo".
+"
+" }}}
+
+" ==============================================================================
+" functions for easy insert mode mappings.
+" ==============================================================================
+" IMAP: Adds a "fake" insert mode mapping. {{{
+" For example, doing
+" IMAP('abc', 'def' ft)
+" will mean that if the letters abc are pressed in insert mode, then
+" they will be replaced by def. If ft != '', then the "mapping" will be
+" specific to the files of type ft.
+"
+" Using IMAP has a few advantages over simply doing:
+" imap abc def
+" 1. with imap, if you begin typing abc, the cursor will not advance and
+" long as there is a possible completion, the letters a, b, c will be
+" displayed on on top of the other. using this function avoids that.
+" 2. with imap, if a backspace or arrow key is pressed before completing
+" the word, then the mapping is lost. this function allows movement.
+" (this ofcourse means that this function is only limited to
+" left-hand-sides which do not have movement keys or unprintable
+" characters)
+" It works by only mapping the last character of the left-hand side.
+" when this character is typed in, then a reverse lookup is done and if
+" the previous characters consititute the left hand side of the mapping,
+" the previously typed characters and erased and the right hand side is
+" inserted
+
+" IMAP: set up a filetype specific mapping.
+" Description:
+" "maps" the lhs to rhs in files of type 'ft'. If supplied with 2
+" additional arguments, then those are assumed to be the placeholder
+" characters in rhs. If unspecified, then the placeholder characters
+" are assumed to be '<+' and '+>' These placeholder characters in
+" a:rhs are replaced with the users setting of
+" [bg]:Imap_PlaceHolderStart and [bg]:Imap_PlaceHolderEnd settings.
+"
+function! IMAP(lhs, rhs, ft, ...)
+
+ " Find the place holders to save for IMAP_PutTextWithMovement() .
+ if a:0 < 2
+ let phs = '<+'
+ let phe = '+>'
+ else
+ let phs = a:1
+ let phe = a:2
+ endif
+
+ let hash = s:Hash(a:lhs)
+ let s:Map_{a:ft}_{hash} = a:rhs
+ let s:phs_{a:ft}_{hash} = phs
+ let s:phe_{a:ft}_{hash} = phe
+
+ " Add a:lhs to the list of left-hand sides that end with lastLHSChar:
+ let lastLHSChar = a:lhs[strlen(a:lhs)-1]
+ let hash = s:Hash(lastLHSChar)
+ if !exists("s:LHS_" . a:ft . "_" . hash)
+ let s:LHS_{a:ft}_{hash} = escape(a:lhs, '\')
+ else
+ let s:LHS_{a:ft}_{hash} = escape(a:lhs, '\') .'\|'. s:LHS_{a:ft}_{hash}
+ endif
+
+ " map only the last character of the left-hand side.
+ if lastLHSChar == ' '
+ let lastLHSChar = '<space>'
+ end
+ exe 'inoremap <silent>'
+ \ escape(lastLHSChar, '|')
+ \ '<C-r>=<SID>LookupCharacter("' .
+ \ escape(lastLHSChar, '\|"') .
+ \ '")<CR>'
+endfunction
+
+" }}}
+" IMAP_list: list the rhs and place holders corresponding to a:lhs {{{
+"
+" Added mainly for debugging purposes, but maybe worth keeping.
+function! IMAP_list(lhs)
+ let char = a:lhs[strlen(a:lhs)-1]
+ let charHash = s:Hash(char)
+ if exists("s:LHS_" . &ft ."_". charHash) && a:lhs =~ s:LHS_{&ft}_{charHash}
+ let ft = &ft
+ elseif exists("s:LHS__" . charHash) && a:lhs =~ s:LHS__{charHash}
+ let ft = ""
+ else
+ return ""
+ endif
+ let hash = s:Hash(a:lhs)
+ return "rhs = " . s:Map_{ft}_{hash} . " place holders = " .
+ \ s:phs_{ft}_{hash} . " and " . s:phe_{ft}_{hash}
+endfunction
+" }}}
+" LookupCharacter: inserts mapping corresponding to this character {{{
+"
+" This function extracts from s:LHS_{&ft}_{a:char} or s:LHS__{a:char}
+" the longest lhs matching the current text. Then it replaces lhs with the
+" corresponding rhs saved in s:Map_{ft}_{lhs} .
+" The place-holder variables are passed to IMAP_PutTextWithMovement() .
+function! s:LookupCharacter(char)
+ if IMAP_GetVal('Imap_FreezeImap', 0) == 1
+ return a:char
+ endif
+ let charHash = s:Hash(a:char)
+
+ " The line so far, including the character that triggered this function:
+ let text = strpart(getline("."), 0, col(".")-1) . a:char
+ " Prefer a local map to a global one, even if the local map is shorter.
+ " Is this what we want? Do we care?
+ " Use '\V' (very no-magic) so that only '\' is special, and it was already
+ " escaped when building up s:LHS_{&ft}_{charHash} .
+ if exists("s:LHS_" . &ft . "_" . charHash)
+ \ && text =~ "\\C\\V\\(" . s:LHS_{&ft}_{charHash} . "\\)\\$"
+ let ft = &ft
+ elseif exists("s:LHS__" . charHash)
+ \ && text =~ "\\C\\V\\(" . s:LHS__{charHash} . "\\)\\$"
+ let ft = ""
+ else
+ " If this is a character which could have been used to trigger an
+ " abbreviation, check if an abbreviation exists.
+ if a:char !~ '\k'
+ let lastword = matchstr(getline('.'), '\k\+$', '')
+ call IMAP_Debug('getting lastword = ['.lastword.']', 'imap')
+ if lastword != ''
+ " An extremeley wierd way to get around the fact that vim
+ " doesn't have the equivalent of the :mapcheck() function for
+ " abbreviations.
+ let _a = @a
+ exec "redir @a | silent! iab ".lastword." | redir END"
+ let abbreviationRHS = matchstr(@a."\n", "\n".'i\s\+'.lastword.'\s\+@\?\zs.*\ze'."\n")
+
+ call IMAP_Debug('getting abbreviationRHS = ['.abbreviationRHS.']', 'imap')
+
+ if @a =~ "No abbreviation found" || abbreviationRHS == ""
+ let @a = _a
+ return a:char
+ endif
+
+ let @a = _a
+ let abbreviationRHS = escape(abbreviationRHS, '\<"')
+ exec 'let abbreviationRHS = "'.abbreviationRHS.'"'
+
+ let lhs = lastword.a:char
+ let rhs = abbreviationRHS.a:char
+ let phs = IMAP_GetPlaceHolderStart()
+ let phe = IMAP_GetPlaceHolderEnd()
+ else
+ return a:char
+ endif
+ else
+ return a:char
+ endif
+ endif
+ " Find the longest left-hand side that matches the line so far.
+ " matchstr() returns the match that starts first. This automatically
+ " ensures that the longest LHS is used for the mapping.
+ if !exists('lhs') || !exists('rhs')
+ let lhs = matchstr(text, "\\C\\V\\(" . s:LHS_{ft}_{charHash} . "\\)\\$")
+ let hash = s:Hash(lhs)
+ let rhs = s:Map_{ft}_{hash}
+ let phs = s:phs_{ft}_{hash}
+ let phe = s:phe_{ft}_{hash}
+ endif
+
+ if strlen(lhs) == 0
+ return a:char
+ endif
+ " enough back-spaces to erase the left-hand side; -1 for the last
+ " character typed:
+ let bs = substitute(strpart(lhs, 1), ".", "\<bs>", "g")
+ return bs . IMAP_PutTextWithMovement(rhs, phs, phe)
+endfunction
+
+" }}}
+" IMAP_PutTextWithMovement: returns the string with movement appended {{{
+" Description:
+" If a:str contains "placeholders", then appends movement commands to
+" str in a way that the user moves to the first placeholder and enters
+" insert or select mode. If supplied with 2 additional arguments, then
+" they are assumed to be the placeholder specs. Otherwise, they are
+" assumed to be '<+' and '+>'. These placeholder chars are replaced
+" with the users settings of [bg]:Imap_PlaceHolderStart and
+" [bg]:Imap_PlaceHolderEnd.
+function! IMAP_PutTextWithMovement(str, ...)
+
+ " The placeholders used in the particular input string. These can be
+ " different from what the user wants to use.
+ if a:0 < 2
+ let phs = '<+'
+ let phe = '+>'
+ else
+ let phs = escape(a:1, '\')
+ let phe = escape(a:2, '\')
+ endif
+
+ let text = a:str
+
+ " The user's placeholder settings.
+ let phsUser = IMAP_GetPlaceHolderStart()
+ let pheUser = IMAP_GetPlaceHolderEnd()
+
+ " Problem: depending on the setting of the 'encoding' option, a character
+ " such as "\xab" may not match itself. We try to get around this by
+ " changing the encoding of all our strings. At the end, we have to
+ " convert text back.
+ let phsEnc = s:Iconv(phs, "encode")
+ let pheEnc = s:Iconv(phe, "encode")
+ let phsUserEnc = s:Iconv(phsUser, "encode")
+ let pheUserEnc = s:Iconv(pheUser, "encode")
+ let textEnc = s:Iconv(text, "encode")
+ if textEnc != text
+ let textEncoded = 1
+ else
+ let textEncoded = 0
+ endif
+
+ let pattern = '\V\(\.\{-}\)' .phs. '\(\.\{-}\)' .phe. '\(\.\*\)'
+ " If there are no placeholders, just return the text.
+ if textEnc !~ pattern
+ call IMAP_Debug('Not getting '.phs.' and '.phe.' in '.textEnc, 'imap')
+ return text
+ endif
+ " Break text up into "initial <+template+> final"; any piece may be empty.
+ let initialEnc = substitute(textEnc, pattern, '\1', '')
+ let templateEnc = substitute(textEnc, pattern, '\2', '')
+ let finalEnc = substitute(textEnc, pattern, '\3', '')
+
+ " If the user does not want to use placeholders, then remove all but the
+ " first placeholder.
+ " Otherwise, replace all occurences of the placeholders here with the
+ " user's choice of placeholder settings.
+ if exists('g:Imap_UsePlaceHolders') && !g:Imap_UsePlaceHolders
+ let finalEnc = substitute(finalEnc, '\V'.phs.'\.\{-}'.phe, '', 'g')
+ else
+ let finalEnc = substitute(finalEnc, '\V'.phs.'\(\.\{-}\)'.phe,
+ \ phsUserEnc.'\1'.pheUserEnc, 'g')
+ endif
+
+ " The substitutions are done, so convert back, if necessary.
+ if textEncoded
+ let initial = s:Iconv(initialEnc, "decode")
+ let template = s:Iconv(templateEnc, "decode")
+ let final = s:Iconv(finalEnc, "decode")
+ else
+ let initial = initialEnc
+ let template = templateEnc
+ let final = finalEnc
+ endif
+
+ " Build up the text to insert:
+ " 1. the initial text plus an extra character;
+ " 2. go to Normal mode with <C-\><C-N>, so it works even if 'insertmode'
+ " is set, and mark the position;
+ " 3. replace the extra character with tamplate and final;
+ " 4. back to Normal mode and restore the cursor position;
+ " 5. call IMAP_Jumpfunc().
+ let template = phsUser . template . pheUser
+ " Old trick: insert and delete a character to get the same behavior at
+ " start, middle, or end of line and on empty lines.
+ let text = initial . "X\<C-\>\<C-N>:call IMAP_Mark('set')\<CR>\"_s"
+ let text = text . template . final
+ let text = text . "\<C-\>\<C-N>:call IMAP_Mark('go')\<CR>"
+ let text = text . "i\<C-r>=IMAP_Jumpfunc('', 1)\<CR>"
+
+ call IMAP_Debug('IMAP_PutTextWithMovement: text = ['.text.']', 'imap')
+ return text
+endfunction
+
+" }}}
+" IMAP_Jumpfunc: takes user to next <+place-holder+> {{{
+" Author: Luc Hermitte
+" Arguments:
+" direction: flag for the search() function. If set to '', search forwards,
+" if 'b', then search backwards. See the {flags} argument of the
+" |search()| function for valid values.
+" inclusive: In vim, the search() function is 'exclusive', i.e we always goto
+" next cursor match even if there is a match starting from the
+" current cursor position. Setting this argument to 1 makes
+" IMAP_Jumpfunc() also respect a match at the current cursor
+" position. 'inclusive'ness is necessary for IMAP() because a
+" placeholder string can occur at the very beginning of a map which
+" we want to select.
+" We use a non-zero value only in special conditions. Most mappings
+" should use a zero value.
+function! IMAP_Jumpfunc(direction, inclusive)
+
+ " The user's placeholder settings.
+ let phsUser = IMAP_GetPlaceHolderStart()
+ let pheUser = IMAP_GetPlaceHolderEnd()
+
+ let searchString = ''
+ " If this is not an inclusive search or if it is inclusive, but the
+ " current cursor position does not contain a placeholder character, then
+ " search for the placeholder characters.
+ if !a:inclusive || strpart(getline('.'), col('.')-1) !~ '\V\^'.phsUser
+ let searchString = '\V'.phsUser.'\_.\{-}'.pheUser
+ endif
+
+ " If we didn't find any placeholders return quietly.
+ if searchString != '' && !search(searchString, a:direction)
+ return ''
+ endif
+
+ " Open any closed folds and make this part of the text visible.
+ silent! foldopen!
+
+ " Calculate if we have an empty placeholder or if it contains some
+ " description.
+ let template =
+ \ matchstr(strpart(getline('.'), col('.')-1),
+ \ '\V\^'.phsUser.'\zs\.\{-}\ze\('.pheUser.'\|\$\)')
+ let placeHolderEmpty = !strlen(template)
+
+ " If we are selecting in exclusive mode, then we need to move one step to
+ " the right
+ let extramove = ''
+ if &selection == 'exclusive'
+ let extramove = 'l'
+ endif
+
+ " Select till the end placeholder character.
+ let movement = "\<C-o>v/\\V".pheUser."/e\<CR>".extramove
+
+ " First remember what the search pattern was. s:RemoveLastHistoryItem will
+ " reset @/ to this pattern so we do not create new highlighting.
+ let g:Tex_LastSearchPattern = @/
+
+ " Now either goto insert mode or select mode.
+ if placeHolderEmpty && g:Imap_DeleteEmptyPlaceHolders
+ " delete the empty placeholder into the blackhole.
+ return movement."\"_c\<C-o>:".s:RemoveLastHistoryItem."\<CR>"
+ else
+ return movement."\<C-\>\<C-N>:".s:RemoveLastHistoryItem."\<CR>gv\<C-g>"
+ endif
+
+endfunction
+
+" }}}
+" Maps for IMAP_Jumpfunc {{{
+"
+" These mappings use <Plug> and thus provide for easy user customization. When
+" the user wants to map some other key to jump forward, he can do for
+" instance:
+" nmap ,f <plug>IMAP_JumpForward
+" etc.
+
+" jumping forward and back in insert mode.
+imap <silent> <Plug>IMAP_JumpForward <c-r>=IMAP_Jumpfunc('', 0)<CR>
+imap <silent> <Plug>IMAP_JumpBack <c-r>=IMAP_Jumpfunc('b', 0)<CR>
+
+" jumping in normal mode
+nmap <silent> <Plug>IMAP_JumpForward i<c-r>=IMAP_Jumpfunc('', 0)<CR>
+nmap <silent> <Plug>IMAP_JumpBack i<c-r>=IMAP_Jumpfunc('b', 0)<CR>
+
+" deleting the present selection and then jumping forward.
+vmap <silent> <Plug>IMAP_DeleteAndJumpForward "_<Del>i<c-r>=IMAP_Jumpfunc('', 0)<CR>
+vmap <silent> <Plug>IMAP_DeleteAndJumpBack "_<Del>i<c-r>=IMAP_Jumpfunc('b', 0)<CR>
+
+" jumping forward without deleting present selection.
+vmap <silent> <Plug>IMAP_JumpForward <C-\><C-N>i<c-r>=IMAP_Jumpfunc('', 0)<CR>
+vmap <silent> <Plug>IMAP_JumpBack <C-\><C-N>`<i<c-r>=IMAP_Jumpfunc('b', 0)<CR>
+
+" }}}
+" Default maps for IMAP_Jumpfunc {{{
+" map only if there is no mapping already. allows for user customization.
+" NOTE: Default mappings for jumping to the previous placeholder are not
+" provided. It is assumed that if the user will create such mappings
+" hself if e so desires.
+if !hasmapto('<Plug>IMAP_JumpForward', 'i')
+ imap <C-J> <Plug>IMAP_JumpForward
+endif
+if !hasmapto('<Plug>IMAP_JumpForward', 'n')
+ nmap <C-J> <Plug>IMAP_JumpForward
+endif
+if exists('g:Imap_StickyPlaceHolders') && g:Imap_StickyPlaceHolders
+ if !hasmapto('<Plug>IMAP_JumpForward', 'v')
+ vmap <C-J> <Plug>IMAP_JumpForward
+ endif
+else
+ if !hasmapto('<Plug>IMAP_DeleteAndJumpForward', 'v')
+ vmap <C-J> <Plug>IMAP_DeleteAndJumpForward
+ endif
+endif
+" }}}
+
+nmap <silent> <script> <plug><+SelectRegion+> `<v`>
+
+" ==============================================================================
+" enclosing selected region.
+" ==============================================================================
+" VEnclose: encloses the visually selected region with given arguments {{{
+" Description: allows for differing action based on visual line wise
+" selection or visual characterwise selection. preserves the
+" marks and search history.
+function! VEnclose(vstart, vend, VStart, VEnd)
+
+ " its characterwise if
+ " 1. characterwise selection and valid values for vstart and vend.
+ " OR
+ " 2. linewise selection and invalid values for VStart and VEnd
+ if (visualmode() == 'v' && (a:vstart != '' || a:vend != '')) || (a:VStart == '' && a:VEnd == '')
+
+ let newline = ""
+ let _r = @r
+
+ let normcmd = "normal! \<C-\>\<C-n>`<v`>\"_s"
+
+ exe "normal! \<C-\>\<C-n>`<v`>\"ry"
+ if @r =~ "\n$"
+ let newline = "\n"
+ let @r = substitute(@r, "\n$", '', '')
+ endif
+
+ " In exclusive selection, we need to select an extra character.
+ if &selection == 'exclusive'
+ let movement = 8
+ else
+ let movement = 7
+ endif
+ let normcmd = normcmd.
+ \ a:vstart."!!mark!!".a:vend.newline.
+ \ "\<C-\>\<C-N>?!!mark!!\<CR>v".movement."l\"_s\<C-r>r\<C-\>\<C-n>"
+
+ " this little if statement is because till very recently, vim used to
+ " report col("'>") > length of selected line when `> is $. on some
+ " systems it reports a -ve number.
+ if col("'>") < 0 || col("'>") > strlen(getline("'>"))
+ let lastcol = strlen(getline("'>"))
+ else
+ let lastcol = col("'>")
+ endif
+ if lastcol - col("'<") != 0
+ let len = lastcol - col("'<")
+ else
+ let len = ''
+ endif
+
+ " the next normal! is for restoring the marks.
+ let normcmd = normcmd."`<v".len."l\<C-\>\<C-N>"
+
+ " First remember what the search pattern was. s:RemoveLastHistoryItem
+ " will reset @/ to this pattern so we do not create new highlighting.
+ let g:Tex_LastSearchPattern = @/
+
+ silent! exe normcmd
+ " this is to restore the r register.
+ let @r = _r
+ " and finally, this is to restore the search history.
+ execute s:RemoveLastHistoryItem
+
+ else
+
+ exec 'normal! `<O'.a:VStart."\<C-\>\<C-n>"
+ exec 'normal! `>o'.a:VEnd."\<C-\>\<C-n>"
+ if &indentexpr != ''
+ silent! normal! `<kV`>j=
+ endif
+ silent! normal! `>
+ endif
+endfunction
+
+" }}}
+" ExecMap: adds the ability to correct an normal/visual mode mapping. {{{
+" Author: Hari Krishna Dara <hari_vim@yahoo.com>
+" Reads a normal mode mapping at the command line and executes it with the
+" given prefix. Press <BS> to correct and <Esc> to cancel.
+function! ExecMap(prefix, mode)
+ " Temporarily remove the mapping, otherwise it will interfere with the
+ " mapcheck call below:
+ let myMap = maparg(a:prefix, a:mode)
+ exec a:mode."unmap ".a:prefix
+
+ " Generate a line with spaces to clear the previous message.
+ let i = 1
+ let clearLine = "\r"
+ while i < &columns
+ let clearLine = clearLine . ' '
+ let i = i + 1
+ endwhile
+
+ let mapCmd = a:prefix
+ let foundMap = 0
+ let breakLoop = 0
+ echon "\rEnter Map: " . mapCmd
+ while !breakLoop
+ let char = getchar()
+ if char !~ '^\d\+$'
+ if char == "\<BS>"
+ let mapCmd = strpart(mapCmd, 0, strlen(mapCmd) - 1)
+ endif
+ else " It is the ascii code.
+ let char = nr2char(char)
+ if char == "\<Esc>"
+ let breakLoop = 1
+ else
+ let mapCmd = mapCmd . char
+ if maparg(mapCmd, a:mode) != ""
+ let foundMap = 1
+ let breakLoop = 1
+ elseif mapcheck(mapCmd, a:mode) == ""
+ let mapCmd = strpart(mapCmd, 0, strlen(mapCmd) - 1)
+ endif
+ endif
+ endif
+ echon clearLine
+ echon "\rEnter Map: " . mapCmd
+ endwhile
+ if foundMap
+ if a:mode == 'v'
+ " use a plug to select the region instead of using something like
+ " `<v`> to avoid problems caused by some of the characters in
+ " '`<v`>' being mapped.
+ let gotoc = "\<plug><+SelectRegion+>"
+ else
+ let gotoc = ''
+ endif
+ exec "normal ".gotoc.mapCmd
+ endif
+ exec a:mode.'noremap '.a:prefix.' '.myMap
+endfunction
+
+" }}}
+
+" ==============================================================================
+" helper functions
+" ==============================================================================
+" Strntok: extract the n^th token from a list {{{
+" example: Strntok('1,23,3', ',', 2) = 23
+fun! <SID>Strntok(s, tok, n)
+ return matchstr( a:s.a:tok[0], '\v(\zs([^'.a:tok.']*)\ze['.a:tok.']){'.a:n.'}')
+endfun
+
+" }}}
+" s:RemoveLastHistoryItem: removes last search item from search history {{{
+" Description: Execute this string to clean up the search history.
+let s:RemoveLastHistoryItem = ':call histdel("/", -1)|let @/=g:Tex_LastSearchPattern'
+
+" }}}
+" s:Hash: Return a version of a string that can be used as part of a variable" {{{
+" name.
+" Converts every non alphanumeric character into _{ascii}_ where {ascii} is
+" the ASCII code for that character...
+fun! s:Hash(text)
+ return substitute(a:text, '\([^[:alnum:]]\)',
+ \ '\="_".char2nr(submatch(1))."_"', 'g')
+endfun
+"" }}}
+" IMAP_GetPlaceHolderStart and IMAP_GetPlaceHolderEnd: "{{{
+" return the buffer local placeholder variables, or the global one, or the default.
+function! IMAP_GetPlaceHolderStart()
+ if exists("b:Imap_PlaceHolderStart") && strlen(b:Imap_PlaceHolderEnd)
+ return b:Imap_PlaceHolderStart
+ elseif exists("g:Imap_PlaceHolderStart") && strlen(g:Imap_PlaceHolderEnd)
+ return g:Imap_PlaceHolderStart
+ else
+ return "<+"
+endfun
+function! IMAP_GetPlaceHolderEnd()
+ if exists("b:Imap_PlaceHolderEnd") && strlen(b:Imap_PlaceHolderEnd)
+ return b:Imap_PlaceHolderEnd
+ elseif exists("g:Imap_PlaceHolderEnd") && strlen(g:Imap_PlaceHolderEnd)
+ return g:Imap_PlaceHolderEnd
+ else
+ return "+>"
+endfun
+" }}}
+" s:Iconv: a wrapper for iconv()" {{{
+" Problem: after
+" let text = "\xab"
+" (or using the raw 8-bit ASCII character in a file with 'fenc' set to
+" "latin1") if 'encoding' is set to utf-8, then text does not match itself:
+" echo text =~ text
+" returns 0.
+" Solution: When this happens, a re-encoded version of text does match text:
+" echo iconv(text, "latin1", "utf8") =~ text
+" returns 1. In this case, convert text to utf-8 with iconv().
+" TODO: Is it better to use &encoding instead of "utf8"? Internally, vim
+" uses utf-8, and can convert between latin1 and utf-8 even when compiled with
+" -iconv, so let's try using utf-8.
+" Arguments:
+" a:text = text to be encoded or decoded
+" a:mode = "encode" (latin1 to utf8) or "decode" (utf8 to latin1)
+" Caution: do not encode and then decode without checking whether the text
+" has changed, becuase of the :if clause in encoding!
+function! s:Iconv(text, mode)
+ if a:mode == "decode"
+ return iconv(a:text, "utf8", "latin1")
+ endif
+ if a:text =~ '\V\^' . escape(a:text, '\') . '\$'
+ return a:text
+ endif
+ let textEnc = iconv(a:text, "latin1", "utf8")
+ if textEnc !~ '\V\^' . escape(a:text, '\') . '\$'
+ call IMAP_Debug('Encoding problems with text '.a:text.' ', 'imap')
+ endif
+ return textEnc
+endfun
+"" }}}
+" IMAP_Debug: interface to Tex_Debug if available, otherwise emulate it {{{
+" Description:
+" Do not want a memory leak! Set this to zero so that imaps always
+" starts out in a non-debugging mode.
+if !exists('g:Imap_Debug')
+ let g:Imap_Debug = 0
+endif
+function! IMAP_Debug(string, pattern)
+ if !g:Imap_Debug
+ return
+ endif
+ if exists('*Tex_Debug')
+ call Tex_Debug(a:string, a:pattern)
+ else
+ if !exists('s:debug_'.a:pattern)
+ let s:debug_{a:pattern} = a:string
+ else
+ let s:debug_{a:pattern} = s:debug_{a:pattern}.a:string
+ endif
+ endif
+endfunction " }}}
+" IMAP_DebugClear: interface to Tex_DebugClear if avaialable, otherwise emulate it {{{
+" Description:
+function! IMAP_DebugClear(pattern)
+ if exists('*Tex_DebugClear')
+ call Tex_DebugClear(a:pattern)
+ else
+ let s:debug_{a:pattern} = ''
+ endif
+endfunction " }}}
+" IMAP_PrintDebug: interface to Tex_DebugPrint if avaialable, otherwise emulate it {{{
+" Description:
+function! IMAP_PrintDebug(pattern)
+ if exists('*Tex_PrintDebug')
+ call Tex_PrintDebug(a:pattern)
+ else
+ if exists('s:debug_'.a:pattern)
+ echo s:debug_{a:pattern}
+ endif
+ endif
+endfunction " }}}
+" IMAP_Mark: Save the cursor position (if a:action == 'set') in a" {{{
+" script-local variable; restore this position if a:action == 'go'.
+let s:Mark = "(0,0)"
+let s:initBlanks = ''
+function! IMAP_Mark(action)
+ if a:action == 'set'
+ let s:Mark = "(" . line(".") . "," . col(".") . ")"
+ let s:initBlanks = matchstr(getline('.'), '^\s*')
+ elseif a:action == 'go'
+ execute "call cursor" s:Mark
+ let blanksNow = matchstr(getline('.'), '^\s*')
+ if strlen(blanksNow) > strlen(s:initBlanks)
+ execute 'silent! normal! '.(strlen(blanksNow) - strlen(s:initBlanks)).'l'
+ elseif strlen(blanksNow) < strlen(s:initBlanks)
+ execute 'silent! normal! '.(strlen(s:initBlanks) - strlen(blanksNow)).'h'
+ endif
+ endif
+endfunction "" }}}
+" IMAP_GetVal: gets the value of a variable {{{
+" Description: first checks window local, then buffer local etc.
+function! IMAP_GetVal(name, ...)
+ if a:0 > 0
+ let default = a:1
+ else
+ let default = ''
+ endif
+ if exists('w:'.a:name)
+ return w:{a:name}
+ elseif exists('b:'.a:name)
+ return b:{a:name}
+ elseif exists('g:'.a:name)
+ return g:{a:name}
+ else
+ return default
+ endif
+endfunction " }}}
+
+" ==============================================================================
+" A bonus function: Snip()
+" ==============================================================================
+" Snip: puts a scissor string above and below block of text {{{
+" Desciption:
+"-------------------------------------%<-------------------------------------
+" this puts a the string "--------%<---------" above and below the visually
+" selected block of lines. the length of the 'tearoff' string depends on the
+" maximum string length in the selected range. this is an aesthetically more
+" pleasing alternative instead of hardcoding a length.
+"-------------------------------------%<-------------------------------------
+function! <SID>Snip() range
+ let i = a:firstline
+ let maxlen = -2
+ " find out the maximum virtual length of each line.
+ while i <= a:lastline
+ exe i
+ let length = virtcol('$')
+ let maxlen = (length > maxlen ? length : maxlen)
+ let i = i + 1
+ endwhile
+ let maxlen = (maxlen > &tw && &tw != 0 ? &tw : maxlen)
+ let half = maxlen/2
+ exe a:lastline
+ " put a string below
+ exe "norm! o\<esc>".(half - 1)."a-\<esc>A%<\<esc>".(half - 1)."a-"
+ " and above. its necessary to put the string below the block of lines
+ " first because that way the first line number doesnt change...
+ exe a:firstline
+ exe "norm! O\<esc>".(half - 1)."a-\<esc>A%<\<esc>".(half - 1)."a-"
+endfunction
+
+com! -nargs=0 -range Snip :<line1>,<line2>call <SID>Snip()
+" }}}
+
+let &cpo = s:save_cpo
+
+" vim:ft=vim:ts=4:sw=4:noet:fdm=marker:commentstring=\"\ %s:nowrap