" genutils.vim: Please see plugin/genutils.vim " " TODO: " - Vim7: redir can be used with a variable. " - fnamemodify() on Unix doesn't expand to full name if the filename doesn't " really exist on the filesystem. " - Is setting 'scrolloff' and 'sidescrolloff' to 0 required while moving the " cursor? " - http://www.vim.org/tips/tip.php?tip_id=1379 " " - EscapeCommand() didn't work for David Fishburn. " - Save/RestoreWindowSettings doesn't work well. " " Vim7: " - Save/RestoreWindowSettings can use winsave/restview() functions. " " Make sure line-continuations won't cause any problem. This will be restored " at the end let s:save_cpo = &cpo set cpo&vim let g:makeArgumentString = 'exec genutils#MakeArgumentString()' let g:makeArgumentList = 'exec genutils#MakeArgumentList()' let s:makeArgumentString = '' function! genutils#MakeArgumentString(...) if s:makeArgumentString == '' let s:makeArgumentString = genutils#ExtractFuncListing(s:SNR(). \ '_makeArgumentString', 0, 0) endif if a:0 > 0 && a:1 != '' return substitute(s:makeArgumentString, '\', a:1, 'g') else return s:makeArgumentString endif endfunction let s:makeArgumentList = '' function! genutils#MakeArgumentList(...) if s:makeArgumentList == '' let s:makeArgumentList = genutils#ExtractFuncListing(s:SNR(). \ '_makeArgumentList', 0, 0) endif if a:0 > 0 && a:1 != '' let mkArgLst = substitute(s:makeArgumentList, '\', a:1, 'g') if a:0 > 1 && a:2 != '' let mkArgLst = substitute(s:makeArgumentList, \ '\(\s\+let __argSeparator = \)[^'."\n".']*', "\\1'".a:2."'", '') endif return mkArgLst else return s:makeArgumentList endif endfunction function! genutils#ExtractFuncListing(funcName, hLines, tLines) let listing = genutils#GetVimCmdOutput('func '.a:funcName) let listing = substitute(listing, \ '^\%(\s\|'."\n".'\)*function '.a:funcName.'([^)]*)'."\n", '', '') "let listing = substitute(listing, '\%(\s\|'."\n".'\)*endfunction\%(\s\|'."\n".'\)*$', '', '') " Leave the last newline character. let listing = substitute(listing, '\%('."\n".'\)\@<=\s*endfunction\s*$', '', '') let listing = substitute(listing, '\(\%(^\|'."\n".'\)\s*\)\@<=\d\+', \ '', 'g') if a:hLines > 0 let listing = substitute(listing, '^\%([^'."\n".']*'."\n".'\)\{'. \ a:hLines.'}', '', '') endif if a:tLines > 0 let listing = substitute(listing, '\%([^'."\n".']*'."\n".'\)\{'. \ a:tLines.'}$', '', '') endif return listing endfunction function! genutils#CreateArgString(argList, sep, ...) let sep = (a:0 == 0) ? a:sep : a:1 " This is no longer used. " Matching multvals functionality means, we need to ignore the trailing " separator. let argList = split(substitute(a:argList, a:sep.'$', '', ''), a:sep, 1) let argString = "'" for nextArg in argList " FIXME: I think this check is not required. If "'" is the separator, we " don't expect to see them in the elements. if a:sep != "'" let nextArg = substitute(nextArg, "'", "' . \"'\" . '", 'g') endif let argString = argString . nextArg . "', '" endfor let argString = strpart(argString, 0, strlen(argString) - 3) return argString endfunction " {{{ function! s:_makeArgumentString() let __argCounter = 1 let argumentString = '' while __argCounter <= a:0 if type(a:{__argCounter}) let __nextArg = "'" . \ substitute(a:{__argCounter}, "'", "' . \"'\" . '", "g") . "'" else let __nextArg = a:{__argCounter} endif let argumentString = argumentString. __nextArg . \ ((__argCounter == a:0) ? '' : ', ') let __argCounter = __argCounter + 1 endwhile unlet __argCounter if exists('__nextArg') unlet __nextArg endif endfunction function! s:_makeArgumentList() let __argCounter = 1 let __argSeparator = ',' let argumentList = '' while __argCounter <= a:0 let argumentList = argumentList . a:{__argCounter} if __argCounter != a:0 let argumentList = argumentList . __argSeparator endif let __argCounter = __argCounter + 1 endwhile unlet __argCounter unlet __argSeparator endfunction " }}} function! genutils#DebugShowArgs(...) let i = 0 let argString = '' while i < a:0 let argString = argString . a:{i + 1} . ', ' let i = i + 1 endwhile let argString = strpart(argString, 0, strlen(argString) - 2) call input("Args: " . argString) endfunction " Window related functions {{{ function! genutils#NumberOfWindows() let i = 1 while winbufnr(i) != -1 let i = i+1 endwhile return i - 1 endfunction " Find the window number for the buffer passed. " The fileName argument is treated literally, unlike the bufnr() which treats " the argument as a regex pattern. function! genutils#FindWindowForBuffer(bufferName, checkUnlisted) return bufwinnr(genutils#FindBufferForName(a:bufferName)) endfunction function! genutils#FindBufferForName(fileName) " The name could be having extra backslashes to protect certain chars (such " as '#' and '%'), so first expand them. return s:FindBufferForName(genutils#UnEscape(a:fileName, '#%')) endfunction function! s:FindBufferForName(fileName) let fileName = genutils#Escape(a:fileName, '[?,{') let _isf = &isfname try set isfname-=\ set isfname-=[ let i = bufnr('^' . fileName . '$') finally let &isfname = _isf endtry return i endfunction function! genutils#GetBufNameForAu(bufName) let bufName = a:bufName " Autocommands always require forward-slashes. let bufName = substitute(bufName, "\\\\", '/', 'g') let bufName = escape(bufName, '*?,{}[ ') return bufName endfunction function! genutils#MoveCursorToWindow(winno) if genutils#NumberOfWindows() != 1 execute a:winno . " wincmd w" endif endfunction function! genutils#MoveCurLineToWinLine(n) normal! zt if a:n == 1 return endif let _wrap = &l:wrap setl nowrap let n = a:n if n >= winheight(0) let n = winheight(0) endif let n = n - 1 execute "normal! " . n . "\" let &l:wrap = _wrap endfunction function! genutils#CloseWindow(win, force) let _eventignore = &eventignore try set eventignore=all call genutils#MarkActiveWindow() let &eventignore = _eventignore exec a:win 'wincmd w' exec 'close'.(a:force ? '!' : '') set eventignore=all if a:win < t:curWinnr let t:curWinnr = t:curWinnr - 1 endif if a:win < t:prevWinnr let t:prevWinnr = t:prevWinnr - 1 endif finally call genutils#RestoreActiveWindow() let &eventignore = _eventignore endtry endfunction function! genutils#MarkActiveWindow() let t:curWinnr = winnr() " We need to restore the previous-window also at the end. silent! wincmd p let t:prevWinnr = winnr() silent! wincmd p endfunction function! genutils#RestoreActiveWindow() if !exists('t:curWinnr') return endif " Restore the original window. if winnr() != t:curWinnr exec t:curWinnr'wincmd w' endif if t:curWinnr != t:prevWinnr exec t:prevWinnr'wincmd w' wincmd p endif endfunction function! genutils#IsOnlyVerticalWindow() let onlyVertWin = 1 let _eventignore = &eventignore try "set eventignore+=WinEnter,WinLeave set eventignore=all call genutils#MarkActiveWindow() wincmd j if winnr() != t:curWinnr let onlyVertWin = 0 else wincmd k if winnr() != t:curWinnr let onlyVertWin = 0 endif endif finally call genutils#RestoreActiveWindow() let &eventignore = _eventignore endtry return onlyVertWin endfunction function! genutils#IsOnlyHorizontalWindow() let onlyHorizWin = 1 let _eventignore = &eventignore try set eventignore=all call genutils#MarkActiveWindow() wincmd l if winnr() != t:curWinnr let onlyHorizWin = 0 else wincmd h if winnr() != t:curWinnr let onlyHorizWin = 0 endif endif finally call genutils#RestoreActiveWindow() let &eventignore = _eventignore endtry return onlyHorizWin endfunction function! genutils#MoveCursorToNextInWinStack(dir) let newwin = genutils#GetNextWinnrInStack(a:dir) if newwin != 0 exec newwin 'wincmd w' endif endfunction function! genutils#GetNextWinnrInStack(dir) let newwin = winnr() let _eventignore = &eventignore try set eventignore=all call genutils#MarkActiveWindow() let newwin = s:GetNextWinnrInStack(a:dir) finally call genutils#RestoreActiveWindow() let &eventignore = _eventignore endtry return newwin endfunction function! genutils#MoveCursorToLastInWinStack(dir) let newwin = genutils#GetLastWinnrInStack(a:dir) if newwin != 0 exec newwin 'wincmd w' endif endfunction function! genutils#GetLastWinnrInStack(dir) let newwin = winnr() let _eventignore = &eventignore try set eventignore=all call genutils#MarkActiveWindow() while 1 let wn = s:GetNextWinnrInStack(a:dir) if wn != 0 let newwin = wn exec newwin 'wincmd w' else break endif endwhile finally call genutils#RestoreActiveWindow() let &eventignore = _eventignore endtry return newwin endfunction " Based on the WinStackMv() function posted by Charles E. Campbell, Jr. on vim " mailing list on Jul 14, 2004. function! s:GetNextWinnrInStack(dir) "call Decho("genutils#MoveCursorToNextInWinStack(dir<".a:dir.">)") let isHorizontalMov = (a:dir ==# 'h' || a:dir ==# 'l') ? 1 : 0 let orgwin = winnr() let orgdim = s:GetWinDim(a:dir, orgwin) let _winwidth = &winwidth let _winheight = &winheight try set winwidth=1 set winheight=1 exec 'wincmd' a:dir let newwin = winnr() if orgwin == newwin " No more windows in this direction. "call Decho("newwin=".newwin." stopped".winheight(newwin)."x".winwidth(newwin)) return 0 endif if s:GetWinDim(a:dir, newwin) != orgdim " Window dimension has changed, indicates a move across window stacks. "call Decho("newwin=".newwin." height changed".winheight(newwin)."x".winwidth(newwin)) return 0 endif " Determine if changing original window height affects current window " height. exec orgwin 'wincmd w' try if orgdim == 1 exec 'wincmd' (isHorizontalMov ? '_' : '|') else exec 'wincmd' (isHorizontalMov ? '-' : '<') endif if s:GetWinDim(a:dir, newwin) != s:GetWinDim(a:dir, orgwin) "call Decho("newwin=".newwin." different row".winheight(newwin)."x".winwidth(newwin)) return 0 endif "call Decho("newwin=".newwin." same row".winheight(newwin)."x".winwidth(newwin)) finally exec (isHorizontalMov ? '' : 'vert') 'resize' orgdim endtry "call Decho("genutils#MoveCursorToNextInWinStack") return newwin finally let &winwidth = _winwidth let &winheight = _winheight endtry endfunction function! s:GetWinDim(dir, win) return (a:dir ==# 'h' || a:dir ==# 'l') ? winheight(a:win) : winwidth(a:win) endfunction function! genutils#OpenWinNoEa(winOpenCmd) call s:ExecWinCmdNoEa(a:winOpenCmd) endfunction function! genutils#CloseWinNoEa(winnr, force) call s:ExecWinCmdNoEa(a:winnr.'wincmd w | close'.(a:force?'!':'')) endfunction function! s:ExecWinCmdNoEa(winCmd) let _eventignore = &eventignore try set eventignore=all call genutils#MarkActiveWindow() windo let w:_winfixheight = &winfixheight windo set winfixheight call genutils#RestoreActiveWindow() let &eventignore = _eventignore exec a:winCmd set eventignore=all call genutils#MarkActiveWindow() silent! windo let &winfixheight = w:_winfixheight silent! windo unlet w:_winfixheight call genutils#RestoreActiveWindow() finally let &eventignore = _eventignore endtry endfunction " Window related functions }}} function! genutils#SetupScratchBuffer() setlocal nobuflisted setlocal noswapfile setlocal buftype=nofile " Just in case, this will make sure we are always hidden. setlocal bufhidden=delete setlocal nolist setlocal nonumber setlocal foldcolumn=0 nofoldenable setlocal noreadonly endfunction function! genutils#CleanDiffOptions() setlocal nodiff setlocal noscrollbind setlocal scrollopt-=hor setlocal wrap setlocal foldmethod=manual setlocal foldcolumn=0 normal zE endfunction function! genutils#ArrayVarExists(varName, index) try exec "let test = " . a:varName . "{a:index}" catch /^Vim\%((\a\+)\)\=:E121/ return 0 endtry return 1 endfunction function! genutils#Escape(str, chars) return substitute(a:str, '\\\@ 0 let pipe = join(a:pipe, ' ') elseif type(a:pipe) == 1 && a:pipe !~# '^\s*$' let pipe = a:pipe endif if pipe != '' let fullCmd = fullCmd . ' ' . a:pipe endif if a:cmd !~# '^\s*$' let fullCmd = a:cmd . ' ' . fullCmd endif if shellEnvType ==# g:ST_WIN_SH && &shell =~# '\' let fullCmd = substitute(fullCmd, '\\', '\\\\', 'g') endif return fullCmd endfunction let g:ST_WIN_CMD = 0 | let g:ST_WIN_SH = 1 | let g:ST_UNIX = 2 function! genutils#GetShellEnvType() " When 'shellslash' option is available, then the platform must be one of " those that support '\' as a pathsep. if exists('+shellslash') if stridx(&shell, 'cmd.exe') != -1 || \ stridx(&shell, 'command.com') != -1 return g:ST_WIN_CMD else return g:ST_WIN_SH endif else return g:ST_UNIX endif endfunction function! genutils#ExpandStr(str) let str = substitute(a:str, '"', '\\"', 'g') exec "let str = \"" . str . "\"" return str endfunction function! genutils#QuoteStr(str) return "'".substitute(a:str, "'", "'.\"'\".'", 'g')."'" endfunction function! genutils#GetPreviewWinnr() let _eventignore = &eventignore let curWinNr = winnr() let winnr = -1 try set eventignore=all exec "wincmd P" let winnr = winnr() catch /^Vim\%((\a\+)\)\=:E441/ " Ignore, winnr is already set to -1. finally if winnr() != curWinNr exec curWinNr.'wincmd w' endif let &eventignore = _eventignore endtry return winnr endfunction " Save/Restore window settings {{{ function! genutils#SaveWindowSettings() call genutils#SaveWindowSettings2('SaveWindowSettings', 1) endfunction function! genutils#RestoreWindowSettings() call genutils#RestoreWindowSettings2('SaveWindowSettings') endfunction function! genutils#ResetWindowSettings() call genutils#ResetWindowSettings2('SaveWindowSettings') endfunction function! genutils#SaveWindowSettings2(id, overwrite) if genutils#ArrayVarExists("t:winSettings", a:id) && ! a:overwrite return endif let t:winSettings{a:id} = [] call add(t:winSettings{a:id}, genutils#NumberOfWindows()) call add(t:winSettings{a:id}, &lines) call add(t:winSettings{a:id}, &columns) call add(t:winSettings{a:id}, winnr()) let i = 1 while winbufnr(i) != -1 call add(t:winSettings{a:id}, winheight(i)) call add(t:winSettings{a:id}, winwidth(i)) let i = i + 1 endwhile "let g:savedWindowSettings = t:winSettings{a:id} " Debug. endfunction function! genutils#RestoreWindowSettings2(id) " Calling twice fixes most of the resizing issues. This seems to be how the " :mksession with "winsize" in 'sesionoptions' seems to work. call s:RestoreWindowSettings2(a:id) call s:RestoreWindowSettings2(a:id) endfunction function! s:RestoreWindowSettings2(id) "if ! exists("t:winSettings" . a:id) if ! genutils#ArrayVarExists("t:winSettings", a:id) return endif let nWindows = t:winSettings{a:id}[0] if nWindows != genutils#NumberOfWindows() unlet t:winSettings{a:id} return endif let orgLines = t:winSettings{a:id}[1] let orgCols = t:winSettings{a:id}[2] let activeWindow = t:winSettings{a:id}[3] let mainWinSizeSame = (orgLines == &lines && orgCols == &columns) let winNo = 1 let i = 4 while i < len(t:winSettings{a:id}) let height = t:winSettings{a:id}[i] let width = t:winSettings{a:id}[i+1] let height = (mainWinSizeSame ? height : \ ((&lines * height + (orgLines / 2)) / orgLines)) let width = (mainWinSizeSame ? width : \ ((&columns * width + (orgCols / 2)) / orgCols)) if winheight(winnr()) != height exec winNo'resize' height endif if winwidth(winnr()) != width exec 'vert' winNo 'resize' width endif let winNo = winNo + 1 let i = i + 2 endwhile " Restore the current window. call genutils#MoveCursorToWindow(activeWindow) "unlet g:savedWindowSettings endfunction function! genutils#ResetWindowSettings2(id) if genutils#ArrayVarExists("t:winSettings", a:id) unlet t:winSettings{a:id} endif endfunction " Save/Restore window settings }}} " Save/Restore selection {{{ function! genutils#SaveVisualSelection(id) let curmode = mode() if curmode == 'n' normal! gv endif let s:vismode{a:id} = mode() let s:firstline{a:id} = line("'<") let s:lastline{a:id} = line("'>") let s:firstcol{a:id} = col("'<") let s:lastcol{a:id} = col("'>") if curmode !=# s:vismode{a:id} exec "normal \" endif endfunction function! genutils#RestoreVisualSelection(id) if mode() !=# 'n' exec "normal \" endif if exists('s:vismode{id}') exec 'normal' s:firstline{a:id}.'gg'.s:firstcol{a:id}.'|'. \ s:vismode{a:id}.(s:lastline{a:id}-s:firstline{a:id}).'j'. \ (s:lastcol{a:id}-s:firstcol{a:id}).'l' endif endfunction " Save/Restore selection }}} function! genutils#CleanupFileName(fileName) return genutils#CleanupFileName2(a:fileName, '') endfunction function! genutils#CleanupFileName2(fileName, win32ProtectedChars) let fileName = substitute(a:fileName, '^\s\+\|\s\+$', '', 'g') " Expand relative paths and paths containing relative components (takes care " of ~ also). if ! genutils#PathIsAbsolute(fileName) let fileName = fnamemodify(fileName, ':p') endif " I think we can have UNC paths on UNIX, if samba is installed. if genutils#OnMS() && (match(fileName, '^//') == 0 || \ match(fileName, '^\\\\') == 0) let uncPath = 1 else let uncPath = 0 endif " Remove multiple path separators. if has('win32') if a:win32ProtectedChars != '' let fileName=substitute(fileName, '\\['.a:win32ProtectedChars.']\@!', '/', \ 'g') else let fileName=substitute(fileName, '\\', '/', 'g') endif elseif genutils#OnMS() " On non-win32 systems, the forward-slash is not supported, so leave " back-slash. let fileName=substitute(fileName, '\\\{2,}', '\', 'g') endif let fileName=substitute(fileName, '/\{2,}', '/', 'g') " Remove ending extra path separators. let fileName=substitute(fileName, '/$', '', '') let fileName=substitute(fileName, '\\$', '', '') " If it was an UNC path, add back an extra slash. if uncPath let fileName = '/'.fileName endif if genutils#OnMS() let fileName=substitute(fileName, '^[A-Z]:', '\L&', '') " Add drive letter if missing (just in case). if !uncPath && match(fileName, '^/') == 0 let curDrive = substitute(getcwd(), '^\([a-zA-Z]:\).*$', '\L\1', '') let fileName = curDrive . fileName endif endif return fileName endfunction "echo genutils#CleanupFileName('\\a///b/c\') "echo genutils#CleanupFileName('C:\a/b/c\d') "echo genutils#CleanupFileName('a/b/c\d') "echo genutils#CleanupFileName('~/a/b/c\d') "echo genutils#CleanupFileName('~/a/b/../c\d') function! genutils#OnMS() return has('win32') || has('dos32') || has('win16') || has('dos16') || \ has('win95') endfunction function! genutils#PathIsAbsolute(path) let absolute=0 if has('unix') || genutils#OnMS() if match(a:path, '^/') == 0 let absolute=1 endif endif if (! absolute) && genutils#OnMS() if match(a:path, "^\\") == 0 let absolute=1 endif endif if (! absolute) && genutils#OnMS() if match(a:path, "^[A-Za-z]:") == 0 let absolute=1 endif endif return absolute endfunction function! genutils#PathIsFileNameOnly(path) return (match(a:path, "\\") < 0) && (match(a:path, "/") < 0) endfunction let s:mySNR = '' function! s:SNR() if s:mySNR == '' let s:mySNR = matchstr(expand(''), '\d\+_\zeSNR$') endif return s:mySNR endfun "" --- START save/restore position. {{{ function! genutils#SaveSoftPosition(id) let b:sp_startline_{a:id} = getline(".") call genutils#SaveHardPosition(a:id) endfunction function! genutils#RestoreSoftPosition(id) 0 call genutils#RestoreHardPosition(a:id) let stLine = b:sp_startline_{a:id} if getline('.') !=# stLine if ! search('\V\^'.escape(stLine, "\\").'\$', 'W') call search('\V\^'.escape(stLine, "\\").'\$', 'bW') endif endif call genutils#MoveCurLineToWinLine(b:sp_winline_{a:id}) endfunction function! genutils#ResetSoftPosition(id) unlet b:sp_startline_{a:id} endfunction " A synonym for genutils#SaveSoftPosition. function! genutils#SaveHardPositionWithContext(id) call genutils#SaveSoftPosition(a:id) endfunction " A synonym for genutils#RestoreSoftPosition. function! genutils#RestoreHardPositionWithContext(id) call genutils#RestoreSoftPosition(a:id) endfunction " A synonym for genutils#ResetSoftPosition. function! genutils#ResetHardPositionWithContext(id) call genutils#ResetSoftPosition(a:id) endfunction function! genutils#SaveHardPosition(id) let b:sp_col_{a:id} = virtcol(".") let b:sp_lin_{a:id} = line(".") " Avoid accounting for wrapped lines. let _wrap = &l:wrap try setl nowrap let b:sp_winline_{a:id} = winline() finally let &l:wrap = _wrap endtry endfunction function! genutils#RestoreHardPosition(id) " This doesn't take virtual column. "call cursor(b:sp_lin_{a:id}, b:sp_col_{a:id}) " Vim7 generates E16 if line number is invalid. " TODO: Why is this leaving cursor on the last-but-one line when the " condition meets? execute ((line('$') < b:sp_lin_{a:id}) ? line('$') : \ b:sp_lin_{a:id}) "execute b:sp_lin_{a:id} execute ((line('$') < b:sp_lin_{a:id}) ? line('$') : \ b:sp_lin_{a:id}) "execute b:sp_lin_{a:id} execute "normal!" b:sp_col_{a:id} . "|" call genutils#MoveCurLineToWinLine(b:sp_winline_{a:id}) endfunction function! genutils#ResetHardPosition(id) unlet b:sp_col_{a:id} unlet b:sp_lin_{a:id} unlet b:sp_winline_{a:id} endfunction function! genutils#GetLinePosition(id) return b:sp_lin_{a:id} endfunction function! genutils#GetColPosition(id) return b:sp_col_{a:id} endfunction function! genutils#IsPositionSet(id) return exists('b:sp_col_' . a:id) endfunction "" --- END save/restore position. }}} "" "" --- START: Notify window close -- {{{ "" let s:notifyWindow = {} function! genutils#AddNotifyWindowClose(windowTitle, functionName) let bufName = a:windowTitle let s:notifyWindow[bufName] = a:functionName "let g:notifyWindow = s:notifyWindow " Debug. " Start listening to events. aug NotifyWindowClose au! au WinEnter * :call genutils#CheckWindowClose() au BufEnter * :call genutils#CheckWindowClose() aug END endfunction function! genutils#RemoveNotifyWindowClose(windowTitle) let bufName = a:windowTitle if has_key(s:notifyWindow, bufName) call remove(s:notifyWindow, bufName) if len(s:notifyWindow) == 0 "unlet g:notifyWindow " Debug. aug NotifyWindowClose au! aug END endif endif endfunction function! genutils#CheckWindowClose() if !exists('s:notifyWindow') return endif " Now iterate over all the registered window titles and see which one's are " closed. for nextWin in keys(s:notifyWindow) if bufwinnr(s:FindBufferForName(nextWin)) == -1 let funcName = s:notifyWindow[nextWin] " Remove this entry as these are going to be processed. The caller can add " them back if needed. unlet s:notifyWindow[nextWin] "call input("cmd: " . cmd) call call(funcName, [nextWin]) endif endfor endfunction "function! NotifyWindowCloseF(title) " call input(a:title . " closed") "endfunction " "function! RunNotifyWindowCloseTest() " call input("Creating three windows, 'ABC', 'XYZ' and 'b':") " split ABC " split X Y Z " split b " redraw " call genutils#AddNotifyWindowClose("ABC", "NotifyWindowCloseF") " call genutils#AddNotifyWindowClose("X Y Z", "NotifyWindowCloseF") " call input("notifyWindow: " . string(s:notifyWindow)) " au NotifyWindowClose WinEnter " call input("Added notifications for 'ABC' and 'XYZ'\n". " \ "Now closing the windows, you should see ONLY two notifications:") " quit " quit " quit "endfunction "" "" --- END: Notify window close -- }}} "" " TODO: For large ranges, the cmd can become too big, so make it one cmd per " line. function! genutils#ShowLinesWithSyntax() range " This makes sure we start (subsequent) echo's on the first line in the " command-line area. " echo '' let cmd = '' let prev_group = ' x ' " Something that won't match any syntax group. let show_line = a:firstline let isMultiLine = ((a:lastline - a:firstline) > 1) while show_line <= a:lastline let cmd = '' let length = strlen(getline(show_line)) let column = 1 while column <= length let group = synIDattr(synID(show_line, column, 1), 'name') if group != prev_group if cmd != '' let cmd = cmd . "'|" endif let cmd = cmd . 'echohl ' . (group == '' ? 'NONE' : group) . "|echon '" let prev_group = group endif let char = strpart(getline(show_line), column - 1, 1) if char == "'" let char = "'."'".'" endif let cmd = cmd . char let column = column + 1 endwhile try exec cmd."\n'" catch echo '' endtry let show_line = show_line + 1 endwhile echohl NONE endfunction function! genutils#AlignWordWithWordInPreviousLine() "First go to the first col in the word. if getline('.')[col('.') - 1] =~ '\s' normal! w else normal! "_yiw endif let orgVcol = virtcol('.') let prevLnum = prevnonblank(line('.') - 1) if prevLnum == -1 return endif let prevLine = getline(prevLnum) " First get the column to align with. if prevLine[orgVcol - 1] =~ '\s' " column starts from 1 where as index starts from 0. let nonSpaceStrInd = orgVcol " column starts from 1 where as index starts from 0. while prevLine[nonSpaceStrInd] =~ '\s' let nonSpaceStrInd = nonSpaceStrInd + 1 endwhile else if strlen(prevLine) < orgVcol let nonSpaceStrInd = strlen(prevLine) - 1 else let nonSpaceStrInd = orgVcol - 1 endif while prevLine[nonSpaceStrInd - 1] !~ '\s' && nonSpaceStrInd > 0 let nonSpaceStrInd = nonSpaceStrInd - 1 endwhile endif let newVcol = nonSpaceStrInd + 1 " Convert to column number. if orgVcol > newVcol " We need to reduce the spacing. let sub = strpart(getline('.'), newVcol - 1, (orgVcol - newVcol)) if sub =~ '^\s\+$' " Remove the excess space. exec 'normal! ' . newVcol . '|' exec 'normal! ' . (orgVcol - newVcol) . 'x' endif elseif orgVcol < newVcol " We need to insert spacing. exec 'normal! ' . orgVcol . '|' exec 'normal! ' . (newVcol - orgVcol) . 'i ' endif endfunction function! genutils#ShiftWordInSpace(dir) if a:dir == 1 " forward. " If currently on ... if getline(".")[col(".") - 1] == " " let move1 = 'wf ' else " If next col is a "if getline(".")[col(".") + 1] let move1 = 'f ' endif let removeCommand = "x" let pasteCommand = "bi " let move2 = 'w' let offset = 0 else " backward. " If currently on ... if getline(".")[col(".") - 1] == " " let move1 = 'w' else let move1 = '"_yiW' endif let removeCommand = "hx" let pasteCommand = 'h"_yiwEa ' let move2 = 'b' let offset = -3 endif let savedCol = col(".") exec "normal!" move1 let curCol = col(".") let possible = 0 " Check if there is a space at the end. if col("$") == (curCol + 1) " Works only for forward case, as expected. let possible = 1 elseif getline(".")[curCol + offset] == " " " Remove the space from here. exec "normal!" removeCommand let possible = 1 endif " Move back into the word. "exec "normal!" savedCol . "|" if possible == 1 exec "normal!" pasteCommand exec "normal!" move2 else " Move to the original location. exec "normal!" savedCol . "|" endif endfunction function! genutils#CenterWordInSpace() let line = getline('.') let orgCol = col('.') " If currently on ... if line[orgCol - 1] == " " let matchExpr = ' *\%'. orgCol . 'c *\w\+ \+' else let matchExpr = ' \+\(\w*\%' . orgCol . 'c\w*\) \+' endif let matchInd = match(line, matchExpr) if matchInd == -1 return endif let matchStr = matchstr(line, matchExpr) let nSpaces = strlen(substitute(matchStr, '[^ ]', '', 'g')) let word = substitute(matchStr, ' ', '', 'g') let middle = nSpaces / 2 let left = nSpaces - middle let newStr = '' while middle > 0 let newStr = newStr . ' ' let middle = middle - 1 endwhile let newStr = newStr . word while left > 0 let newStr = newStr . ' ' let left = left - 1 endwhile let newLine = strpart(line, 0, matchInd) let newLine = newLine . newStr let newLine = newLine . strpart (line, matchInd + strlen(matchStr)) silent! keepjumps call setline(line('.'), newLine) endfunction function! genutils#MapAppendCascaded(lhs, rhs, mapMode) " Determine the map mode from the map command. let mapChar = strpart(a:mapMode, 0, 1) " Check if this is already mapped. let oldrhs = maparg(a:lhs, mapChar) if oldrhs != "" let self = oldrhs else let self = a:lhs endif "echomsg a:mapMode . "oremap" . " " . a:lhs . " " . self . a:rhs exec a:mapMode . "oremap" a:lhs self . a:rhs endfunction " smartSlash simply says whether to depend on shellslash and ArgLead for " determining path separator. If it shouldn't depend, it will always assume " that the required pathsep is forward-slash. function! genutils#UserFileComplete(ArgLead, CmdLine, CursorPos, smartSlash, \ searchPath) let glob = '' let opathsep = "\\" let npathsep = '/' if exists('+shellslash') && ! &shellslash && a:smartSlash && \ stridx(a:ArgLead, "\\") != -1 let opathsep = '/' let npathsep = "\\" endif if a:searchPath !=# '' for nextPath in split(a:searchPath, genutils#CrUnProtectedCharsPattern(',')) let nextPath = genutils#CleanupFileName(nextPath) let matches = glob(nextPath.'/'.a:ArgLead.'*') if matches !~# '^\_s*$' let matches = s:FixPathSep(matches, opathsep, npathsep) let nextPath = substitute(nextPath, opathsep, npathsep, 'g') let matches = substitute(matches, '\V'.escape(nextPath.npathsep, "\\"), \ '', 'g') let glob = glob . matches . "\n" endif endfor else let glob = s:FixPathSep(glob(a:ArgLead.'*'), opathsep, npathsep) endif " FIXME: Need an option to control if ArgLead should also be returned or " not. return glob."\n".a:ArgLead endfunction command! -complete=file -nargs=* GUDebugEcho :echo function! genutils#UserFileExpand(fileArgs) return substitute(genutils#GetVimCmdOutput( \ 'GUDebugEcho ' . a:fileArgs), '^\_s\+\|\_s\+$', '', 'g') endfunction function! s:FixPathSep(matches, opathsep, npathsep) let matches = a:matches let matches = substitute(matches, a:opathsep, a:npathsep, 'g') let matches = substitute(matches, "\\([^\n]\\+\\)", '\=submatch(1).'. \ '(isdirectory(submatch(1)) ? a:npathsep : "")', 'g') return matches endfunction function! genutils#GetVimCmdOutput(cmd) let v:errmsg = '' let output = '' let _z = @z let _shortmess = &shortmess try set shortmess= redir @z silent exec a:cmd catch /.*/ let v:errmsg = substitute(v:exception, '^[^:]\+:', '', '') finally redir END let &shortmess = _shortmess if v:errmsg == '' let output = @z endif let @z = _z endtry return output endfunction function! genutils#OptClearBuffer() " Go as far as possible in the undo history to conserve Vim resources. let _modifiable = &l:modifiable let _undolevels = &undolevels try setl modifiable set undolevels=-1 silent! keepjumps 0,$delete _ finally let &undolevels = _undolevels let &l:modifiable = _modifiable endtry endfunction "" START: Sorting support. {{{ "" " " Comapare functions. " function! genutils#CmpByLineLengthNname(line1, line2, ...) let direction = (a:0?a:1:1) let cmp = genutils#CmpByLength(a:line1, a:line2, direction) if cmp == 0 let cmp = genutils#CmpByString(a:line1, a:line2, direction) endif return cmp endfunction function! genutils#CmpByLength(line1, line2, ...) let direction = (a:0?a:1:1) let len1 = strlen(a:line1) let len2 = strlen(a:line2) return direction * (len1 - len2) endfunction " Compare first by name and then by length. " Useful for sorting Java imports. function! genutils#CmpJavaImports(line1, line2, ...) let direction = (a:0?a:1:1) " FIXME: Simplify this. if stridx(a:line1, '.') == -1 let pkg1 = '' let cls1 = substitute(a:line1, '.* \(^[ ]\+\)', '\1', '') else let pkg1 = substitute(a:line1, '.*import\s\+\(.*\)\.[^. ;]\+.*$', '\1', '') let cls1 = substitute(a:line1, '^.*\.\([^. ;]\+\).*$', '\1', '') endif if stridx(a:line2, '.') == -1 let pkg2 = '' let cls2 = substitute(a:line2, '.* \(^[ ]\+\)', '\1', '') else let pkg2 = substitute(a:line2, '.*import\s\+\(.*\)\.[^. ;]\+.*$', '\1', '') let cls2 = substitute(a:line2, '^.*\.\([^. ;]\+\).*$', '\1', '') endif let cmp = genutils#CmpByString(pkg1, pkg2, direction) if cmp == 0 let cmp = genutils#CmpByLength(cls1, cls2, direction) endif return cmp endfunction function! genutils#CmpByString(line1, line2, ...) let direction = (a:0?a:1:1) if a:line1 < a:line2 return -direction elseif a:line1 > a:line2 return direction else return 0 endif endfunction function! genutils#CmpByStringIgnoreCase(line1, line2, ...) let direction = (a:0?a:1:1) if a:line1 ? a:line2 return direction else return 0 endif endfunction function! genutils#CmpByNumber(line1, line2, ...) let direction = (a:0 ? a:1 :1) let num1 = a:line1 + 0 let num2 = a:line2 + 0 if num1 < num2 return -direction elseif num1 > num2 return direction else return 0 endif endfunction function! genutils#QSort(cmp, direction) range call s:QSortR(a:firstline, a:lastline, a:cmp, a:direction, \ 's:BufLineAccessor', 's:BufLineSwapper', '') endfunction function! genutils#QSort2(start, end, cmp, direction, accessor, swapper, context) call s:QSortR(a:start, a:end, a:cmp, a:direction, a:accessor, a:swapper, \ a:context) endfunction " The default swapper that swaps lines in the current buffer. function! s:BufLineSwapper(line1, line2, context) let str2 = getline(a:line1) silent! keepjumps call setline(a:line1, getline(a:line2)) silent! keepjumps call setline(a:line2, str2) endfunction " The default accessor that returns lines from the current buffer. function! s:BufLineAccessor(line, context) return getline(a:line) endfunction " The default mover that moves lines from one place to another in the current " buffer. function! s:BufLineMover(from, to, context) let line = getline(a:from) exec a:from.'d_' call append(a:to, line) endfunction " " Sort lines. QSortR() is called recursively. " function! s:QSortR(start, end, cmp, direction, accessor, swapper, context) if a:end > a:start let low = a:start let high = a:end " Arbitrarily establish partition element at the midpoint of the data. let midStr = {a:accessor}(((a:start + a:end) / 2), a:context) " Loop through the data until indices cross. while low <= high " Find the first element that is greater than or equal to the partition " element starting from the left Index. while low < a:end let result = {a:cmp}({a:accessor}(low, a:context), midStr, a:direction) if result < 0 let low = low + 1 else break endif endwhile " Find an element that is smaller than or equal to the partition element " starting from the right Index. while high > a:start let result = {a:cmp}({a:accessor}(high, a:context), midStr, a:direction) if result > 0 let high = high - 1 else break endif endwhile " If the indexes have not crossed, swap. if low <= high " Swap lines low and high. call {a:swapper}(high, low, a:context) let low = low + 1 let high = high - 1 endif endwhile " If the right index has not reached the left side of data must now sort " the left partition. if a:start < high call s:QSortR(a:start, high, a:cmp, a:direction, a:accessor, a:swapper, \ a:context) endif " If the left index has not reached the right side of data must now sort " the right partition. if low < a:end call s:QSortR(low, a:end, a:cmp, a:direction, a:accessor, a:swapper, \ a:context) endif endif endfunction function! genutils#BinSearchForInsert(start, end, line, cmp, direction) return genutils#BinSearchForInsert2(a:start, a:end, a:line, a:cmp, \ a:direction, 's:BufLineAccessor', '') endfunction function! genutils#BinSearchForInsert2(start, end, line, cmp, direction, \ accessor, context) let start = a:start - 1 let end = a:end while start < end let middle = (start + end + 1) / 2 " Support passing both Funcref's as well as names. if type(a:cmp) == 2 if type(a:accessor) == 2 let result = a:cmp(a:accessor(middle, a:context), a:line, a:direction) else let result = a:cmp({a:accessor}(middle, a:context), a:line, a:direction) endif else if type(a:accessor) == 2 let result = {a:cmp}(a:accessor(middle, a:context), a:line, a:direction) else let result = {a:cmp}({a:accessor}(middle, a:context), a:line, a:direction) endif endif if result < 0 let start = middle else let end = middle - 1 endif endwhile return start endfunction function! genutils#BinSearchList(list, start, end, item, cmp) let start = a:start - 1 let end = a:end while start < end let middle = (start + end + 1) / 2 let result = call(a:cmp, [get(a:list, middle), a:item]) if result < 0 let start = middle else let end = middle - 1 endif endwhile return start endfunction function! genutils#BinInsertSort(cmp, direction) range call genutils#BinInsertSort2(a:firstline, a:lastline, a:cmp, a:direction, \ 's:BufLineAccessor', 's:BufLineMover', '') endfunction function! genutils#BinInsertSort2(start, end, cmp, direction, accessor, mover, context) let i = a:start + 1 while i <= a:end let low = s:BinSearchToAppend2(a:start, i, {a:accessor}(i, a:context), \ a:cmp, a:direction, a:accessor, a:context) " Move the object. if low < i call {a:mover}(i, low - 1, a:context) endif let i = i + 1 endwhile endfunction function! s:BinSearchToAppend(start, end, line, cmp, direction) return s:BinSearchToAppend2(a:start, a:end, a:line, a:cmp, a:direction, \ 's:BufLineAccessor', '') endfunction function! s:BinSearchToAppend2(start, end, line, cmp, direction, accessor, \ context) let low = a:start let high = a:end while low < high let mid = (low + high) / 2 let diff = {a:cmp}({a:accessor}(mid, a:context), a:line, a:direction) if diff > 0 let high = mid else let low = mid + 1 if diff == 0 break endif endif endwhile return low endfunction """ END: Sorting support. }}} " Eats character if it matches the given pattern. " " Originally, " From: Benji Fisher " Date: Mon, 25 Mar 2002 15:05:14 -0500 " " Based on Bram's idea of eating a character while type to expand an " abbreviation. This solves the problem with abbreviations, where we are " left with an extra space after the expansion. " Ex: " inoreabbr \stdout\ System.out.println("");=genutils#EatChar('\s') function! genutils#EatChar(pat) let c = nr2char(getchar()) "call input('Pattern: '.a:pat.' '. " \ ((c =~ a:pat) ? 'Returning empty' : 'Returning: '.char2nr(c))) return (c =~ a:pat) ? '' : c endfun " Can return a spacer from 0 to 80 characters width. let s:spacer= " ". \ " " function! genutils#GetSpacer(width) return strpart(s:spacer, 0, a:width) endfunction function! genutils#SilentSubstitute(pat, cmd) call genutils#SaveHardPosition('SilentSubstitute') let _search = @/ try let @/ = a:pat keepjumps silent! exec a:cmd finally let @/ = _search call genutils#RestoreHardPosition('SilentSubstitute') call genutils#ResetHardPosition('SilentSubstitute') endtry endfunction function! genutils#SilentDelete(arg1, ...) " For backwards compatibility. if a:0 let range = a:arg1 let pat = a:1 else let range = '' let pat = a:arg1 endif let _search = @/ call genutils#SaveHardPosition('SilentDelete') try let @/ = pat keepjumps silent! exec range'g//d _' finally let @/ = _search call genutils#RestoreHardPosition('SilentDelete') call genutils#ResetHardPosition('SilentDelete') endtry endfunction " START: genutils#Roman2Decimal {{{ let s:I = 1 let s:V = 5 let s:X = 10 let s:L = 50 let s:C = 100 let s:D = 500 let s:M = 1000 function! s:Char2Num(c) " A bit of magic on empty strings if a:c == "" return 0 endif exec 'let n = s:' . toupper(a:c) return n endfun function! genutils#Roman2Decimal(str) if a:str !~? '^[IVXLCDM]\+$' return a:str endif let sum = 0 let i = 0 let n0 = s:Char2Num(a:str[i]) let len = strlen(a:str) while i < len let i = i + 1 let n1 = s:Char2Num(a:str[i]) " Magic: n1=0 when i exceeds len if n1 > n0 let sum = sum - n0 else let sum = sum + n0 endif let n0 = n1 endwhile return sum endfun " END: genutils#Roman2Decimal }}} " BEGIN: Relative path {{{ function! genutils#CommonPath(path1, path2, ...) let path1 = genutils#CleanupFileName(a:path1) . ((a:0 > 0 ? a:1 : 0) ? '/' : '') let path2 = genutils#CleanupFileName(a:path2) . ((a:0 > 1 ? a:2 : 0) ? '/' : '') return genutils#CommonString(path1, path2) endfunction function! genutils#CommonString(str1, str2) let str1 = a:str1 let str2 = a:str2 if str1 == str2 return str1 endif let n = 0 while str1[n] == str2[n] let n = n+1 endwhile return strpart(str1, 0, n) endfunction function! genutils#RelPathFromFile(srcFile, tgtFile) return genutils#RelPathFromDir(fnamemodify(a:srcFile, ':h'), a:tgtFile) endfunction function! genutils#RelPathFromDir(srcDir, tgtFile) let cleanDir = genutils#CleanupFileName(a:srcDir).'/' let cleanFile = genutils#CleanupFileName(a:tgtFile) let cmnPath = genutils#CommonPath(cleanDir, cleanFile, 1) let relDirFromCmnPath = strpart(cleanDir, strlen(cmnPath)) if relDirFromCmnPath == '/' " This means the file is under the srcDir. let relDirFromCmnPath = '' endif let relFileFromCmnPath = strpart(cleanFile, strlen(cmnPath)) return substitute(relDirFromCmnPath, '[^/]\+', '..', 'g') . \ relFileFromCmnPath endfunction " END: Relative path }}} " BEGIN: Persistent settings {{{ if ! exists("g:genutilsNoPersist") || ! g:genutilsNoPersist " Make sure the '!' option to store global variables that are upper cased are " stored in viminfo file. " Make sure it is the first option, so that it will not interfere with the " 'n' option ("Todd J. Cosgrove" (todd dot cosgrove at softechnics dot " com)). " Also take care of empty value, when 'compatible' mode is on (Bram " Moolenar) if &viminfo == '' set viminfo=!,'20,"50,h else set viminfo^=! endif endif function! genutils#PutPersistentVar(pluginName, persistentVar, value) if ! exists("g:genutilsNoPersist") || ! g:genutilsNoPersist let globalVarName = s:PersistentVarName(a:pluginName, a:persistentVar) exec 'let ' . globalVarName . " = '" . a:value . "'" endif endfunction function! genutils#GetPersistentVar(pluginName, persistentVar, default) if ! exists("g:genutilsNoPersist") || ! g:genutilsNoPersist let globalVarName = s:PersistentVarName(a:pluginName, a:persistentVar) if (exists(globalVarName)) exec 'let value = ' . globalVarName exec 'unlet ' . globalVarName else let value = a:default endif return value else return default endif endfunction function! s:PersistentVarName(pluginName, persistentVar) return 'g:GU_' . toupper(a:pluginName) . '_' . toupper(a:persistentVar) endfunction " END: Persistent settings }}} " FileChangedShell handling {{{ if !exists('s:fcShellPreFuncs') let s:fcShellPreFuncs = {} endif function! genutils#AddToFCShellPre(funcName) let s:fcShellPreFuncs[a:funcName] = a:funcName endfunction function! genutils#RemoveFromFCShellPre(funcName) if has_key(s:fcShellPreFuncs, a:funcName) unlet s:fcShellPreFuncs[a:funcName] endif endfunction let s:defFCShellInstalled = 0 function! genutils#DefFCShellInstall() if ! s:defFCShellInstalled aug DefFCShell au! au FileChangedShell * nested call genutils#DefFileChangedShell() aug END endif let s:defFCShellInstalled = s:defFCShellInstalled + 1 endfunction function! genutils#DefFCShellUninstall() if s:defFCShellInstalled <= 0 return endif let s:defFCShellInstalled = s:defFCShellInstalled - 1 if ! s:defFCShellInstalled aug DefFCShell au! aug END endif endfunction function! genutils#DefFileChangedShell() let autoread = s:InvokeFuncs(s:fcShellPreFuncs) let bufNo = expand('') + 0 let fName = expand('') let msg = '' let v:fcs_choice = '' if getbufvar(bufNo, '&modified') let v:fcs_choice = 'ask' elseif ! autoread let v:fcs_choice = 'ask' else let v:fcs_choice = 'reload' endif return endfunction function! s:InvokeFuncs(funcList) let autoread = &autoread if len(a:funcList) != 0 for nextFunc in keys(a:funcList) let result = call(nextFunc, []) if result != -1 let autoread = autoread || result endif endfor endif return autoread endfunction " FileChangedShell handling }}} " Sign related utilities {{{ function! genutils#CurLineHasSign() let signs = genutils#GetVimCmdOutput('sign place buffer=' . bufnr('%'), 1) return (match(signs, \ 'line=' . line('.') . '\s\+id=\d\+\s\+name=VimBreakPt') != -1) endfunction function! genutils#ClearAllSigns() let signs = genutils#GetVimCmdOutput('sign place buffer=' . bufnr('%'), 1) let curIdx = 0 let pat = 'line=\d\+\s\+id=\zs\d\+\ze\s\+name=VimBreakPt' let id = 0 while curIdx != -1 && curIdx < strlen(signs) let id = matchstr(signs, pat, curIdx) if id != '' exec 'sign unplace ' . id . ' buffer=' . bufnr('%') endif let curIdx = matchend(signs, pat, curIdx) endwhile endfunction " }}} let s:UNPROTECTED_CHAR_PRFX = '\%(\%([^\\]\|^\)\\\%(\\\\\)*\)\@ 0?1:0) let regex = s:UNPROTECTED_CHAR_PRFX let chars = a:chars if strlen(chars) > 1 let chars = '['.chars.']' endif if capture let chars = '\('.chars.'\)' endif return regex.chars endfunction function! genutils#PromptForElement(array, default, msg, skip, useDialog, \ nCols) let nCols = a:nCols let index = 0 let line = "" let element = "" let optionsMsg = "" let colWidth = &columns / nCols - 1 " Leave a margin of one column as a gap. let curCol = 1 let nElements = len(a:array) let newArray = [] " Without the skip element. if index(a:array, a:skip) != -1 let nElements = nElements - 1 endif for element in a:array if element ==# a:skip continue endif call add(newArray, element) let element = strpart(index." ", 0, 4) . element let eleColWidth = (strlen(element) - 1) / colWidth + 1 " Fill up the spacer for the rest of the partial column. let element = element . genutils#GetSpacer( \ eleColWidth * (colWidth + 1) - strlen(element) - 1) let wouldBeLength = strlen(line) + strlen(element) + 1 if wouldBeLength > (curCol * (colWidth + eleColWidth)) && \ wouldBeLength > &columns let splitLine = 2 " Split before adding the new element. elseif curCol == nCols let splitLine = 1 " Split after adding the new element. else let splitLine = 0 endif if splitLine == 2 if strlen(line) == &columns " Remove the last space as it otherwise results in an extra empty line " on the screen. let line = strpart(line, 0, strlen(line) - 1) endif let optionsMsg = optionsMsg . line . "\n" let line = element . ' ' let curCol = strlen(element) / (colWidth + 1) else let line = line . element . ' ' if splitLine == 1 if strlen(line) == &columns " Remove the last space as it otherwise results in an extra empty line " on the screen. let line = strpart(line, 0, strlen(line) - 1) endif let curCol = 0 " Reset col count. let optionsMsg = optionsMsg . line . "\n" let line = "" endif endif let curCol = curCol + 1 let index = index + 1 endfor " Finally if there is anything left in line, then append that too. if line.'' != '' let optionsMsg = optionsMsg . line . "\n" let line = "" endif " Default index or empty string. let default = '' if type(a:default) == 0 let default = a:default elseif a:default.'' != '' let default = index(a:array, a:default) endif if a:default == -1 let default = '' endif while !exists("selectedElement") if a:useDialog let s:selection = inputdialog(optionsMsg . a:msg, default) else let s:selection = input(optionsMsg . a:msg, default) endif if s:selection.'' == '' let selectedElement = '' let s:selection = -1 else let s:selection = (s:selection !~# '^\d\+$') ? -1 : (s:selection + 0) if s:selection >= 0 && s:selection < nElements let selectedElement = newArray[s:selection] else echohl ERROR | echo "\nInvalid selection, please try again" | \ echohl NONE endif endif echo "\n" endwhile return selectedElement endfunction let s:selection = -1 function! genutils#GetSelectedIndex() return s:selection endfunction " Always match() with 'ignorecase' and 'smartcase' off. function! s:Match(expr, pat, start) let _ic = &ignorecase let _scs = &smartcase let result = -1 try set noignorecase set nosmartcase let result = match(a:expr, a:pat, a:start) finally let &ignorecase = _ic let &smartcase = _scs endtry return result endfunction " Restore cpo. let &cpo = s:save_cpo unlet s:save_cpo " vim6:fdm=marker et