annotate .vim/plugin/imaps.vim @ 26:0cd5ed5e4bcc

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