add vim conf files
[arrow.git] / conf_slk120 / vim / _vim / plugin / imaps.vim
blobe6ac5b9faefae35e84375b74ffae00cf44e11ff7
1 "        File: imaps.vim
2 "     Authors: Srinath Avadhanula <srinath AT fastmail.fm>
3 "              Benji Fisher <benji AT member.AMS.org>
4 "              
5 "         WWW: http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/vim-latex/vimfiles/plugin/imaps.vim?only_with_tag=MAIN
7 " Description: insert mode template expander with cursor placement
8 "              while preserving filetype indentation.
10 "     $Id: imaps.vim,v 1.1.1.1 2006/03/31 18:55:13 arrow Exp $
12 " Documentation: {{{
14 " Motivation:
15 " this script provides a way to generate insert mode mappings which do not
16 " suffer from some of the problem of mappings and abbreviations while allowing
17 " cursor placement after the expansion. It can alternatively be thought of as
18 " a template expander. 
20 " Consider an example. If you do
22 " imap lhs something
24 " then a mapping is set up. However, there will be the following problems:
25 " 1. the 'ttimeout' option will generally limit how easily you can type the
26 "    lhs. if you type the left hand side too slowly, then the mapping will not
27 "    be activated.
28 " 2. if you mistype one of the letters of the lhs, then the mapping is
29 "    deactivated as soon as you backspace to correct the mistake.
31 " If, in order to take care of the above problems, you do instead
33 " iab lhs something
35 " then the timeout problem is solved and so is the problem of mistyping.
36 " however, abbreviations are only expanded after typing a non-word character.
37 " which causes problems of cursor placement after the expansion and invariably
38 " spurious spaces are inserted.
39
40 " Usage Example:
41 " this script attempts to solve all these problems by providing an emulation
42 " of imaps wchich does not suffer from its attendant problems. Because maps
43 " are activated without having to press additional characters, therefore
44 " cursor placement is possible. furthermore, file-type specific indentation is
45 " preserved, because the rhs is expanded as if the rhs is typed in literally
46 " by the user.
47 "  
48 " The script already provides some default mappings. each "mapping" is of the
49 " form:
51 " call IMAP (lhs, rhs, ft)
52
53 " Some characters in the RHS have special meaning which help in cursor
54 " placement.
56 " Example One:
58 "       call IMAP ("bit`", "\\begin{itemize}\<cr>\\item <++>\<cr>\\end{itemize}<++>", "tex")
59
60 " This effectively sets up the map for "bit`" whenever you edit a latex file.
61 " When you type in this sequence of letters, the following text is inserted:
62
63 " \begin{itemize}
64 " \item *
65 " \end{itemize}<++>
67 " where * shows the cursor position. The cursor position after inserting the
68 " text is decided by the position of the first "place-holder". Place holders
69 " are special characters which decide cursor placement and movement. In the
70 " example above, the place holder characters are <+ and +>. After you have typed
71 " in the item, press <C-j> and you will be taken to the next set of <++>'s.
72 " Therefore by placing the <++> characters appropriately, you can minimize the
73 " use of movement keys.
75 " NOTE: Set g:Imap_UsePlaceHolders to 0 to disable placeholders altogether.
76 " Set 
77 "       g:Imap_PlaceHolderStart and g:Imap_PlaceHolderEnd
78 " to something else if you want different place holder characters.
79 " Also, b:Imap_PlaceHolderStart and b:Imap_PlaceHolderEnd override the values
80 " of g:Imap_PlaceHolderStart and g:Imap_PlaceHolderEnd respectively. This is
81 " useful for setting buffer specific place hoders.
82
83 " Example Two:
84 " You can use the <C-r> command to insert dynamic elements such as dates.
85 "       call IMAP ('date`', "\<c-r>=strftime('%b %d %Y')\<cr>", '')
87 " sets up the map for date` to insert the current date.
89 "--------------------------------------%<--------------------------------------
90 " Bonus: This script also provides a command Snip which puts tearoff strings,
91 " '----%<----' above and below the visually selected range of lines. The
92 " length of the string is chosen to be equal to the longest line in the range.
93 " Recommended Usage:
94 "   '<,'>Snip
95 "--------------------------------------%<--------------------------------------
96 " }}}
98 " ==============================================================================
99 " Script Options / Variables
100 " ============================================================================== 
101 " Options {{{
102 if !exists('g:Imap_StickyPlaceHolders')
103         let g:Imap_StickyPlaceHolders = 1
104 endif
105 if !exists('g:Imap_DeleteEmptyPlaceHolders')
106         let g:Imap_DeleteEmptyPlaceHolders = 1
107 endif
108 " }}}
109 " Variables {{{
110 " s:LHS_{ft}_{char} will be generated automatically.  It will look like
111 " s:LHS_tex_o = 'fo\|foo\|boo' and contain all mapped sequences ending in "o".
112 " s:Map_{ft}_{lhs} will be generated automatically.  It will look like
113 " s:Map_c_foo = 'for(<++>; <++>; <++>)', the mapping for "foo".
115 " }}}
117 " ==============================================================================
118 " functions for easy insert mode mappings.
119 " ==============================================================================
120 " IMAP: Adds a "fake" insert mode mapping. {{{
121 "       For example, doing
122 "           IMAP('abc', 'def' ft) 
123 "       will mean that if the letters abc are pressed in insert mode, then
124 "       they will be replaced by def. If ft != '', then the "mapping" will be
125 "       specific to the files of type ft. 
127 "       Using IMAP has a few advantages over simply doing:
128 "           imap abc def
129 "       1. with imap, if you begin typing abc, the cursor will not advance and
130 "          long as there is a possible completion, the letters a, b, c will be
131 "          displayed on on top of the other. using this function avoids that.
132 "       2. with imap, if a backspace or arrow key is pressed before completing
133 "          the word, then the mapping is lost. this function allows movement. 
134 "          (this ofcourse means that this function is only limited to
135 "          left-hand-sides which do not have movement keys or unprintable
136 "          characters)
137 "       It works by only mapping the last character of the left-hand side.
138 "       when this character is typed in, then a reverse lookup is done and if
139 "       the previous characters consititute the left hand side of the mapping,
140 "       the previously typed characters and erased and the right hand side is
141 "       inserted
143 " IMAP: set up a filetype specific mapping.
144 " Description:
145 "   "maps" the lhs to rhs in files of type 'ft'. If supplied with 2
146 "   additional arguments, then those are assumed to be the placeholder
147 "   characters in rhs. If unspecified, then the placeholder characters
148 "   are assumed to be '<+' and '+>' These placeholder characters in
149 "   a:rhs are replaced with the users setting of
150 "   [bg]:Imap_PlaceHolderStart and [bg]:Imap_PlaceHolderEnd settings.
152 function! IMAP(lhs, rhs, ft, ...)
154         " Find the place holders to save for IMAP_PutTextWithMovement() .
155         if a:0 < 2
156                 let phs = '<+'
157                 let phe = '+>'
158         else
159                 let phs = a:1
160                 let phe = a:2
161         endif
163         let hash = s:Hash(a:lhs)
164         let s:Map_{a:ft}_{hash} = a:rhs
165         let s:phs_{a:ft}_{hash} = phs
166         let s:phe_{a:ft}_{hash} = phe
168         " Add a:lhs to the list of left-hand sides that end with lastLHSChar:
169         let lastLHSChar = a:lhs[strlen(a:lhs)-1]
170         let hash = s:Hash(lastLHSChar)
171         if !exists("s:LHS_" . a:ft . "_" . hash)
172                 let s:LHS_{a:ft}_{hash} = escape(a:lhs, '\')
173         else
174                 let s:LHS_{a:ft}_{hash} = escape(a:lhs, '\') .'\|'.  s:LHS_{a:ft}_{hash}
175         endif
177         " map only the last character of the left-hand side.
178         if lastLHSChar == ' '
179                 let lastLHSChar = '<space>'
180         end
181         exe 'inoremap <silent>'
182                                 \ escape(lastLHSChar, '|')
183                                 \ '<C-r>=<SID>LookupCharacter("' .
184                                 \ escape(lastLHSChar, '\|"') .
185                                 \ '")<CR>'
186 endfunction
188 " }}}
189 " IMAP_list:  list the rhs and place holders corresponding to a:lhs {{{
191 " Added mainly for debugging purposes, but maybe worth keeping.
192 function! IMAP_list(lhs)
193         let char = a:lhs[strlen(a:lhs)-1]
194         let charHash = s:Hash(char)
195         if exists("s:LHS_" . &ft ."_". charHash) && a:lhs =~ s:LHS_{&ft}_{charHash}
196                 let ft = &ft
197         elseif exists("s:LHS__" . charHash) && a:lhs =~ s:LHS__{charHash}
198                 let ft = ""
199         else
200                 return ""
201         endif
202         let hash = s:Hash(a:lhs)
203         return "rhs = " . s:Map_{ft}_{hash} . " place holders = " .
204                                 \ s:phs_{ft}_{hash} . " and " . s:phe_{ft}_{hash}
205 endfunction
206 " }}}
207 " LookupCharacter: inserts mapping corresponding to this character {{{
209 " This function extracts from s:LHS_{&ft}_{a:char} or s:LHS__{a:char}
210 " the longest lhs matching the current text.  Then it replaces lhs with the
211 " corresponding rhs saved in s:Map_{ft}_{lhs} .
212 " The place-holder variables are passed to IMAP_PutTextWithMovement() .
213 function! s:LookupCharacter(char)
214         let charHash = s:Hash(a:char)
216         " The line so far, including the character that triggered this function:
217         let text = strpart(getline("."), 0, col(".")-1) . a:char
218         " Prefer a local map to a global one, even if the local map is shorter.
219         " Is this what we want?  Do we care?
220         " Use '\V' (very no-magic) so that only '\' is special, and it was already
221         " escaped when building up s:LHS_{&ft}_{charHash} .
222         if exists("s:LHS_" . &ft . "_" . charHash)
223                                 \ && text =~ '\V\(' . s:LHS_{&ft}_{charHash} . '\)\$'
224                 let ft = &ft
225         elseif exists("s:LHS__" . charHash)
226                                 \ && text =~ '\V\(' . s:LHS__{charHash} . '\)\$'
227                 let ft = ""
228         else
229                 " If this is a character which could have been used to trigger an
230                 " abbreviation, check if an abbreviation exists.
231                 if a:char !~ '\k'
232                         let lastword = matchstr(getline('.'), '\k\+$', '')
233                         if lastword != ''
234                                 " An extremeley wierd way to get around the fact that vim
235                                 " doesn't have the equivalent of the :mapcheck() function for
236                                 " abbreviations.
237                                 let _a = @a
238                                 exec "redir @a | silent! iab ".lastword." | redir END"
239                                 let abbreviationRHS = matchstr(@a."\n", "\n".'i\s\+'.lastword.'\+\s\+@\?\zs.*\ze'."\n")
241                                 if @a =~ "No abbreviation found" || abbreviationRHS == ""
242                                         let @a = _a
243                                         return a:char
244                                 endif
246                                 let @a = _a
247                                 let abbreviationRHS = escape(abbreviationRHS, '\<"')
248                                 exec 'let abbreviationRHS = "'.abbreviationRHS.'"'
250                                 let lhs = lastword.a:char
251                                 let rhs = abbreviationRHS.a:char
252                                 let phs = IMAP_GetPlaceHolderStart()
253                                 let phe = IMAP_GetPlaceHolderEnd()
254                         else
255                                 return a:char
256                         endif
257                 else
258                         return a:char
259                 endif
260         endif
261         " Find the longest left-hand side that matches the line so far.
262         " matchstr() returns the match that starts first. This automatically
263         " ensures that the longest LHS is used for the mapping.
264         if !exists('lhs') || !exists('rhs')
265                 let lhs = matchstr(text, '\V\(' . s:LHS_{ft}_{charHash} . '\)\$')
266                 let hash = s:Hash(lhs)
267                 let rhs = s:Map_{ft}_{hash}
268                 let phs = s:phs_{ft}_{hash} 
269                 let phe = s:phe_{ft}_{hash}
270         endif
272         if strlen(lhs) == 0
273                 return a:char
274         endif
275         " enough back-spaces to erase the left-hand side; -1 for the last
276         " character typed:
277         let bs = substitute(strpart(lhs, 1), ".", "\<bs>", "g")
278         return bs . IMAP_PutTextWithMovement(rhs, phs, phe)
279 endfunction
281 " }}}
282 " IMAP_PutTextWithMovement: returns the string with movement appended {{{
283 " Description:
284 "   If a:str contains "placeholders", then appends movement commands to
285 "   str in a way that the user moves to the first placeholder and enters
286 "   insert or select mode. If supplied with 2 additional arguments, then
287 "   they are assumed to be the placeholder specs. Otherwise, they are
288 "   assumed to be '<+' and '+>'. These placeholder chars are replaced
289 "   with the users settings of [bg]:Imap_PlaceHolderStart and
290 "   [bg]:Imap_PlaceHolderEnd.
291 function! IMAP_PutTextWithMovement(str, ...)
293         " The placeholders used in the particular input string. These can be
294         " different from what the user wants to use.
295         if a:0 < 2
296                 let phs = '<+'
297                 let phe = '+>'
298         else
299                 let phs = escape(a:1, '\')
300                 let phe = escape(a:2, '\')
301         endif
303         let text = a:str
305         " The user's placeholder settings.
306         let phsUser = IMAP_GetPlaceHolderStart()
307         let pheUser = IMAP_GetPlaceHolderEnd()
309         " Problem:  depending on the setting of the 'encoding' option, a character
310         " such as "\xab" may not match itself.  We try to get around this by
311         " changing the encoding of all our strings.  At the end, we have to
312         " convert text back.
313         let phsEnc     = s:Iconv(phs, "encode")
314         let pheEnc     = s:Iconv(phe, "encode")
315         let phsUserEnc = s:Iconv(phsUser, "encode")
316         let pheUserEnc = s:Iconv(pheUser, "encode")
317         let textEnc    = s:Iconv(text, "encode")
318         if textEnc != text
319                 let textEncoded = 1
320         else
321                 let textEncoded = 0
322         endif
324         let pattern = '\V\(\.\{-}\)' .phs. '\(\.\{-}\)' .phe. '\(\.\*\)'
325         " If there are no placeholders, just return the text.
326         if textEnc !~ pattern
327                 call IMAP_Debug('Not getting '.phs.' and '.phe.' in '.textEnc, 'imap')
328                 return text
329         endif
330         " Break text up into "initial <+template+> final"; any piece may be empty.
331         let initialEnc  = substitute(textEnc, pattern, '\1', '')
332         let templateEnc = substitute(textEnc, pattern, '\2', '')
333         let finalEnc    = substitute(textEnc, pattern, '\3', '')
335         " If the user does not want to use placeholders, then remove all but the
336         " first placeholder.
337         " Otherwise, replace all occurences of the placeholders here with the
338         " user's choice of placeholder settings.
339         if exists('g:Imap_UsePlaceHolders') && !g:Imap_UsePlaceHolders
340                 let finalEnc = substitute(finalEnc, '\V'.phs.'\.\{-}'.phe, '', 'g')
341         else
342                 let finalEnc = substitute(finalEnc, '\V'.phs.'\(\.\{-}\)'.phe,
343                                         \ phsUserEnc.'\1'.pheUserEnc, 'g')
344         endif
346         " The substitutions are done, so convert back, if necessary.
347         if textEncoded
348                 let initial = s:Iconv(initialEnc, "decode")
349                 let template = s:Iconv(templateEnc, "decode")
350                 let final = s:Iconv(finalEnc, "decode")
351         else
352                 let initial = initialEnc
353                 let template = templateEnc
354                 let final = finalEnc
355         endif
357         " Build up the text to insert:
358         " 1. the initial text plus an extra character;
359         " 2. go to Normal mode with <C-\><C-N>, so it works even if 'insertmode'
360         " is set, and mark the position;
361         " 3. replace the extra character with tamplate and final;
362         " 4. back to Normal mode and restore the cursor position;
363         " 5. call IMAP_Jumpfunc().
364         let template = phsUser . template . pheUser
365         " Old trick:  insert and delete a character to get the same behavior at
366         " start, middle, or end of line and on empty lines.
367         let text = initial . "X\<C-\>\<C-N>:call IMAP_Mark('set')\<CR>\"_s"
368         let text = text . template . final
369         let text = text . "\<C-\>\<C-N>:call IMAP_Mark('go')\<CR>"
370         let text = text . "i\<C-r>=IMAP_Jumpfunc('', 1)\<CR>"
372         return text
373 endfunction
375 " }}}
376 " IMAP_Jumpfunc: takes user to next <+place-holder+> {{{
377 " Author: Luc Hermitte
378 " Arguments:
379 " direction: flag for the search() function. If set to '', search forwards,
380 "            if 'b', then search backwards. See the {flags} argument of the
381 "            |search()| function for valid values.
382 " inclusive: In vim, the search() function is 'exclusive', i.e we always goto
383 "            next cursor match even if there is a match starting from the
384 "            current cursor position. Setting this argument to 1 makes
385 "            IMAP_Jumpfunc() also respect a match at the current cursor
386 "            position. 'inclusive'ness is necessary for IMAP() because a
387 "            placeholder string can occur at the very beginning of a map which
388 "            we want to select.
389 "            We use a non-zero value only in special conditions. Most mappings
390 "            should use a zero value.
391 function! IMAP_Jumpfunc(direction, inclusive)
393         " The user's placeholder settings.
394         let phsUser = IMAP_GetPlaceHolderStart()
395         let pheUser = IMAP_GetPlaceHolderEnd()
397         let searchString = ''
398         " If this is not an inclusive search or if it is inclusive, but the
399         " current cursor position does not contain a placeholder character, then
400         " search for the placeholder characters.
401         if !a:inclusive || strpart(getline('.'), col('.')-1) !~ '\V\^'.phsUser
402                 let searchString = '\V'.phsUser.'\_.\{-}'.pheUser
403         endif
405         " If we didn't find any placeholders return quietly.
406         if searchString != '' && !search(searchString, a:direction)
407                 return ''
408         endif
410         " Open any closed folds and make this part of the text visible.
411         silent! foldopen!
413         " Calculate if we have an empty placeholder or if it contains some
414         " description.
415         let template = 
416                 \ matchstr(strpart(getline('.'), col('.')-1),
417                 \          '\V\^'.phsUser.'\zs\.\{-}\ze\('.pheUser.'\|\$\)')
418         let placeHolderEmpty = !strlen(template)
420         " If we are selecting in exclusive mode, then we need to move one step to
421         " the right
422         let extramove = ''
423         if &selection == 'exclusive'
424                 let extramove = 'l'
425         endif
427         " Select till the end placeholder character.
428         let movement = "\<C-o>v/\\V".pheUser."/e\<CR>".extramove
430         " First remember what the search pattern was. s:RemoveLastHistoryItem will
431         " reset @/ to this pattern so we do not create new highlighting.
432         let g:Tex_LastSearchPattern = @/
434         " Now either goto insert mode or select mode.
435         if placeHolderEmpty && g:Imap_DeleteEmptyPlaceHolders
436                 " delete the empty placeholder into the blackhole.
437                 return movement."\"_c\<C-o>:".s:RemoveLastHistoryItem."\<CR>"
438         else
439                 return movement."\<C-\>\<C-N>:".s:RemoveLastHistoryItem."\<CR>gv\<C-g>"
440         endif
441         
442 endfunction
444 " }}}
445 " Maps for IMAP_Jumpfunc {{{
447 " These mappings use <Plug> and thus provide for easy user customization. When
448 " the user wants to map some other key to jump forward, he can do for
449 " instance:
450 "   nmap ,f   <plug>IMAP_JumpForward
451 " etc.
453 " jumping forward and back in insert mode.
454 imap <silent> <Plug>IMAP_JumpForward    <c-r>=IMAP_Jumpfunc('', 0)<CR>
455 imap <silent> <Plug>IMAP_JumpBack       <c-r>=IMAP_Jumpfunc('b', 0)<CR>
457 " jumping in normal mode
458 nmap <silent> <Plug>IMAP_JumpForward        i<c-r>=IMAP_Jumpfunc('', 0)<CR>
459 nmap <silent> <Plug>IMAP_JumpBack           i<c-r>=IMAP_Jumpfunc('b', 0)<CR>
461 " deleting the present selection and then jumping forward.
462 vmap <silent> <Plug>IMAP_DeleteAndJumpForward       "_<Del>i<c-r>=IMAP_Jumpfunc('', 0)<CR>
463 vmap <silent> <Plug>IMAP_DeleteAndJumpBack          "_<Del>i<c-r>=IMAP_Jumpfunc('b', 0)<CR>
465 " jumping forward without deleting present selection.
466 vmap <silent> <Plug>IMAP_JumpForward       <C-\><C-N>i<c-r>=IMAP_Jumpfunc('', 0)<CR>
467 vmap <silent> <Plug>IMAP_JumpBack          <C-\><C-N>`<i<c-r>=IMAP_Jumpfunc('b', 0)<CR>
469 " }}}
470 " Default maps for IMAP_Jumpfunc {{{
471 " map only if there is no mapping already. allows for user customization.
472 " NOTE: Default mappings for jumping to the previous placeholder are not
473 "       provided. It is assumed that if the user will create such mappings
474 "       hself if e so desires.
475 if !hasmapto('<Plug>IMAP_JumpForward', 'i')
476     imap <C-J> <Plug>IMAP_JumpForward
477 endif
478 if !hasmapto('<Plug>IMAP_JumpForward', 'n')
479     nmap <C-J> <Plug>IMAP_JumpForward
480 endif
481 if exists('g:Imap_StickyPlaceHolders') && g:Imap_StickyPlaceHolders
482         if !hasmapto('<Plug>IMAP_JumpForward', 'v')
483                 vmap <C-J> <Plug>IMAP_JumpForward
484         endif
485 else
486         if !hasmapto('<Plug>IMAP_DeleteAndJumpForward', 'v')
487                 vmap <C-J> <Plug>IMAP_DeleteAndJumpForward
488         endif
489 endif
490 " }}}
492 nmap <silent> <script> <plug><+SelectRegion+> `<v`>
494 " ============================================================================== 
495 " enclosing selected region.
496 " ============================================================================== 
497 " VEnclose: encloses the visually selected region with given arguments {{{
498 " Description: allows for differing action based on visual line wise
499 "              selection or visual characterwise selection. preserves the
500 "              marks and search history.
501 function! VEnclose(vstart, vend, VStart, VEnd)
503         " its characterwise if
504         " 1. characterwise selection and valid values for vstart and vend.
505         " OR
506         " 2. linewise selection and invalid values for VStart and VEnd
507         if (visualmode() == 'v' && (a:vstart != '' || a:vend != '')) || (a:VStart == '' && a:VEnd == '')
509                 let newline = ""
510                 let _r = @r
512                 let normcmd = "normal! \<C-\>\<C-n>`<v`>\"_s"
514                 exe "normal! \<C-\>\<C-n>`<v`>\"ry"
515                 if @r =~ "\n$"
516                         let newline = "\n"
517                         let @r = substitute(@r, "\n$", '', '')
518                 endif
520                 " In exclusive selection, we need to select an extra character.
521                 if &selection == 'exclusive'
522                         let movement = 8
523                 else
524                         let movement = 7
525                 endif
526                 let normcmd = normcmd.
527                         \ a:vstart."!!mark!!".a:vend.newline.
528                         \ "\<C-\>\<C-N>?!!mark!!\<CR>v".movement."l\"_s\<C-r>r\<C-\>\<C-n>"
530                 " this little if statement is because till very recently, vim used to
531                 " report col("'>") > length of selected line when `> is $. on some
532                 " systems it reports a -ve number.
533                 if col("'>") < 0 || col("'>") > strlen(getline("'>"))
534                         let lastcol = strlen(getline("'>"))
535                 else
536                         let lastcol = col("'>")
537                 endif
538                 if lastcol - col("'<") != 0
539                         let len = lastcol - col("'<")
540                 else
541                         let len = ''
542                 endif
544                 " the next normal! is for restoring the marks.
545                 let normcmd = normcmd."`<v".len."l\<C-\>\<C-N>"
547                 " First remember what the search pattern was. s:RemoveLastHistoryItem
548                 " will reset @/ to this pattern so we do not create new highlighting.
549                 let g:Tex_LastSearchPattern = @/
551                 silent! exe normcmd
552                 " this is to restore the r register.
553                 let @r = _r
554                 " and finally, this is to restore the search history.
555                 execute s:RemoveLastHistoryItem
557         else
559                 exec 'normal! `<O'.a:VStart."\<C-\>\<C-n>"
560                 exec 'normal! `>o'.a:VEnd."\<C-\>\<C-n>"
561                 if &indentexpr != ''
562                         silent! normal! `<kV`>j=
563                 endif
564                 silent! normal! `>
565         endif
566 endfunction 
568 " }}}
569 " ExecMap: adds the ability to correct an normal/visual mode mapping.  {{{
570 " Author: Hari Krishna Dara <hari_vim@yahoo.com>
571 " Reads a normal mode mapping at the command line and executes it with the
572 " given prefix. Press <BS> to correct and <Esc> to cancel.
573 function! ExecMap(prefix, mode)
574         " Temporarily remove the mapping, otherwise it will interfere with the
575         " mapcheck call below:
576         let myMap = maparg(a:prefix, a:mode)
577         exec a:mode."unmap ".a:prefix
579         " Generate a line with spaces to clear the previous message.
580         let i = 1
581         let clearLine = "\r"
582         while i < &columns
583                 let clearLine = clearLine . ' '
584                 let i = i + 1
585         endwhile
587         let mapCmd = a:prefix
588         let foundMap = 0
589         let breakLoop = 0
590         echon "\rEnter Map: " . mapCmd
591         while !breakLoop
592                 let char = getchar()
593                 if char !~ '^\d\+$'
594                         if char == "\<BS>"
595                                 let mapCmd = strpart(mapCmd, 0, strlen(mapCmd) - 1)
596                         endif
597                 else " It is the ascii code.
598                         let char = nr2char(char)
599                         if char == "\<Esc>"
600                                 let breakLoop = 1
601                         else
602                                 let mapCmd = mapCmd . char
603                                 if maparg(mapCmd, a:mode) != ""
604                                         let foundMap = 1
605                                         let breakLoop = 1
606                                 elseif mapcheck(mapCmd, a:mode) == ""
607                                         let mapCmd = strpart(mapCmd, 0, strlen(mapCmd) - 1)
608                                 endif
609                         endif
610                 endif
611                 echon clearLine
612                 echon "\rEnter Map: " . mapCmd
613         endwhile
614         if foundMap
615                 if a:mode == 'v'
616                         " use a plug to select the region instead of using something like
617                         " `<v`> to avoid problems caused by some of the characters in
618                         " '`<v`>' being mapped.
619                         let gotoc = "\<plug><+SelectRegion+>"
620                 else
621                         let gotoc = ''
622                 endif
623                 exec "normal ".gotoc.mapCmd
624         endif
625         exec a:mode.'noremap '.a:prefix.' '.myMap
626 endfunction
628 " }}}
630 " ============================================================================== 
631 " helper functions
632 " ============================================================================== 
633 " Strntok: extract the n^th token from a list {{{
634 " example: Strntok('1,23,3', ',', 2) = 23
635 fun! <SID>Strntok(s, tok, n)
636         return matchstr( a:s.a:tok[0], '\v(\zs([^'.a:tok.']*)\ze['.a:tok.']){'.a:n.'}')
637 endfun
639 " }}}
640 " s:RemoveLastHistoryItem: removes last search item from search history {{{
641 " Description: Execute this string to clean up the search history.
642 let s:RemoveLastHistoryItem = ':call histdel("/", -1)|let @/=g:Tex_LastSearchPattern'
644 " }}}
645 " s:Hash: Return a version of a string that can be used as part of a variable" {{{
646 " name.
647 "       Converts every non alphanumeric character into _{ascii}_ where {ascii} is
648 "       the ASCII code for that character...
649 fun! s:Hash(text)
650         return substitute(a:text, '\([^[:alnum:]]\)',
651                                 \ '\="_".char2nr(submatch(1))."_"', 'g')
652 endfun
653 "" }}}
654 " IMAP_GetPlaceHolderStart and IMAP_GetPlaceHolderEnd:  "{{{
655 " return the buffer local placeholder variables, or the global one, or the default.
656 function! IMAP_GetPlaceHolderStart()
657         if exists("b:Imap_PlaceHolderStart") && strlen(b:Imap_PlaceHolderEnd)
658                 return b:Imap_PlaceHolderStart
659         elseif exists("g:Imap_PlaceHolderStart") && strlen(g:Imap_PlaceHolderEnd)
660                 return g:Imap_PlaceHolderStart
661         else
662                 return "<+"
663 endfun
664 function! IMAP_GetPlaceHolderEnd()
665         if exists("b:Imap_PlaceHolderEnd") && strlen(b:Imap_PlaceHolderEnd)
666                 return b:Imap_PlaceHolderEnd
667         elseif exists("g:Imap_PlaceHolderEnd") && strlen(g:Imap_PlaceHolderEnd)
668                 return g:Imap_PlaceHolderEnd
669         else
670                 return "+>"
671 endfun
672 " }}}
673 " s:Iconv:  a wrapper for iconv()" {{{
674 " Problem:  after
675 "       let text = "\xab"
676 " (or using the raw 8-bit ASCII character in a file with 'fenc' set to
677 " "latin1") if 'encoding' is set to utf-8, then text does not match itself:
678 "       echo text =~ text
679 " returns 0.
680 " Solution:  When this happens, a re-encoded version of text does match text:
681 "       echo iconv(text, "latin1", "utf8") =~ text
682 " returns 1.  In this case, convert text to utf-8 with iconv().
683 " TODO:  Is it better to use &encoding instead of "utf8"?  Internally, vim
684 " uses utf-8, and can convert between latin1 and utf-8 even when compiled with
685 " -iconv, so let's try using utf-8.
686 " Arguments:
687 "       a:text = text to be encoded or decoded
688 "       a:mode = "encode" (latin1 to utf8) or "decode" (utf8 to latin1)
689 " Caution:  do not encode and then decode without checking whether the text
690 " has changed, becuase of the :if clause in encoding!
691 function! s:Iconv(text, mode)
692         if a:mode == "decode"
693                 return iconv(a:text, "utf8", "latin1")
694         endif
695         if a:text =~ '\V\^' . escape(a:text, '\') . '\$'
696                 return a:text
697         endif
698         let textEnc = iconv(a:text, "latin1", "utf8")
699         if textEnc !~ '\V\^' . escape(a:text, '\') . '\$''
700                 call IMAP_Debug('Encoding problems with text '.a:text.' ', 'imap')
701         endif
702         return textEnc
703 endfun
704 "" }}}
705 " IMAP_Debug: interface to Tex_Debug if available, otherwise emulate it {{{
706 " Description: 
707 " Do not want a memory leak! Set this to zero so that imaps always
708 " starts out in a non-debugging mode.
709 if !exists('g:Imap_Debug')
710         let g:Imap_Debug = 0
711 endif
712 function! IMAP_Debug(string, pattern)
713         if !g:Imap_Debug
714                 return
715         endif
716         if exists('*Tex_Debug')
717                 call Tex_Debug(a:string, a:pattern)
718         else
719                 if !exists('s:debug_'.a:pattern)
720                         let s:debug_{a:pattern} = a:string
721                 else
722                         let s:debug_{a:pattern} = s:debug_{a:pattern}.a:string
723                 endif
724         endif
725 endfunction " }}}
726 " IMAP_DebugClear: interface to Tex_DebugClear if avaialable, otherwise emulate it {{{
727 " Description: 
728 function! IMAP_DebugClear(pattern)
729         if exists('*Tex_DebugClear')
730                 call Tex_DebugClear(a:pattern)
731         else    
732                 let s:debug_{a:pattern} = ''
733         endif
734 endfunction " }}}
735 " IMAP_DebugPrint: interface to Tex_DebugPrint if avaialable, otherwise emulate it {{{
736 " Description: 
737 function! IMAP_DebugPrint(pattern)
738         if exists('*Tex_DebugPrint')
739                 call Tex_DebugPrint(a:pattern)
740         else
741                 if exists('s:debug_'.a:pattern)
742                         let s:debug_{a:pattern} = ''
743                 else
744                         echo s:debug_{a:pattern}
745                 endif
746         endif
747 endfunction " }}}
748 " IMAP_Mark:  Save the cursor position (if a:action == 'set') in a" {{{
749 " script-local variable; restore this position if a:action == 'go'.
750 let s:Mark = "(0,0)"
751 function! IMAP_Mark(action)
752         if a:action == 'set'
753                 let s:Mark = "(" . line(".") . "," . col(".") . ")"
754         elseif a:action == 'go'
755                 execute "call cursor" s:Mark
756         endif
757 endfunction     "" }}}
759 " ============================================================================== 
760 " A bonus function: Snip()
761 " ============================================================================== 
762 " Snip: puts a scissor string above and below block of text {{{
763 " Desciption:
764 "-------------------------------------%<-------------------------------------
765 "   this puts a the string "--------%<---------" above and below the visually
766 "   selected block of lines. the length of the 'tearoff' string depends on the
767 "   maximum string length in the selected range. this is an aesthetically more
768 "   pleasing alternative instead of hardcoding a length.
769 "-------------------------------------%<-------------------------------------
770 function! <SID>Snip() range
771         let i = a:firstline
772         let maxlen = -2
773         " find out the maximum virtual length of each line.
774         while i <= a:lastline
775                 exe i
776                 let length = virtcol('$')
777                 let maxlen = (length > maxlen ? length : maxlen)
778                 let i = i + 1
779         endwhile
780         let maxlen = (maxlen > &tw && &tw != 0 ? &tw : maxlen)
781         let half = maxlen/2
782         exe a:lastline
783         " put a string below
784         exe "norm! o\<esc>".(half - 1)."a-\<esc>A%<\<esc>".(half - 1)."a-"
785         " and above. its necessary to put the string below the block of lines
786         " first because that way the first line number doesnt change...
787         exe a:firstline
788         exe "norm! O\<esc>".(half - 1)."a-\<esc>A%<\<esc>".(half - 1)."a-"
789 endfunction
791 com! -nargs=0 -range Snip :<line1>,<line2>call <SID>Snip()
792 " }}}
794 " vim:ft=vim:ts=4:sw=4:noet:fdm=marker:commentstring=\"\ %s:nowrap