From 14d64785aaac4bb792f31c2fafb7332b74612c9f Mon Sep 17 00:00:00 2001 From: David Kaufmann Date: Mon, 14 Nov 2011 00:13:19 +0100 Subject: init copy --- vim-latex/plugin/SyntaxFolds.vim | 323 +++++++++++++++ vim-latex/plugin/filebrowser.vim | 250 ++++++++++++ vim-latex/plugin/imaps.vim | 831 +++++++++++++++++++++++++++++++++++++++ vim-latex/plugin/libList.vim | 249 ++++++++++++ vim-latex/plugin/remoteOpen.vim | 163 ++++++++ 5 files changed, 1816 insertions(+) create mode 100644 vim-latex/plugin/SyntaxFolds.vim create mode 100644 vim-latex/plugin/filebrowser.vim create mode 100644 vim-latex/plugin/imaps.vim create mode 100644 vim-latex/plugin/libList.vim create mode 100644 vim-latex/plugin/remoteOpen.vim (limited to 'vim-latex/plugin') diff --git a/vim-latex/plugin/SyntaxFolds.vim b/vim-latex/plugin/SyntaxFolds.vim new file mode 100644 index 0000000..27c622c --- /dev/null +++ b/vim-latex/plugin/SyntaxFolds.vim @@ -0,0 +1,323 @@ +" ============================================================================== +" File: syntaxFolds.vim +" Author: Srinath Avadhanula +" ( srinath@fastmail.fm ) +" Last Change: Sun Oct 27 01:00 AM 2002 PST +" Description: Emulation of the syntax folding capability of vim using manual +" folding +" +" This script provides an emulation of the syntax folding of vim using manual +" folding. Just as in syntax folding, the folds are defined by regions. Each +" region is specified by a call to FoldRegions() which accepts 4 parameters: +" +" call FoldRegions(startpat, endpat, startoff, endoff) +" +" startpat: a line matching this pattern defines the beginning of a fold. +" endpat : a line matching this pattern defines the end of a fold. +" startoff: this is the offset from the starting line at which folding will +" actually start +" endoff : like startoff, but gives the offset of the actual fold end from +" the line satisfying endpat. +" startoff and endoff are necessary when the folding region does +" not have a specific end pattern corresponding to a start +" pattern. for example in latex, +" \begin{section} +" defines the beginning of a section, but its not necessary to +" have a corresponding +" \end{section} +" the section is assumed to end 1 line _before_ another section +" starts. +" startskip: a pattern which defines the beginning of a "skipped" region. +" +" For example, suppose we define a \itemize fold as follows: +" startpat = '^\s*\\item', +" endpat = '^\s*\\item\|^\s*\\end{\(enumerate\|itemize\|description\)}', +" startoff = 0, +" endoff = -1 +" +" This defines a fold which starts with a line beginning with an +" \item and ending one line before a line beginning with an +" \item or \end{enumerate} etc. +" +" Then, as long as \item's are not nested things are fine. +" However, once items begin to nest, the fold started by one +" \item can end because of an \item in an \itemize +" environment within this \item. i.e, the following can happen: +" +" \begin{itemize} +" \item Some text <------- fold will start here +" This item will contain a nested item +" \begin{itemize} <----- fold will end here because next line contains \item... +" \item Hello +" \end{itemize} <----- ... instead of here. +" \item Next item of the parent itemize +" \end{itemize} +" +" Therefore, in order to completely define a folding item which +" allows nesting, we need to also define a "skip" pattern. +" startskip and end skip do that. +" Leave '' when there is no nesting. +" endskip: the pattern which defines the end of the "skip" pattern for +" nested folds. +" +" Example: +" 1. A syntax fold region for a latex section is +" startpat = "\\section{" +" endpat = "\\section{" +" startoff = 0 +" endoff = -1 +" startskip = '' +" endskip = '' +" Note that the start and end patterns are thus the same and endoff has a +" negative value to capture the effect of a section ending one line before +" the next starts. +" 2. A syntax fold region for the \itemize environment is: +" startpat = '^\s*\\item', +" endpat = '^\s*\\item\|^\s*\\end{\(enumerate\|itemize\|description\)}', +" startoff = 0, +" endoff = -1, +" startskip = '^\s*\\begin{\(enumerate\|itemize\|description\)}', +" endskip = '^\s*\\end{\(enumerate\|itemize\|description\)}' +" Note the use of startskip and endskip to allow nesting. +" +" +" Each time a call is made to FoldRegions(), all the regions (which might be +" disjoint, but not nested) are folded up. +" Nested folds can be created by successive calls to FoldRegions(). The first +" call defines the region which is deepest in the folding. See MakeTexFolds() +" for an idea of how this works for latex files. + +" Function: AddSyntaxFoldItem (start, end, startoff, endoff [, skipStart, skipEnd]) {{{ +function! AddSyntaxFoldItem(start, end, startoff, endoff, ...) + if a:0 > 0 + let skipStart = a:1 + let skipEnd = a:2 + else + let skipStart = '' + let skipEnd = '' + end + if !exists('b:numFoldItems') + let b:numFoldItems = 0 + end + let b:numFoldItems = b:numFoldItems + 1 + + exe 'let b:startPat_'.b:numFoldItems.' = a:start' + exe 'let b:endPat_'.b:numFoldItems.' = a:end' + exe 'let b:startOff_'.b:numFoldItems.' = a:startoff' + exe 'let b:endOff_'.b:numFoldItems.' = a:endoff' + exe 'let b:skipStartPat_'.b:numFoldItems.' = skipStart' + exe 'let b:skipEndPat_'.b:numFoldItems.' = skipEnd' +endfunction + + +" }}} +" Function: MakeSyntaxFolds (force) {{{ +" Description: This function calls FoldRegions() several times with the +" parameters specifying various regions resulting in a nested fold +" structure for the file. +function! MakeSyntaxFolds(force, ...) + if exists('b:doneFolding') && a:force == 0 + return + end + + let skipEndPattern = '' + if a:0 > 0 + let line1 = a:1 + let skipEndPattern = '\|'.a:2 + else + let line1 = 1 + let r = line('.') + let c = virtcol('.') + + setlocal fdm=manual + normal! zE + end + if !exists('b:numFoldItems') + b:numFoldItems = 1000000 + end + + let i = 1 + + let maxline = line('.') + + while exists('b:startPat_'.i) && i <= b:numFoldItems + exe 'let startPat = b:startPat_'.i + exe 'let endPat = b:endPat_'.i + exe 'let startOff = b:startOff_'.i + exe 'let endOff = b:endOff_'.i + + let skipStart = '' + let skipEnd = '' + if exists('b:skipStartPat_'.i) + exe 'let skipStart = b:skipStartPat_'.i + exe 'let skipEnd = b:skipEndPat_'.i + end + exe line1 + let lastLoc = line1 + + if skipStart != '' + call InitStack('BeginSkipArray') + call FoldRegionsWithSkip(startPat, endPat, startOff, endOff, skipStart, skipEnd, 1, line('$')) + " call PrintError('done folding ['.startPat.']') + else + call FoldRegionsWithNoSkip(startPat, endPat, startOff, endOff, 1, line('$'), '') + end + + let i = i + 1 + endwhile + + exe maxline + + if a:0 == 0 + exe r + exe "normal! ".c."|" + if foldlevel(r) > 1 + exe "normal! ".(foldlevel(r) - 1)."zo" + end + let b:doneFolding = 0 + end +endfunction + + +" }}} +" FoldRegionsWithSkip: folding things such as \item's which can be nested. {{{ +function! FoldRegionsWithSkip(startpat, endpat, startoff, endoff, startskip, endskip, line1, line2) + exe a:line1 + " count the regions which have been skipped as we go along. do not want to + " create a fold which with a beginning or end line in one of the skipped + " regions. + let skippedRegions = '' + + " start searching for either the starting pattern or the end pattern. + while search(a:startskip.'\|'.a:endskip, 'W') + + if getline('.') =~ a:endskip + + let lastBegin = Pop('BeginSkipArray') + " call PrintError('popping '.lastBegin.' from stack and folding till '.line('.')) + call FoldRegionsWithNoSkip(a:startpat, a:endpat, a:startoff, a:endoff, lastBegin, line('.'), skippedRegions) + let skippedRegions = skippedRegions.lastBegin.','.line('.').'|' + + + " if this is the beginning of a skip region, then, push this line as + " the beginning of a skipped region. + elseif getline('.') =~ a:startskip + + " call PrintError('pushing '.line('.').' ['.getline('.').'] into stack') + call Push('BeginSkipArray', line('.')) + + end + endwhile + + " call PrintError('with skip starting at '.a:line1.' returning at line# '.line('.')) +endfunction + +" }}} +" FoldRegionsWithNoSkip: folding things such as \sections which do not nest. {{{ +function! FoldRegionsWithNoSkip(startpat, endpat, startoff, endoff, line1, line2, skippedRegions) + exe a:line1 + + " call PrintError('line1 = '.a:line1.', searching from '.line('.').'... for ['.a:startpat.'') + let lineBegin = s:MySearch(a:startpat, 'in') + " call PrintError('... and finding it at '.lineBegin) + + while lineBegin <= a:line2 + if IsInSkippedRegion(lineBegin, a:skippedRegions) + let lineBegin = s:MySearch(a:startpat, 'out') + " call PrintError(lineBegin.' is being skipped') + continue + end + let lineEnd = s:MySearch(a:endpat, 'out') + while IsInSkippedRegion(lineEnd, a:skippedRegions) && lineEnd <= a:line2 + let lineEnd = s:MySearch(a:endpat, 'out') + endwhile + if lineEnd > a:line2 + exe (lineBegin + a:startoff).','.a:line2.' fold' + break + else + " call PrintError ('for ['.a:startpat.'] '.(lineBegin + a:startoff).','.(lineEnd + a:endoff).' fold') + exe (lineBegin + a:startoff).','.(lineEnd + a:endoff).' fold' + end + + " call PrintError('line1 = '.a:line1.', searching from '.line('.').'... for ['.a:startpat.'') + let lineBegin = s:MySearch(a:startpat, 'in') + " call PrintError('... and finding it at '.lineBegin) + endwhile + + exe a:line2 + return +endfunction + +" }}} +" InitStack: initialize a stack {{{ +function! InitStack(name) + exe 'let s:'.a:name.'_numElems = 0' +endfunction +" }}} +" Push: push element into stack {{{ +function! Push(name, elem) + exe 'let numElems = s:'.a:name.'_numElems' + let numElems = numElems + 1 + exe 'let s:'.a:name.'_Element_'.numElems.' = a:elem' + exe 'let s:'.a:name.'_numElems = numElems' +endfunction +" }}} +" Pop: pops element off stack {{{ +function! Pop(name) + exe 'let numElems = s:'.a:name.'_numElems' + if numElems == 0 + return '' + else + exe 'let ret = s:'.a:name.'_Element_'.numElems + let numElems = numElems - 1 + exe 'let s:'.a:name.'_numElems = numElems' + return ret + end +endfunction +" }}} +" MySearch: just like search(), but returns large number on failure {{{ +function! MySearch(pat, opt) + if a:opt == 'in' + if getline('.') =~ a:pat + let ret = line('.') + else + let ret = search(a:pat, 'W') + end + else + normal! $ + let ret = search(a:pat, 'W') + end + + if ret == 0 + let ret = line('$') + 1 + end + return ret +endfunction +" }}} +" Function: IsInSkippedRegion (lnum, regions) {{{ +" Description: finds whether a given line number is within one of the regions +" skipped. +function! IsInSkippedRegion(lnum, regions) + let i = 1 + let subset = s:Strntok(a:regions, '|', i) + while subset != '' + let n1 = s:Strntok(subset, ',', 1) + let n2 = s:Strntok(subset, ',', 2) + if a:lnum >= n1 && a:lnum <= n2 + return 1 + end + + let subset = s:Strntok(a:regions, '|', i) + let i = i + 1 + endwhile + + return 0 +endfunction " }}} +" Function: Strntok (string, tok, n) {{{ +" extract the n^th token from s seperated by tok. +" example: Strntok('1,23,3', ',', 2) = 23 +fun! Strntok(s, tok, n) + return matchstr( a:s.a:tok[0], '\v(\zs([^'.a:tok.']*)\ze['.a:tok.']){'.a:n.'}') +endfun " }}} + +" vim600:fdm=marker diff --git a/vim-latex/plugin/filebrowser.vim b/vim-latex/plugin/filebrowser.vim new file mode 100644 index 0000000..4be8c46 --- /dev/null +++ b/vim-latex/plugin/filebrowser.vim @@ -0,0 +1,250 @@ +" filebrowser.vim: utility file for vim 6.2+ +" +" Copyright: Srinath Avadhanula +" Parts of this file are taken from explorer.vim which is a plugin file +" distributed with vim under the Vim charityware license. +" License: distributed under the Vim charityware license. +" +" Settings: +" FB_CallBackFunction: the function name which gets called when the user +" presses on a file-name in the file browser. +" FB_AllowRegexp: A filename has to match this regexp to be displayed. +" FB_RejectRegexp: If a filename matches this regexp, then its not displayed. +" (Both these regexps are '' by default which means no filtering is +" done). + +" line continuation used here. +let s:save_cpo = &cpo +set cpo&vim + +"====================================================================== +" Globally visible functions (API) +"====================================================================== +" FB_OpenFileBrowser: opens a new buffer and displays the file list {{{ +" Description: +function! FB_OpenFileBrowser(dir) + if !isdirectory(a:dir) + return + endif + if exists('s:FB_BufferNumber') + if bufwinnr(s:FB_BufferNumber) != -1 + execute bufwinnr(s:FB_BufferNumber).' wincmd w' + return + endif + execute 'aboveleft split #'.s:FB_BufferNumber + else + aboveleft split __Choose_File__ + let s:FB_BufferNumber = bufnr('%') + endif + call FB_DisplayFiles(a:dir) +endfunction " }}} +" FB_DisplayFiles: displays the files in a given directory {{{ +" Description: +" Call this function only when the cursor is in a temporary buffer +function! FB_DisplayFiles(dir) + if !isdirectory(a:dir) + return + endif + call s:FB_SetSilentSettings() + " make this a "scratch" buffer + call s:FB_SetScratchSettings() + + let allowRegexp = s:FB_GetVar('FB_AllowRegexp', '') + let rejectRegexp = s:FB_GetVar('FB_RejectRegexp', '') + + " change to the directory to make processing simpler. + execute "lcd ".a:dir + " delete everything in the buffer. + " IMPORTANT: we need to be in a scratch buffer + 0,$ d_ + + let allFilenames = glob('*') + let dispFiles = "" + let subDirs = "../\n" + + let allFilenames = allFilenames."\n" + let start = 0 + while 1 + let next = stridx(allFilenames, "\n", start) + let filename = strpart(allFilenames, start, next-start) + if filename == "" + break + endif + + if isdirectory(filename) + let subDirs = subDirs.filename."/\n" + else + if allowRegexp != '' && filename !~ allowRegexp + elseif rejectRegexp != '' && filename =~ rejectRegexp + else + let dispFiles = dispFiles.filename."\n" + endif + endif + + let start = next + 1 + endwhile + + 0put!=dispFiles + 0put!=subDirs + " delte the last empty line resulting from the put + $ d_ + + call s:FB_SetHighlighting() + call s:FB_DisplayHelp() + call s:FB_SetMaps() + + " goto the first file/directory + 0 + call search('^"=', 'w') + normal! j: + + set nomodified nomodifiable + + call s:FB_ResetSilentSettings() +endfunction " }}} +" FB_SetVar: sets script local variables from outside this script {{{ +" Description: +function! FB_SetVar(varname, value) + let s:{a:varname} = a:value +endfunction " }}} + +" ============================================================================== +" Script local functions below this +" ============================================================================== +" FB_SetHighlighting: sets syntax highlighting for the buffer {{{ +" Description: +" Origin: from explorer.vim in vim +function! FB_SetHighlighting() + " Set up syntax highlighting + " Something wrong with the evaluation of the conditional though... + if has("syntax") && exists("g:syntax_on") && !has("syntax_items") + syn match browseSynopsis "^\"[ -].*" + syn match browseDirectory "[^\"].*/ " + syn match browseDirectory "[^\"].*/$" + syn match browseCurDir "^\"= .*$" + syn match browseSortBy "^\" Sorted by .*$" contains=browseSuffixInfo + syn match browseSuffixInfo "(.*)$" contained + syn match browseFilter "^\" Not Showing:.*$" + syn match browseFiletime "«\d\+$" + + "hi def link browseSynopsis PreProc + hi def link browseSynopsis Special + hi def link browseDirectory Directory + hi def link browseCurDir Statement + hi def link browseSortBy String + hi def link browseSuffixInfo Type + hi def link browseFilter String + hi def link browseFiletime Ignore + hi def link browseSuffixes Type + endif +endfunction " }}} +" FB_SetMaps: sets buffer local maps {{{ +" Description: +function! FB_SetMaps() + nnoremap q :bdelete + nnoremap C :call FB_DisplayFiles(getcwd()) + nnoremap :bdelete + nnoremap :call FB_EditEntry() + nnoremap ? :call FB_ToggleHelp() + + " lock the user in this window + nnoremap +endfunction " }}} +" FB_SetSilentSettings: some settings which make things silent {{{ +" Description: +" Origin: from explorer.vim distributed with vim. +function! FB_SetSilentSettings() + let s:save_report=&report + let s:save_showcmd = &sc + set report=10000 noshowcmd +endfunction +" FB_ResetSilentSettings: reset settings set by FB_SetSilentSettings +" Description: +function! FB_ResetSilentSettings() + let &report=s:save_report + let &showcmd = s:save_showcmd +endfunction " }}} +" FB_SetScratchSettings: makes the present buffer a scratch buffer {{{ +" Description: +function! FB_SetScratchSettings() + " Turn off the swapfile, set the buffer type so that it won't get + " written, and so that it will get deleted when it gets hidden. + setlocal noreadonly modifiable + setlocal noswapfile + setlocal buftype=nowrite + setlocal bufhidden=delete + " Don't wrap around long lines + setlocal nowrap +endfunction + +" }}} +" FB_ToggleHelp: toggles verbosity of help {{{ +" Description: +function! FB_ToggleHelp() + let s:FB_VerboseHelp = 1 - s:FB_GetVar('FB_VerboseHelp', 0) + + call FB_DisplayFiles('.') +endfunction " }}} +" FB_DisplayHelp: displays a helpful header {{{ +" Description: +function! FB_DisplayHelp() + let verboseHelp = s:FB_GetVar('FB_VerboseHelp', 0) + if verboseHelp + let txt = + \ "\" : on file, choose the file and quit\n" + \ ."\" on dir, enter directory\n" + \ ."\" q/: quit without choosing\n" + \ ."\" C: change directory to getcwd()\n" + \ ."\" ?: toggle help verbosity\n" + \ ."\"= ".getcwd() + else + let txt = "\" ?: toggle help verbosity\n" + \ ."\"= ".getcwd() + endif + 0put!=txt +endfunction " }}} +" FB_EditEntry: handles the user pressing on a line {{{ +" Description: +function! FB_EditEntry() + let line = getline('.') + + if isdirectory(line) + call FB_DisplayFiles(line) + endif + + " If the user has a call back function defined on choosing a file, handle + " it. + let cbf = s:FB_GetVar('FB_CallBackFunction', '') + if cbf != '' && line !~ '^" ' && filereadable(line) + let fname = fnamemodify(line, ':p') + bdelete + + let arguments = s:FB_GetVar('FB_CallBackFunctionArgs', '') + if arguments != '' + let arguments = ','.arguments + endif + call Tex_Debug('arguments = '.arguments, 'fb') + call Tex_Debug("call ".cbf."('".fname."'".arguments.')', 'fb') + exec "call ".cbf."('".fname."'".arguments.')' + endif +endfunction " }}} +" FB_GetVar: gets the most local value of a variable {{{ +function! FB_GetVar(name, default) + if exists('s:'.a:name) + return s:{a:name} + elseif 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 a:default + endif +endfunction + +" }}} + +let &cpo = s:save_cpo + +" vim:fdm=marker:ff=unix:noet:ts=4:sw=4:nowrap diff --git a/vim-latex/plugin/imaps.vim b/vim-latex/plugin/imaps.vim new file mode 100644 index 0000000..28023be --- /dev/null +++ b/vim-latex/plugin/imaps.vim @@ -0,0 +1,831 @@ +" File: imaps.vim +" Authors: Srinath Avadhanula +" Benji Fisher +" +" 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}\\\item <++>\\\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 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 command to insert dynamic elements such as dates. +" call IMAP ('date`', "\=strftime('%b %d %Y')\", '') +" +" 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 = '' + end + exe 'inoremap ' + \ escape(lastLHSChar, '|') + \ '=LookupCharacter("' . + \ escape(lastLHSChar, '\|"') . + \ '")' +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), ".", "\", "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 , 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\\:call IMAP_Mark('set')\\"_s" + let text = text . template . final + let text = text . "\\:call IMAP_Mark('go')\" + let text = text . "i\=IMAP_Jumpfunc('', 1)\" + + 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 = "\v/\\V".pheUser."/e\".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\:".s:RemoveLastHistoryItem."\" + else + return movement."\\:".s:RemoveLastHistoryItem."\gv\" + endif + +endfunction + +" }}} +" Maps for IMAP_Jumpfunc {{{ +" +" These mappings use 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 IMAP_JumpForward +" etc. + +" jumping forward and back in insert mode. +imap IMAP_JumpForward =IMAP_Jumpfunc('', 0) +imap IMAP_JumpBack =IMAP_Jumpfunc('b', 0) + +" jumping in normal mode +nmap IMAP_JumpForward i=IMAP_Jumpfunc('', 0) +nmap IMAP_JumpBack i=IMAP_Jumpfunc('b', 0) + +" deleting the present selection and then jumping forward. +vmap IMAP_DeleteAndJumpForward "_i=IMAP_Jumpfunc('', 0) +vmap IMAP_DeleteAndJumpBack "_i=IMAP_Jumpfunc('b', 0) + +" jumping forward without deleting present selection. +vmap IMAP_JumpForward i=IMAP_Jumpfunc('', 0) +vmap IMAP_JumpBack `=IMAP_Jumpfunc('b', 0) + +" }}} +" 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('IMAP_JumpForward', 'i') + imap IMAP_JumpForward +endif +if !hasmapto('IMAP_JumpForward', 'n') + nmap IMAP_JumpForward +endif +if exists('g:Imap_StickyPlaceHolders') && g:Imap_StickyPlaceHolders + if !hasmapto('IMAP_JumpForward', 'v') + vmap IMAP_JumpForward + endif +else + if !hasmapto('IMAP_DeleteAndJumpForward', 'v') + vmap IMAP_DeleteAndJumpForward + endif +endif +" }}} + +nmap