From ad4d8ddf70e450c199c91b7055397d82f5affd55 Mon Sep 17 00:00:00 2001 From: "Mike D. Lowis" Date: Sat, 11 Feb 2012 22:06:55 -0500 Subject: [PATCH] Initial Commit --- after/plugin/snipMate.vim | 35 + autoload/acp.vim | 431 +++ autoload/snipMate.vim | 433 +++ doc/EasyGrep.txt | 580 +++++ doc/NERD_commenter.txt | 988 +++++++ doc/NERD_tree.txt | 1222 +++++++++ doc/acp.jax | 298 +++ doc/acp.txt | 512 ++++ doc/bufexplorer.txt | 513 ++++ doc/conque_term.txt | 645 +++++ doc/snipMate.txt | 286 ++ doc/tags | 287 ++ doc/tags-ja | 44 + ftdetect/clojure.vim | 1 + ftplugin/html_snip_helper.vim | 10 + nerdtree_plugin/exec_menuitem.vim | 41 + nerdtree_plugin/fs_menu.vim | 194 ++ plugin/NERD_commenter.vim | 2790 ++++++++++++++++++++ plugin/NERD_tree.vim | 4059 +++++++++++++++++++++++++++++ plugin/acp.vim | 170 ++ plugin/bufrestore.vim | 38 + plugin/cctree.vim | 3272 +++++++++++++++++++++++ plugin/cscopeloader.vim | 101 + plugin/kwbd.vim | 64 + plugin/minibufexpl.vim | 1838 +++++++++++++ plugin/snipMate.vim | 247 ++ plugin/tagloader.vim | 73 + snippets/_.snippets | 7 + snippets/c.snippets | 214 ++ snippets/snippet.snippets | 7 + syntax/clojure.vim | 261 ++ syntax/snippet.vim | 19 + 32 files changed, 19680 insertions(+) create mode 100644 after/plugin/snipMate.vim create mode 100644 autoload/acp.vim create mode 100644 autoload/snipMate.vim create mode 100644 doc/EasyGrep.txt create mode 100644 doc/NERD_commenter.txt create mode 100644 doc/NERD_tree.txt create mode 100644 doc/acp.jax create mode 100644 doc/acp.txt create mode 100644 doc/bufexplorer.txt create mode 100644 doc/conque_term.txt create mode 100644 doc/snipMate.txt create mode 100644 doc/tags create mode 100644 doc/tags-ja create mode 100644 ftdetect/clojure.vim create mode 100644 ftplugin/html_snip_helper.vim create mode 100644 nerdtree_plugin/exec_menuitem.vim create mode 100644 nerdtree_plugin/fs_menu.vim create mode 100644 plugin/NERD_commenter.vim create mode 100644 plugin/NERD_tree.vim create mode 100644 plugin/acp.vim create mode 100644 plugin/bufrestore.vim create mode 100644 plugin/cctree.vim create mode 100644 plugin/cscopeloader.vim create mode 100644 plugin/kwbd.vim create mode 100644 plugin/minibufexpl.vim create mode 100644 plugin/snipMate.vim create mode 100644 plugin/tagloader.vim create mode 100644 snippets/_.snippets create mode 100644 snippets/c.snippets create mode 100644 snippets/snippet.snippets create mode 100644 syntax/clojure.vim create mode 100644 syntax/snippet.vim diff --git a/after/plugin/snipMate.vim b/after/plugin/snipMate.vim new file mode 100644 index 0000000..03e79ae --- /dev/null +++ b/after/plugin/snipMate.vim @@ -0,0 +1,35 @@ +" These are the mappings for snipMate.vim. Putting it here ensures that it +" will be mapped after other plugins such as supertab.vim. +if !exists('loaded_snips') || exists('s:did_snips_mappings') + finish +endif +let s:did_snips_mappings = 1 + +ino =TriggerSnippet() +snor i=TriggerSnippet() +ino =BackwardsSnippet() +snor i=BackwardsSnippet() +ino =ShowAvailableSnips() + +" The default mappings for these are annoying & sometimes break snipMate. +" You can change them back if you want, I've put them here for convenience. +snor b +snor a +snor bi +snor ' b' +snor ` b` +snor % b% +snor U bU +snor ^ b^ +snor \ b\ +snor b + +" By default load snippets in snippets_dir +if empty(snippets_dir) + finish +endif + +call GetSnippets(snippets_dir, '_') " Get global snippets + +au FileType * if &ft != 'help' | call GetSnippets(snippets_dir, &ft) | endif +" vim:noet:sw=4:ts=4:ft=vim diff --git a/autoload/acp.vim b/autoload/acp.vim new file mode 100644 index 0000000..827bbcc --- /dev/null +++ b/autoload/acp.vim @@ -0,0 +1,431 @@ +"============================================================================= +" Copyright (c) 2007-2009 Takeshi NISHIDA +" +"============================================================================= +" LOAD GUARD {{{1 + +if exists('g:loaded_autoload_acp') || v:version < 702 + finish +endif +let g:loaded_autoload_acp = 1 + +" }}}1 +"============================================================================= +" GLOBAL FUNCTIONS: {{{1 + +" +function acp#enable() + call acp#disable() + + augroup AcpGlobalAutoCommand + autocmd! + autocmd InsertEnter * unlet! s:posLast s:lastUncompletable + autocmd InsertLeave * call s:finishPopup(1) + augroup END + + if g:acp_mappingDriven + call s:mapForMappingDriven() + else + autocmd AcpGlobalAutoCommand CursorMovedI * call s:feedPopup() + endif + + nnoremap i i=feedPopup() + nnoremap a a=feedPopup() + nnoremap R R=feedPopup() +endfunction + +" +function acp#disable() + call s:unmapForMappingDriven() + augroup AcpGlobalAutoCommand + autocmd! + augroup END + nnoremap i | nunmap i + nnoremap a | nunmap a + nnoremap R | nunmap R +endfunction + +" +function acp#lock() + let s:lockCount += 1 +endfunction + +" +function acp#unlock() + let s:lockCount -= 1 + if s:lockCount < 0 + let s:lockCount = 0 + throw "AutoComplPop: not locked" + endif +endfunction + +" +function acp#meetsForSnipmate(context) + if g:acp_behaviorSnipmateLength < 0 + return 0 + endif + let matches = matchlist(a:context, '\(^\|\s\|\<\)\(\u\{' . + \ g:acp_behaviorSnipmateLength . ',}\)$') + return !empty(matches) && !empty(s:getMatchingSnipItems(matches[2])) +endfunction + +" +function acp#meetsForKeyword(context) + if g:acp_behaviorKeywordLength < 0 + return 0 + endif + let matches = matchlist(a:context, '\(\k\{' . g:acp_behaviorKeywordLength . ',}\)$') + if empty(matches) + return 0 + endif + for ignore in g:acp_behaviorKeywordIgnores + if stridx(ignore, matches[1]) == 0 + return 0 + endif + endfor + return 1 +endfunction + +" +function acp#meetsForFile(context) + if g:acp_behaviorFileLength < 0 + return 0 + endif + if has('win32') || has('win64') + let separator = '[/\\]' + else + let separator = '\/' + endif + if a:context !~ '\f' . separator . '\f\{' . g:acp_behaviorFileLength . ',}$' + return 0 + endif + return a:context !~ '[*/\\][/\\]\f*$\|[^[:print:]]\f*$' +endfunction + +" +function acp#meetsForRubyOmni(context) + if !has('ruby') + return 0 + endif + if g:acp_behaviorRubyOmniMethodLength >= 0 && + \ a:context =~ '[^. \t]\(\.\|::\)\k\{' . + \ g:acp_behaviorRubyOmniMethodLength . ',}$' + return 1 + endif + if g:acp_behaviorRubyOmniSymbolLength >= 0 && + \ a:context =~ '\(^\|[^:]\):\k\{' . + \ g:acp_behaviorRubyOmniSymbolLength . ',}$' + return 1 + endif + return 0 +endfunction + +" +function acp#meetsForPythonOmni(context) + return has('python') && g:acp_behaviorPythonOmniLength >= 0 && + \ a:context =~ '\k\.\k\{' . g:acp_behaviorPythonOmniLength . ',}$' +endfunction + +" +function acp#meetsForPerlOmni(context) + return g:acp_behaviorPerlOmniLength >= 0 && + \ a:context =~ '\w->\k\{' . g:acp_behaviorPerlOmniLength . ',}$' +endfunction + +" +function acp#meetsForXmlOmni(context) + return g:acp_behaviorXmlOmniLength >= 0 && + \ a:context =~ '\(<\|<\/\|<[^>]\+ \|<[^>]\+=\"\)\k\{' . + \ g:acp_behaviorXmlOmniLength . ',}$' +endfunction + +" +function acp#meetsForHtmlOmni(context) + return g:acp_behaviorHtmlOmniLength >= 0 && + \ a:context =~ '\(<\|<\/\|<[^>]\+ \|<[^>]\+=\"\)\k\{' . + \ g:acp_behaviorHtmlOmniLength . ',}$' +endfunction + +" +function acp#meetsForCssOmni(context) + if g:acp_behaviorCssOmniPropertyLength >= 0 && + \ a:context =~ '\(^\s\|[;{]\)\s*\k\{' . + \ g:acp_behaviorCssOmniPropertyLength . ',}$' + return 1 + endif + if g:acp_behaviorCssOmniValueLength >= 0 && + \ a:context =~ '[:@!]\s*\k\{' . + \ g:acp_behaviorCssOmniValueLength . ',}$' + return 1 + endif + return 0 +endfunction + +" +function acp#completeSnipmate(findstart, base) + if a:findstart + let s:posSnipmateCompletion = len(matchstr(s:getCurrentText(), '.*\U')) + return s:posSnipmateCompletion + endif + let lenBase = len(a:base) + let items = filter(GetSnipsInCurrentScope(), + \ 'strpart(v:key, 0, lenBase) ==? a:base') + return map(sort(items(items)), 's:makeSnipmateItem(v:val[0], v:val[1])') +endfunction + +" +function acp#onPopupCloseSnipmate() + let word = s:getCurrentText()[s:posSnipmateCompletion :] + for trigger in keys(GetSnipsInCurrentScope()) + if word ==# trigger + call feedkeys("\=TriggerSnippet()\", "n") + return 0 + endif + endfor + return 1 +endfunction + +" +function acp#onPopupPost() + " to clear = expression on command-line + echo '' + if pumvisible() + inoremap acp#onBs() + inoremap acp#onBs() + " a command to restore to original text and select the first match + return (s:behavsCurrent[s:iBehavs].command =~# "\" ? "\\" + \ : "\\") + endif + let s:iBehavs += 1 + if len(s:behavsCurrent) > s:iBehavs + call s:setCompletefunc() + return printf("\%s\=acp#onPopupPost()\", + \ s:behavsCurrent[s:iBehavs].command) + else + let s:lastUncompletable = { + \ 'word': s:getCurrentWord(), + \ 'commands': map(copy(s:behavsCurrent), 'v:val.command')[1:], + \ } + call s:finishPopup(0) + return "\" + endif +endfunction + +" +function acp#onBs() + " using "matchstr" and not "strpart" in order to handle multi-byte + " characters + if call(s:behavsCurrent[s:iBehavs].meets, + \ [matchstr(s:getCurrentText(), '.*\ze.')]) + return "\" + endif + return "\\" +endfunction + +" }}}1 +"============================================================================= +" LOCAL FUNCTIONS: {{{1 + +" +function s:mapForMappingDriven() + call s:unmapForMappingDriven() + let s:keysMappingDriven = [ + \ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + \ 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + \ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + \ 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + \ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + \ '-', '_', '~', '^', '.', ',', ':', '!', '#', '=', '%', '$', '@', '<', '>', '/', '\', + \ '', '', '', ] + for key in s:keysMappingDriven + execute printf('inoremap %s %s=feedPopup()', + \ key, key) + endfor +endfunction + +" +function s:unmapForMappingDriven() + if !exists('s:keysMappingDriven') + return + endif + for key in s:keysMappingDriven + execute 'iunmap ' . key + endfor + let s:keysMappingDriven = [] +endfunction + +" +function s:setTempOption(group, name, value) + call extend(s:tempOptionSet[a:group], { a:name : eval('&' . a:name) }, 'keep') + execute printf('let &%s = a:value', a:name) +endfunction + +" +function s:restoreTempOptions(group) + for [name, value] in items(s:tempOptionSet[a:group]) + execute printf('let &%s = value', name) + endfor + let s:tempOptionSet[a:group] = {} +endfunction + +" +function s:getCurrentWord() + return matchstr(s:getCurrentText(), '\k*$') +endfunction + +" +function s:getCurrentText() + return strpart(getline('.'), 0, col('.') - 1) +endfunction + +" +function s:getPostText() + return strpart(getline('.'), col('.') - 1) +endfunction + +" +function s:isModifiedSinceLastCall() + if exists('s:posLast') + let posPrev = s:posLast + let nLinesPrev = s:nLinesLast + let textPrev = s:textLast + endif + let s:posLast = getpos('.') + let s:nLinesLast = line('$') + let s:textLast = getline('.') + if !exists('posPrev') + return 1 + elseif posPrev[1] != s:posLast[1] || nLinesPrev != s:nLinesLast + return (posPrev[1] - s:posLast[1] == nLinesPrev - s:nLinesLast) + elseif textPrev ==# s:textLast + return 0 + elseif posPrev[2] > s:posLast[2] + return 1 + elseif has('gui_running') && has('multi_byte') + " NOTE: auto-popup causes a strange behavior when IME/XIM is working + return posPrev[2] + 1 == s:posLast[2] + endif + return posPrev[2] != s:posLast[2] +endfunction + +" +function s:makeCurrentBehaviorSet() + let modified = s:isModifiedSinceLastCall() + if exists('s:behavsCurrent[s:iBehavs].repeat') && s:behavsCurrent[s:iBehavs].repeat + let behavs = [ s:behavsCurrent[s:iBehavs] ] + elseif exists('s:behavsCurrent[s:iBehavs]') + return [] + elseif modified + let behavs = copy(exists('g:acp_behavior[&filetype]') + \ ? g:acp_behavior[&filetype] + \ : g:acp_behavior['*']) + else + return [] + endif + let text = s:getCurrentText() + call filter(behavs, 'call(v:val.meets, [text])') + let s:iBehavs = 0 + if exists('s:lastUncompletable') && + \ stridx(s:getCurrentWord(), s:lastUncompletable.word) == 0 && + \ map(copy(behavs), 'v:val.command') ==# s:lastUncompletable.commands + let behavs = [] + else + unlet! s:lastUncompletable + endif + return behavs +endfunction + +" +function s:feedPopup() + " NOTE: CursorMovedI is not triggered while the popup menu is visible. And + " it will be triggered when popup menu is disappeared. + if s:lockCount > 0 || pumvisible() || &paste + return '' + endif + if exists('s:behavsCurrent[s:iBehavs].onPopupClose') + if !call(s:behavsCurrent[s:iBehavs].onPopupClose, []) + call s:finishPopup(1) + return '' + endif + endif + let s:behavsCurrent = s:makeCurrentBehaviorSet() + if empty(s:behavsCurrent) + call s:finishPopup(1) + return '' + endif + " In case of dividing words by symbols (e.g. "for(int", "ab==cd") while a + " popup menu is visible, another popup is not available unless input + " or try popup once. So first completion is duplicated. + call insert(s:behavsCurrent, s:behavsCurrent[s:iBehavs]) + call s:setTempOption(s:GROUP0, 'spell', 0) + call s:setTempOption(s:GROUP0, 'completeopt', 'menuone' . (g:acp_completeoptPreview ? ',preview' : '')) + call s:setTempOption(s:GROUP0, 'complete', g:acp_completeOption) + call s:setTempOption(s:GROUP0, 'ignorecase', g:acp_ignorecaseOption) + " NOTE: With CursorMovedI driven, Set 'lazyredraw' to avoid flickering. + " With Mapping driven, set 'nolazyredraw' to make a popup menu visible. + call s:setTempOption(s:GROUP0, 'lazyredraw', !g:acp_mappingDriven) + " NOTE: 'textwidth' must be restored after . + call s:setTempOption(s:GROUP1, 'textwidth', 0) + call s:setCompletefunc() + call feedkeys(s:behavsCurrent[s:iBehavs].command . "\=acp#onPopupPost()\", 'n') + return '' " this function is called by = +endfunction + +" +function s:finishPopup(fGroup1) + inoremap | iunmap + inoremap | iunmap + let s:behavsCurrent = [] + call s:restoreTempOptions(s:GROUP0) + if a:fGroup1 + call s:restoreTempOptions(s:GROUP1) + endif +endfunction + +" +function s:setCompletefunc() + if exists('s:behavsCurrent[s:iBehavs].completefunc') + call s:setTempOption(0, 'completefunc', s:behavsCurrent[s:iBehavs].completefunc) + endif +endfunction + +" +function s:makeSnipmateItem(key, snip) + if type(a:snip) == type([]) + let descriptions = map(copy(a:snip), 'v:val[0]') + let snipFormatted = '[MULTI] ' . join(descriptions, ', ') + else + let snipFormatted = substitute(a:snip, '\(\n\|\s\)\+', ' ', 'g') + endif + return { + \ 'word': a:key, + \ 'menu': strpart(snipFormatted, 0, 80), + \ } +endfunction + +" +function s:getMatchingSnipItems(base) + let key = a:base . "\n" + if !exists('s:snipItems[key]') + let s:snipItems[key] = items(GetSnipsInCurrentScope()) + call filter(s:snipItems[key], 'strpart(v:val[0], 0, len(a:base)) ==? a:base') + call map(s:snipItems[key], 's:makeSnipmateItem(v:val[0], v:val[1])') + endif + return s:snipItems[key] +endfunction + +" }}}1 +"============================================================================= +" INITIALIZATION {{{1 + +let s:GROUP0 = 0 +let s:GROUP1 = 1 +let s:lockCount = 0 +let s:behavsCurrent = [] +let s:iBehavs = 0 +let s:tempOptionSet = [{}, {}] +let s:snipItems = {} + +" }}}1 +"============================================================================= +" vim: set fdm=marker: diff --git a/autoload/snipMate.vim b/autoload/snipMate.vim new file mode 100644 index 0000000..dcd28f6 --- /dev/null +++ b/autoload/snipMate.vim @@ -0,0 +1,433 @@ +fun! Filename(...) + let filename = expand('%:t:r') + if filename == '' | return a:0 == 2 ? a:2 : '' | endif + return !a:0 || a:1 == '' ? filename : substitute(a:1, '$1', filename, 'g') +endf + +fun s:RemoveSnippet() + unl! g:snipPos s:curPos s:snipLen s:endCol s:endLine s:prevLen + \ s:lastBuf s:oldWord + if exists('s:update') + unl s:startCol s:origWordLen s:update + if exists('s:oldVars') | unl s:oldVars s:oldEndCol | endif + endif + aug! snipMateAutocmds +endf + +fun snipMate#expandSnip(snip, col) + let lnum = line('.') | let col = a:col + + let snippet = s:ProcessSnippet(a:snip) + " Avoid error if eval evaluates to nothing + if snippet == '' | return '' | endif + + " Expand snippet onto current position with the tab stops removed + let snipLines = split(substitute(snippet, '$\d\+\|${\d\+.\{-}}', '', 'g'), "\n", 1) + + let line = getline(lnum) + let afterCursor = strpart(line, col - 1) + " Keep text after the cursor + if afterCursor != "\t" && afterCursor != ' ' + let line = strpart(line, 0, col - 1) + let snipLines[-1] .= afterCursor + else + let afterCursor = '' + " For some reason the cursor needs to move one right after this + if line != '' && col == 1 && &ve != 'all' && &ve != 'onemore' + let col += 1 + endif + endif + + call setline(lnum, line.snipLines[0]) + + " Autoindent snippet according to previous indentation + let indent = matchend(line, '^.\{-}\ze\(\S\|$\)') + 1 + call append(lnum, map(snipLines[1:], "'".strpart(line, 0, indent - 1)."'.v:val")) + + " Open any folds snippet expands into + if &fen | sil! exe lnum.','.(lnum + len(snipLines) - 1).'foldopen' | endif + + let [g:snipPos, s:snipLen] = s:BuildTabStops(snippet, lnum, col - indent, indent) + + if s:snipLen + aug snipMateAutocmds + au CursorMovedI * call s:UpdateChangedSnip(0) + au InsertEnter * call s:UpdateChangedSnip(1) + aug END + let s:lastBuf = bufnr(0) " Only expand snippet while in current buffer + let s:curPos = 0 + let s:endCol = g:snipPos[s:curPos][1] + let s:endLine = g:snipPos[s:curPos][0] + + call cursor(g:snipPos[s:curPos][0], g:snipPos[s:curPos][1]) + let s:prevLen = [line('$'), col('$')] + if g:snipPos[s:curPos][2] != -1 | return s:SelectWord() | endif + else + unl g:snipPos s:snipLen + " Place cursor at end of snippet if no tab stop is given + let newlines = len(snipLines) - 1 + call cursor(lnum + newlines, indent + len(snipLines[-1]) - len(afterCursor) + \ + (newlines ? 0: col - 1)) + endif + return '' +endf + +" Prepare snippet to be processed by s:BuildTabStops +fun s:ProcessSnippet(snip) + let snippet = a:snip + " Evaluate eval (`...`) expressions. + " Using a loop here instead of a regex fixes a bug with nested "\=". + if stridx(snippet, '`') != -1 + while match(snippet, '`.\{-}`') != -1 + let snippet = substitute(snippet, '`.\{-}`', + \ substitute(eval(matchstr(snippet, '`\zs.\{-}\ze`')), + \ "\n\\%$", '', ''), '') + endw + let snippet = substitute(snippet, "\r", "\n", 'g') + endif + + " Place all text after a colon in a tab stop after the tab stop + " (e.g. "${#:foo}" becomes "${:foo}foo"). + " This helps tell the position of the tab stops later. + let snippet = substitute(snippet, '${\d\+:\(.\{-}\)}', '&\1', 'g') + + " Update the a:snip so that all the $# become the text after + " the colon in their associated ${#}. + " (e.g. "${1:foo}" turns all "$1"'s into "foo") + let i = 1 + while stridx(snippet, '${'.i) != -1 + let s = matchstr(snippet, '${'.i.':\zs.\{-}\ze}') + if s != '' + let snippet = substitute(snippet, '$'.i, s.'&', 'g') + endif + let i += 1 + endw + + if &et " Expand tabs to spaces if 'expandtab' is set. + return substitute(snippet, '\t', repeat(' ', &sts ? &sts : &sw), 'g') + endif + return snippet +endf + +" Counts occurences of haystack in needle +fun s:Count(haystack, needle) + let counter = 0 + let index = stridx(a:haystack, a:needle) + while index != -1 + let index = stridx(a:haystack, a:needle, index+1) + let counter += 1 + endw + return counter +endf + +" Builds a list of a list of each tab stop in the snippet containing: +" 1.) The tab stop's line number. +" 2.) The tab stop's column number +" (by getting the length of the string between the last "\n" and the +" tab stop). +" 3.) The length of the text after the colon for the current tab stop +" (e.g. "${1:foo}" would return 3). If there is no text, -1 is returned. +" 4.) If the "${#:}" construct is given, another list containing all +" the matches of "$#", to be replaced with the placeholder. This list is +" composed the same way as the parent; the first item is the line number, +" and the second is the column. +fun s:BuildTabStops(snip, lnum, col, indent) + let snipPos = [] + let i = 1 + let withoutVars = substitute(a:snip, '$\d\+', '', 'g') + while stridx(a:snip, '${'.i) != -1 + let beforeTabStop = matchstr(withoutVars, '^.*\ze${'.i.'\D') + let withoutOthers = substitute(withoutVars, '${\('.i.'\D\)\@!\d\+.\{-}}', '', 'g') + + let j = i - 1 + call add(snipPos, [0, 0, -1]) + let snipPos[j][0] = a:lnum + s:Count(beforeTabStop, "\n") + let snipPos[j][1] = a:indent + len(matchstr(withoutOthers, '.*\(\n\|^\)\zs.*\ze${'.i.'\D')) + if snipPos[j][0] == a:lnum | let snipPos[j][1] += a:col | endif + + " Get all $# matches in another list, if ${#:name} is given + if stridx(withoutVars, '${'.i.':') != -1 + let snipPos[j][2] = len(matchstr(withoutVars, '${'.i.':\zs.\{-}\ze}')) + let dots = repeat('.', snipPos[j][2]) + call add(snipPos[j], []) + let withoutOthers = substitute(a:snip, '${\d\+.\{-}}\|$'.i.'\@!\d\+', '', 'g') + while match(withoutOthers, '$'.i.'\(\D\|$\)') != -1 + let beforeMark = matchstr(withoutOthers, '^.\{-}\ze'.dots.'$'.i.'\(\D\|$\)') + call add(snipPos[j][3], [0, 0]) + let snipPos[j][3][-1][0] = a:lnum + s:Count(beforeMark, "\n") + let snipPos[j][3][-1][1] = a:indent + (snipPos[j][3][-1][0] > a:lnum + \ ? len(matchstr(beforeMark, '.*\n\zs.*')) + \ : a:col + len(beforeMark)) + let withoutOthers = substitute(withoutOthers, '$'.i.'\ze\(\D\|$\)', '', '') + endw + endif + let i += 1 + endw + return [snipPos, i - 1] +endf + +fun snipMate#jumpTabStop(backwards) + let leftPlaceholder = exists('s:origWordLen') + \ && s:origWordLen != g:snipPos[s:curPos][2] + if leftPlaceholder && exists('s:oldEndCol') + let startPlaceholder = s:oldEndCol + 1 + endif + + if exists('s:update') + call s:UpdatePlaceholderTabStops() + else + call s:UpdateTabStops() + endif + + " Don't reselect placeholder if it has been modified + if leftPlaceholder && g:snipPos[s:curPos][2] != -1 + if exists('startPlaceholder') + let g:snipPos[s:curPos][1] = startPlaceholder + else + let g:snipPos[s:curPos][1] = col('.') + let g:snipPos[s:curPos][2] = 0 + endif + endif + + let s:curPos += a:backwards ? -1 : 1 + " Loop over the snippet when going backwards from the beginning + if s:curPos < 0 | let s:curPos = s:snipLen - 1 | endif + + if s:curPos == s:snipLen + let sMode = s:endCol == g:snipPos[s:curPos-1][1]+g:snipPos[s:curPos-1][2] + call s:RemoveSnippet() + return sMode ? "\" : TriggerSnippet() + endif + + call cursor(g:snipPos[s:curPos][0], g:snipPos[s:curPos][1]) + + let s:endLine = g:snipPos[s:curPos][0] + let s:endCol = g:snipPos[s:curPos][1] + let s:prevLen = [line('$'), col('$')] + + return g:snipPos[s:curPos][2] == -1 ? '' : s:SelectWord() +endf + +fun s:UpdatePlaceholderTabStops() + let changeLen = s:origWordLen - g:snipPos[s:curPos][2] + unl s:startCol s:origWordLen s:update + if !exists('s:oldVars') | return | endif + " Update tab stops in snippet if text has been added via "$#" + " (e.g., in "${1:foo}bar$1${2}"). + if changeLen != 0 + let curLine = line('.') + + for pos in g:snipPos + if pos == g:snipPos[s:curPos] | continue | endif + let changed = pos[0] == curLine && pos[1] > s:oldEndCol + let changedVars = 0 + let endPlaceholder = pos[2] - 1 + pos[1] + " Subtract changeLen from each tab stop that was after any of + " the current tab stop's placeholders. + for [lnum, col] in s:oldVars + if lnum > pos[0] | break | endif + if pos[0] == lnum + if pos[1] > col || (pos[2] == -1 && pos[1] == col) + let changed += 1 + elseif col < endPlaceholder + let changedVars += 1 + endif + endif + endfor + let pos[1] -= changeLen * changed + let pos[2] -= changeLen * changedVars " Parse variables within placeholders + " e.g., "${1:foo} ${2:$1bar}" + + if pos[2] == -1 | continue | endif + " Do the same to any placeholders in the other tab stops. + for nPos in pos[3] + let changed = nPos[0] == curLine && nPos[1] > s:oldEndCol + for [lnum, col] in s:oldVars + if lnum > nPos[0] | break | endif + if nPos[0] == lnum && nPos[1] > col + let changed += 1 + endif + endfor + let nPos[1] -= changeLen * changed + endfor + endfor + endif + unl s:endCol s:oldVars s:oldEndCol +endf + +fun s:UpdateTabStops() + let changeLine = s:endLine - g:snipPos[s:curPos][0] + let changeCol = s:endCol - g:snipPos[s:curPos][1] + if exists('s:origWordLen') + let changeCol -= s:origWordLen + unl s:origWordLen + endif + let lnum = g:snipPos[s:curPos][0] + let col = g:snipPos[s:curPos][1] + " Update the line number of all proceeding tab stops if has + " been inserted. + if changeLine != 0 + let changeLine -= 1 + for pos in g:snipPos + if pos[0] >= lnum + if pos[0] == lnum | let pos[1] += changeCol | endif + let pos[0] += changeLine + endif + if pos[2] == -1 | continue | endif + for nPos in pos[3] + if nPos[0] >= lnum + if nPos[0] == lnum | let nPos[1] += changeCol | endif + let nPos[0] += changeLine + endif + endfor + endfor + elseif changeCol != 0 + " Update the column of all proceeding tab stops if text has + " been inserted/deleted in the current line. + for pos in g:snipPos + if pos[1] >= col && pos[0] == lnum + let pos[1] += changeCol + endif + if pos[2] == -1 | continue | endif + for nPos in pos[3] + if nPos[0] > lnum | break | endif + if nPos[0] == lnum && nPos[1] >= col + let nPos[1] += changeCol + endif + endfor + endfor + endif +endf + +fun s:SelectWord() + let s:origWordLen = g:snipPos[s:curPos][2] + let s:oldWord = strpart(getline('.'), g:snipPos[s:curPos][1] - 1, + \ s:origWordLen) + let s:prevLen[1] -= s:origWordLen + if !empty(g:snipPos[s:curPos][3]) + let s:update = 1 + let s:endCol = -1 + let s:startCol = g:snipPos[s:curPos][1] - 1 + endif + if !s:origWordLen | return '' | endif + let l = col('.') != 1 ? 'l' : '' + if &sel == 'exclusive' + return "\".l.'v'.s:origWordLen."l\" + endif + return s:origWordLen == 1 ? "\".l.'gh' + \ : "\".l.'v'.(s:origWordLen - 1)."l\" +endf + +" This updates the snippet as you type when text needs to be inserted +" into multiple places (e.g. in "${1:default text}foo$1bar$1", +" "default text" would be highlighted, and if the user types something, +" UpdateChangedSnip() would be called so that the text after "foo" & "bar" +" are updated accordingly) +" +" It also automatically quits the snippet if the cursor is moved out of it +" while in insert mode. +fun s:UpdateChangedSnip(entering) + if exists('g:snipPos') && bufnr(0) != s:lastBuf + call s:RemoveSnippet() + elseif exists('s:update') " If modifying a placeholder + if !exists('s:oldVars') && s:curPos + 1 < s:snipLen + " Save the old snippet & word length before it's updated + " s:startCol must be saved too, in case text is added + " before the snippet (e.g. in "foo$1${2}bar${1:foo}"). + let s:oldEndCol = s:startCol + let s:oldVars = deepcopy(g:snipPos[s:curPos][3]) + endif + let col = col('.') - 1 + + if s:endCol != -1 + let changeLen = col('$') - s:prevLen[1] + let s:endCol += changeLen + else " When being updated the first time, after leaving select mode + if a:entering | return | endif + let s:endCol = col - 1 + endif + + " If the cursor moves outside the snippet, quit it + if line('.') != g:snipPos[s:curPos][0] || col < s:startCol || + \ col - 1 > s:endCol + unl! s:startCol s:origWordLen s:oldVars s:update + return s:RemoveSnippet() + endif + + call s:UpdateVars() + let s:prevLen[1] = col('$') + elseif exists('g:snipPos') + if !a:entering && g:snipPos[s:curPos][2] != -1 + let g:snipPos[s:curPos][2] = -2 + endif + + let col = col('.') + let lnum = line('.') + let changeLine = line('$') - s:prevLen[0] + + if lnum == s:endLine + let s:endCol += col('$') - s:prevLen[1] + let s:prevLen = [line('$'), col('$')] + endif + if changeLine != 0 + let s:endLine += changeLine + let s:endCol = col + endif + + " Delete snippet if cursor moves out of it in insert mode + if (lnum == s:endLine && (col > s:endCol || col < g:snipPos[s:curPos][1])) + \ || lnum > s:endLine || lnum < g:snipPos[s:curPos][0] + call s:RemoveSnippet() + endif + endif +endf + +" This updates the variables in a snippet when a placeholder has been edited. +" (e.g., each "$1" in "${1:foo} $1bar $1bar") +fun s:UpdateVars() + let newWordLen = s:endCol - s:startCol + 1 + let newWord = strpart(getline('.'), s:startCol, newWordLen) + if newWord == s:oldWord || empty(g:snipPos[s:curPos][3]) + return + endif + + let changeLen = g:snipPos[s:curPos][2] - newWordLen + let curLine = line('.') + let startCol = col('.') + let oldStartSnip = s:startCol + let updateTabStops = changeLen != 0 + let i = 0 + + for [lnum, col] in g:snipPos[s:curPos][3] + if updateTabStops + let start = s:startCol + if lnum == curLine && col <= start + let s:startCol -= changeLen + let s:endCol -= changeLen + endif + for nPos in g:snipPos[s:curPos][3][(i):] + " This list is in ascending order, so quit if we've gone too far. + if nPos[0] > lnum | break | endif + if nPos[0] == lnum && nPos[1] > col + let nPos[1] -= changeLen + endif + endfor + if lnum == curLine && col > start + let col -= changeLen + let g:snipPos[s:curPos][3][i][1] = col + endif + let i += 1 + endif + + " "Very nomagic" is used here to allow special characters. + call setline(lnum, substitute(getline(lnum), '\%'.col.'c\V'. + \ escape(s:oldWord, '\'), escape(newWord, '\&'), '')) + endfor + if oldStartSnip != s:startCol + call cursor(0, startCol + s:startCol - oldStartSnip) + endif + + let s:oldWord = newWord + let g:snipPos[s:curPos][2] = newWordLen +endf +" vim:noet:sw=4:ts=4:ft=vim diff --git a/doc/EasyGrep.txt b/doc/EasyGrep.txt new file mode 100644 index 0000000..c6b9ba9 --- /dev/null +++ b/doc/EasyGrep.txt @@ -0,0 +1,580 @@ +*EasyGrep.txt* +============================================================================== + EasyGrep *EasyGrep* +============================================================================== + +Author: Dan Price vim@danprice.fastmail.net *EasyGrep_Author* + +Goal: To be an easy to use, powerful find and |EasyGrep_Motivation| + replace tool for users of all skill levels. +Version: 0.98 |EasyGrep_History| + +License: Public domain, no restrictions whatsoever |EasyGrep_License| +Contribute: Please report any bugs or suggestions |EasyGrep_Bugs| + to the address above. |EasyGrep_Future| + + +============================================================================== + Table of Contents *EasyGrep_Contents* +============================================================================== + + + 1. Motivation.................|EasyGrep_Motivation| + 2. Operation..................|EasyGrep_Operation| + 2.1 Modes..................|EasyGrep_OperationModes| + 3. Keymaps....................|EasyGrep_Keymaps| + 3.1 Option Mappings........|EasyGrep_KeymapsOptions| + 3.2 Mapping Customization..|EasyGrep_KeymapsCustomization| + 4. Commands...................|EasyGrep_Commands| + 5. Options....................|EasyGrep_Options| + 5.1 Summary...............|EasyGrep_OptionsSummary| + 5.2 Explorer..............|EasyGrep_OptionsExplorer| + 5.3 Details...............|EasyGrep_OptionsDetail| + 6. Bugs.......................|EasyGrep_Bugs| + 7. Future.....................|EasyGrep_Future| + 8. History....................|EasyGrep_History| + 9. License....................|EasyGrep_License| + + +============================================================================== + Motivation *EasyGrep_Motivation* +============================================================================== + +EasyGrep's main goal is to make search and replace in files easy. Other Vim +plugins provide similar functionality, but few provide the same level of +functionality with as little configuration as EasyGrep does. In the common +case, all it takes to search for a string across multiple files is three +keypresses: vv. No clicks, no commands, no project/tags setup -- just +three keys. When you need a substitution, it also takes the same number of +keys to start a replace in files. After using EasyGrep, you'll wonder at how +you got around without it. + +While EasyGrep's default configuration will satisfy many users, it provides +more than a dozen options for those who need more control |EasyGrep_Options|. +When you need to change options, EasyGrep provides an options explorer that +indicates which files will be searched and allows visual customization of its +options |EasyGrep_OptionsExplorer|. When this isn't fast enough, EasyGrep +provides key mappings for each option to toggle its value +|EasyGrep_KeymapsOptions|. If you can't find an option you need, contact me +|EasyGrep_Author| and if it doesn't already exist, we'll make it happen. + +I hope that EasyGrep makes Vim more fun, productive, and easy for you to use. + + Happy Vimming! + + +============================================================================== + Operation *EasyGrep_Operation* +============================================================================== + +EasyGrep makes using Vim's grep capabilities easier. When using EasyGrep, +searching for a word is as easy as typing a three keypress mapping +|EasyGrep_Keymaps|. In addition to keymaps, search and replace can be invoked +through commands |EasyGrep_Commands|. + +To determine which files to search, EasyGrep provides three modes, described +in the next section. + + +Search Modes |EasyGrep_OperationModes| + +All + All files will be searched (default). + +Buffers + Files currently open in vim will be searched. Recursion has no meaning in + this mode, and will be turned off. + +TrackExt + Files that match the extension of the currently opened file will be + searched. Additionally, this extension can be mapped to a user defined + set of extensions that will also be searched |EasyGrepFileAssociations|. + + For example: in the default configuration, when test.cpp is open, files + that match any one of + + *.cpp *.hpp *.cxx *.hxx *.cc *.c *.h + + will be searched when a Grep is initiated. I find this mode to be the + most useful. + +User + Specify a custom set of file extensions to search. + + +These modes can be quickly changed through the |EasyGrep_OptionsExplorer| or +|EasyGrep_KeymapsOptions|. + + +============================================================================== + Keymaps *EasyGrep_Keymaps* +============================================================================== + +EasyGrep uses Vim's leader key, which is by default '\'. For information on +this key, type ":help mapleader". + +vv - Grep for the word under the cursor, match all occurences, + like 'g*'. See ":help gstar". +vV - Grep for the word under the cursor, match whole word, like + '*'. See ":help star". +va - Like vv, but add to existing list. +vA - Like vV, but add to existing list. + +vr - Perform a global search on the word under the cursor + and prompt for a pattern with which to replace it. +vR - Like vr, but match whole word. + +Each of these commands has an 'all occurences' and 'whole word' option, +designated by the case of the last character. If you would prefer that these +be reversed, see |EasyGrepInvertWholeWord|. + +In addition to grepping the word under the cursor, text may be visually +selected and these mappings may be used analogously to as they are used above. +Visual selections will automatically be escaped so as not to confuse the +selection with a regular expression. + + e.g. Selecting the text inside the quotes here "/" will match + against "\" but not against "word". + +To search with a regular expression, see the :Grep command |EasyGrep_Commands| + +Each of the above commands will search files according to settings +controlled by: + +vo - Open an options explorer to select the files to search in and + set grep options. + +For the options provided, see |EasyGrep_Options|. + + + *EasyGrep_KeymapsOptions* + +For each of the options presented in the options explorer, there is a mapping +that allows a direct change of this option. The pattern is vy* , +where star is the value listed in the options window for each of the options. + + e.g. To toggle recursive mode, type '\vyr' + +See |EasyGrepOptionPrefix| to change the prefix from '\vy' or to turn these +keymappings off. + + + *EasyGrep_KeymapsCustomization* + +Beyond EasyGrepOptionPrefix, other keymaps may be remapped to your liking. +See the "Keymaps" section of EasyGrep.vim for the names of these items. + +Mappings take the form: + + map (keycombination) (MappingName) +e.g. + map ,op EgMapGrepOptions + + +============================================================================== + Commands *EasyGrep_Commands* +============================================================================== + +:Grep [arg] + Search for the specified arg, like vv. When an ! is added, + search like vV + +:GrepAdd [arg] + Search for the specified arg, add to existing file list, as in + va. When an ! is added, search like vA + + The Above commands can additionally accept command switches: + -r Perform a recursive search + -R Perform a recursive search + -i Perform a case-insensitive search + -I Perform a case-sensitive search + -m Specify the number of matches to get + +:Replace [target] [replacement] +:Replace /[target]/[replacement]/ + Perform a global search and replace. The function searches + the same set of files a grep for the desired target and opens a dialog to + confirm replacement. In the second, forward slash delineated form, back + and forward slashes must be explicitly escaped. + +:ReplaceUndo + Undoes the last :Replace operation. Does not stack successive + searches; only the last replace may be undone. This function may not + work well when edits are made between a call to Replace and a call to + ReplaceUndo. + +:GrepOptions + Open the options explorer to set options. + + +For each of the search and replace commands, searching with regular +expressions is supported. Note that regular expressions are handled as +indicated by the 'magic' option (see ":help 'magic'"). + +:FilterErrorlist [args] + Removes entries from the error list according to whether or not they match + a set of specified patterns. The default behaviour is to save all entries + that match one of the patterns and remove those that do not. + + The Above command can additionally accept command switches: + -v Switch the criteria for matching; remove entries matching one of the + specified patterns instead of saving them + -l Remove entries from the location list + + +============================================================================== + Options *EasyGrep_Options* +============================================================================== + +Options Summary *EasyGrep_OptionsSummary* + + Option Description +------------------------------------------------------------------------------ +|EasyGrepFileAssociations| Specifies the location of the EasyGrep + file associations +------------------------------------------------------------------------------ +|EasyGrepMode| Mode of operation +------------------------------------------------------------------------------ +|EasyGrepCommand| Whether to use vimgrep or grepprg +------------------------------------------------------------------------------ +|EasyGrepRecursive| Recursive searching +------------------------------------------------------------------------------ +|EasyGrepIgnoreCase| Case-sensitivity in searches +------------------------------------------------------------------------------ +|EasyGrepHidden| Include hidden files in searches +------------------------------------------------------------------------------ +|EasyGrepAllOptionsInExplorer| How many options to show in the explorer +------------------------------------------------------------------------------ +|EasyGrepWindow| Quickfix or location list +------------------------------------------------------------------------------ +|EasyGrepWindowPosition| Where the error list window is opened +------------------------------------------------------------------------------ +|EasyGrepOpenWindowOnMatch| Open grep window on successful match +------------------------------------------------------------------------------ +|EasyGrepEveryMatch| Match multiple times per line +------------------------------------------------------------------------------ +|EasyGrepJumpToMatch| Jump to first match +------------------------------------------------------------------------------ +|EasyGrepSearchCurrentBufferDir| Whether to search current buffers dir + in addition to working dir +------------------------------------------------------------------------------ +|EasyGrepInvertWholeWord| Invert the meaning of whole word for vv + and vV keymaps +------------------------------------------------------------------------------ +|EasyGrepFileAssociationsInExplorer| Whether to show the file associations + list in the options explorer +------------------------------------------------------------------------------ +|EasyGrepReplaceWindowMode| Controls whether to use tabs or splits + when replacing in files +------------------------------------------------------------------------------ +|EasyGrepReplaceAllPerFile| Replace on per file or global basis +------------------------------------------------------------------------------ +|EasyGrepOptionPrefix| Specify the keymap for toggling options +------------------------------------------------------------------------------ +|EasyGrepExtraWarnings| Whether to show extra warnings +------------------------------------------------------------------------------ + + +Options Explorer *EasyGrep_OptionsExplorer* + + To invoke the options explorer, type '\vo' (default). The options + explorer presents all of EasyGrep's customizable options and provides + information on the file patterns that will be searched when invoking a + Grep. + + A useful exercise for beginners is to toggle between EasyGrep's options + and modes (|EasyGrep_OperationModes|) and type 'e' to see which files will + be searched for a given configuration. + + +Options Details *EasyGrep_OptionsDetail* + +*EasyGrepFileAssociations* +Specifies the location of a file that contains groups of files that should be +associated with one another. When set to an empty string "", no file read +will be attempted. + +This file has a simple syntax used to logically link different files types. +A simple configuration is shown below: + + C=*.c *.h + C++=*.cpp *.hpp *.cxx *.hxx *.cc + VHDL=*.hdl *.vhd *.vhdl *.vbe *.vst + Web=*.htm *.html *.js + +For example, in this configuration, whenever the active file has the .c +extension, files with the .h extension will also be search. A special feature +of this syntax is the ability to link groups together. For example, the C++ +group links to all files that are in the C group. + + +*EasyGrepMode* +Specifies the mode in which to start. +0 - All files +1 - Open Buffers +2 - Track the current extension + +Note: I find option 2 to be the most powerful, but option 0 is activated by +default because it is the most intuitive for users who haven't take the +time to understand how the script works. See |EasyGrep_OperationModes|. + +*EasyGrepCommand* +Specifies the grep command to use. + +0 - vimgrep +1 - grep (follows grepprg) + +*EasyGrepRecursive* +Specifies that recursive search be activated on start. + +*EasyGrepIgnoreCase* +Specifies the case sensitivity of searches. Note that this can be further +overrided for vimgrep searches with \c and \C. + +*EasyGrepHidden* +Specifies that hidden files search be activated on start. Note that hidden +implies the unix meaning of those files that are prepended with a '.', and not +the Windows meaning of those files with a hidden attribute. + +*EasyGrepAllOptionsInExplorer* +Specifies that all options be included in the explorer window. + +Note: settting this option is very useful when you want to try out and +learn all of the options available in this script. + +*EasyGrepWindow* +Specifies the window to use for matches. +0 - quickfix +1 - location list + +*EasyGrepWindowPosition* +Specifies where the error list window is opened. The value of this option +must match one of Vim's splitting positional window commands, such as topleft +or botright. + +*EasyGrepOpenWindowOnMatch* +Specifies whether to open the with matches after a search. + +*EasyGrepEveryMatch* +Specifies that multiple matches on the same line be treated as different +matches, like the g option to vimgrep. + +*EasyGrepJumpToMatch* +Specifies that jump to first match be activated, like the j option to vimgrep. + +*EasyGrepSearchCurrentBufferDir* +Setting this option causes EasyGrep to search the current buffer's +directory in addition to the current working directory. + +*EasyGrepInvertWholeWord* +Specifies that the whole word search keys should be inverted from their +default meaning. For example, when this option is activated, vv +matches whole word, while vV matches everything that includes the +word. Note that this affects both keymappings and commands. + +*EasyGrepFileAssociationsInExplorer* +Specifies whether to show the file associations list in the options explorer +window. + +*EasyGrepOptionPrefix* +Specifies the prefix that is used when building keymaps for setting options +directly. To specify that no option keymaps be created, set this to the empty +string. + +Default: + let g:EasyGrepOptionPrefix='vy' +Custom: + let g:EasyGrepOptionPrefix=',oe' +None: + let g:EasyGrepOptionPrefix='' + + +*EasyGrepReplaceWindowMode* +Specifies the mode that the script will use when a buffer needs to be changed +while performing a global replace. +0 - Open a new tab for each window +1 - Perform a split of the current window with the next window +2 - autowriteall; create no new windows + +Note: Option 1 has the possibility of running out of vertical space to +split more windows. Actions are taken to make this a non-issue, but this +option can often be more clunky than other options. +Note: As a result of the limitation above, option 0 is the only mode that +won't require saving the files during a replace. + +*EasyGrepReplaceAllPerFile* +Specifies that selecting 'a' (for all) will apply the replacements on a per +file basis, as opposed to globally as is the default. + +*EasyGrepExtraWarnings* +Specifies that warnings be issued for conditions that may be valid but confuse +some users. + + +============================================================================== + Future *EasyGrep_Future* +============================================================================== + +------------------------------------------------------------------------------ +Show search progress? +------------------------------------------------------------------------------ +Allow entries in the file associations list to be regular expressions +------------------------------------------------------------------------------ +create capability to include paths other than the active directories in a +search. e.g. ../../include, $INCLUDE, etc. +------------------------------------------------------------------------------ +set file/directory exclusions +------------------------------------------------------------------------------ + +============================================================================== + Bugs *EasyGrep_Bugs* +============================================================================== + +If you discover any bugs not listed here, please contact the |EasyGrep_Author| + +------------------------------------------------------------------------------ +Successive warning messages can hide a previous message +------------------------------------------------------------------------------ +ReplaceUndo opens a window even if it is already open? +------------------------------------------------------------------------------ +Report that a swap file can't be opened +------------------------------------------------------------------------------ +Don't warn when the current file will actually be searched because recursion +is on and it is below the current directory +------------------------------------------------------------------------------ +Patterns with backslashes in them require double-backslash to be searched from +the command line. This behaviour matches the builtin / command but it may be +unintuitive. +------------------------------------------------------------------------------ + + + +============================================================================== + History *EasyGrep_History* +============================================================================== + + 0.98 + Highlight: The Replace and ReplaceUndo commands were reimplemented. The + granularity of matches inside of a Replace call were improved so that + you can now decide replacements individually per line. + Additionally, Complex operations such as replacing x[i][j][k] with + x(i,j,k) and following it up with a ReplaceUndo are now possible. + + e.g. + :Replace /x\[\([^]]\+\)\]\[\([^]]\+\)\]\[\([^]]\+\)\]/x(\1,\2,\3)/ + :ReplaceUndo + + Please report any regressions with either of these functions. + + Feature: Added count command line option (-m 4 or -4) + Feature: Improved Grep options window keybindings; searching within the + options explorer window is now possible + Feature: Expanded searches to include all of the active buffers' + directories + Bugfix: Fixed recursive operation and expanded search from reporting + duplicate results + Bugfix: Fixed and in replace mode + Feature: Added EasyGrepWindowPosition for specifying where the error + list window will be opened + Feature: Added FilterErrorlist command for filtering the results within + the quickfix or location list windows + Feature: Improved printout when no matches are found + Feature: Improved searching when an entire line is selected + 0.96 + Feature: Expanded upon the list of file associations + Feature: Expanded searches to the current buffer's directory + in addition to the current working directory + Feature: Added command line arguments to :Grep and :Replace for + recursive searches and case sensitivity + Feature: Added toggle for window replace mode + Feature: Added toggle for showing file associations list in options + explorer + Bugfix: Case insensitivity would fail in replacing some patterns + 0.95 + Feature: Added search and replace on visual selections + Feature: Improved Grepping for items that can be interpreted as regular + expressions. Selections are assumed to be literal, whereas explicit + commands are assumed to be regular expressions. + Feature: Removed option g:EasyGrepNoDirectMappings in favor of + g:EasyGrepOptionPrefix, which allows the option prefix to be changed. + Bugfix: The tracked extension would sometimes fail to be updated when + switching between buffers + Documentation: Split the documentation into its own file; greatly + expanded upon its contents + Change: Activating a mode that is already activated will no longer + deactivate it + Change: GrepOptions no longer accepts an argument; use user mode instead + Change: Clarified mapping names; custom mappings will need to + be reset. + 0.9 + Feature: Added forward slash delineated pattern to the Replace command + e.g. :Replace /target/replacement/ + that allows more complicated replacements; you can now work with + patterns that have spaces in them. + Bugfix: If cursorline is off at the start of a replace, now ensuring + that cursorline is turned off for all buffers, and not just the last one + Bugfix: fixed an issue with an extra tab being opened during a + replacement + 0.8 + Implemented case sensitivity that is independent of ignorecase, thanks + to Doro Wu for contributing to this functionality + Changed shortcut key for hidden files from 'i' to 'h' + 0.7 + Expanded search of EasyGrepFileAssociations list to every component of + 'runtimepath'. This solves a starting message for those who placed + EasyGrepFileAssociations in a location other than the first location in + 'runtimepath'. + 0.6 + Fixed paths with spaces in them + Folds will now be disabled where replacements are to be made + Fixed an error with checking for extra warnings + Better highlighting while replacing + Recursive mode can no longer be activated when Buffers mode is activated + 0.5 + Fixed an issue with tracking the file extension where sometimes the + desired extension wouldn't be registered. + Better reporting when no files match. + Now warning when searching from a working directory that doesn't match + the current file's directory. + Added g:EasyGrepExtraWarnings option. + 0.4 + Improved Replace and ReplaceUndo + Added two configurable modes for how the windows operate when doing a + global replace. + Fixed an issue with linked filetypes. + 0.3 + Added experimental :Replace and :ReplaceUndo commands; keymapped + vr for :Replace + Improved response when no matches + 0.2 + Added option to toggle showing fewer or more options; showing fewer + options by default. + Added option '?' to print the current configuration and save it to a + register. + Now creating direct mappings by default; see g:EasyGrepNoDirectMappings + to turn this off. + 0.1 + Initial version + + +============================================================================== + License *EasyGrep_License* +============================================================================== + + Public domain, no restrictions whatsoever + +When writing EasyGrep, I wanted it to be free in the broadest sense. Of +course, most (if not all) plugins for Vim are free, but I wanted mine to be +freer still: I've released EasyGrep in the public domain. It took a lot of +time and learning to get EasyGrep to work, and I want anyone to take advantage +of my contribution. If you see some (or many) snippets of EasyGrep's code +that you need, use it -- you don't need to ask me, think about any copyright, +worry about violating a license, or even note that the code came from me, just +use it. My only request is that if you are thinking of forking EasyGrep (or +enhancing, as some authors claim to do), please contact me to let me know what +you feel is lacking in EasyGrep, and I promise I'll be receptive to correcting +these issues. + + +============================================================================== +vim:tw=78:ts=4:ft=help:norl:fdm=marker diff --git a/doc/NERD_commenter.txt b/doc/NERD_commenter.txt new file mode 100644 index 0000000..5309f61 --- /dev/null +++ b/doc/NERD_commenter.txt @@ -0,0 +1,988 @@ +*NERD_commenter.txt* Plugin for commenting code + + + NERD COMMENTER REFERENCE MANUAL~ + + + + + +============================================================================== +CONTENTS *NERDCommenterContents* + + 1.Intro...................................|NERDCommenter| + 2.Installation............................|NERDComInstallation| + 3.Functionality provided..................|NERDComFunctionality| + 3.1 Functionality Summary.............|NERDComFunctionalitySummary| + 3.2 Functionality Details.............|NERDComFunctionalityDetails| + 3.2.1 Comment map.................|NERDComComment| + 3.2.2 Nested comment map..........|NERDComNestedComment| + 3.2.3 Toggle comment map..........|NERDComToggleComment| + 3.2.4 Minimal comment map.........|NERDComMinimalComment| + 3.2.5 Invert comment map..........|NERDComInvertComment| + 3.2.6 Sexy comment map............|NERDComSexyComment| + 3.2.7 Yank comment map............|NERDComYankComment| + 3.2.8 Comment to EOL map..........|NERDComEOLComment| + 3.2.9 Append com to line map......|NERDComAppendComment| + 3.2.10 Insert comment map.........|NERDComInsertComment| + 3.2.11 Use alternate delims map...|NERDComAltDelim| + 3.2.12 Comment aligned maps.......|NERDComAlignedComment| + 3.2.13 Uncomment line map.........|NERDComUncommentLine| + 3.4 Sexy Comments.....................|NERDComSexyComments| + 3.5 The NERDComment function..........|NERDComNERDComment| + 4.Options.................................|NERDComOptions| + 4.1 Options summary...................|NERDComOptionsSummary| + 4.2 Options details...................|NERDComOptionsDetails| + 4.3 Default delimiter Options.........|NERDComDefaultDelims| + 5. Customising key mappings...............|NERDComMappings| + 6. Issues with the script.................|NERDComIssues| + 6.1 Delimiter detection heuristics....|NERDComHeuristics| + 6.2 Nesting issues....................|NERDComNesting| + 7.About.. ............................|NERDComAbout| + 8.Changelog...............................|NERDComChangelog| + 9.Credits.................................|NERDComCredits| + 10.License................................|NERDComLicense| + +============================================================================== +1. Intro *NERDCommenter* + +The NERD commenter provides many different commenting operations and styles +which are invoked via key mappings and a menu. These operations are available +for most filetypes. + +There are also options that allow to tweak the commenting engine to your +taste. + +============================================================================== +2. Installation *NERDComInstallation* + +The NERD Commenter requires Vim 7 or higher. + +Extract the plugin files in your ~/.vim (*nix) or ~/vimfiles (Windows). You +should have 2 files: > + plugin/NERD_commenter.vim + doc/NERD_commenter.txt +< +Next, to finish installing the help file run: > + :helptags ~/.vim/doc +< +See |add-local-help| for more details. + +Make sure that you have filetype plugins enabled, as the script makes use of +|'commentstring'| where possible (which is usually set in a filetype plugin). +See |filetype-plugin-on| for details, but basically, stick this in your vimrc > + filetype plugin on +< + +============================================================================== +3. Functionality provided *NERDComFunctionality* + +------------------------------------------------------------------------------ +3.1 Functionality summary *NERDComFunctionalitySummary* + +The following key mappings are provided by default (there is also a menu +with items corresponding to all the mappings below): + +[count]cc |NERDComComment| +Comment out the current line or text selected in visual mode. + + +[count]cn |NERDComNestedComment| +Same as cc but forces nesting. + + +[count]c |NERDComToggleComment| +Toggles the comment state of the selected line(s). If the topmost selected +line is commented, all selected lines are uncommented and vice versa. + + +[count]cm |NERDComMinimalComment| +Comments the given lines using only one set of multipart delimiters. + + +[count]ci |NERDComInvertComment| +Toggles the comment state of the selected line(s) individually. + + +[count]cs |NERDComSexyComment| +Comments out the selected lines ``sexily'' + + +[count]cy |NERDComYankComment| +Same as cc except that the commented line(s) are yanked first. + + +c$ |NERDComEOLComment| +Comments the current line from the cursor to the end of line. + + +cA |NERDComAppendComment| +Adds comment delimiters to the end of line and goes into insert mode between +them. + + +|NERDComInsertComment| +Adds comment delimiters at the current cursor position and inserts between. +Disabled by default. + + +ca |NERDComAltDelim| +Switches to the alternative set of delimiters. + + +[count]cl +[count]cb |NERDComAlignedComment| +Same as |NERDComComment| except that the delimiters are aligned down the +left side (cl) or both sides (cb). + + +[count]cu |NERDComUncommentLine| +Uncomments the selected line(s). + +------------------------------------------------------------------------------ +3.2 Functionality details *NERDComFunctionalityDetails* + +------------------------------------------------------------------------------ +3.2.1 Comment map *NERDComComment* + +Default mapping: [count]cc +Mapped to: NERDCommenterComment +Applicable modes: normal visual visual-line visual-block. + + +Comments out the current line. If multiple lines are selected in visual-line +mode, they are all commented out. If some text is selected in visual or +visual-block mode then the script will try to comment out the exact text that +is selected using multi-part delimiters if they are available. + +If a [count] is given in normal mode, the mapping works as though that many +lines were selected in visual-line mode. + +------------------------------------------------------------------------------ +3.2.2 Nested comment map *NERDComNestedComment* + +Default mapping: [count]cn +Mapped to: NERDCommenterNest +Applicable modes: normal visual visual-line visual-block. + +Performs nested commenting. Works the same as cc except that if a line +is already commented then it will be commented again. + +If |'NERDUsePlaceHolders'| is set then the previous comment delimiters will +be replaced by place-holder delimiters if needed. Otherwise the nested +comment will only be added if the current commenting delimiters have no right +delimiter (to avoid syntax errors) + +If a [count] is given in normal mode, the mapping works as though that many +lines were selected in visual-line mode. + +Related options: +|'NERDDefaultNesting'| + +------------------------------------------------------------------------------ +3.2.3 Toggle comment map *NERDComToggleComment* + +Default mapping: [count]c +Mapped to: NERDCommenterToggle +Applicable modes: normal visual-line. + +Toggles commenting of the lines selected. The behaviour of this mapping +depends on whether the first line selected is commented or not. If so, all +selected lines are uncommented and vice versa. + +With this mapping, a line is only considered to be commented if it starts with +a left delimiter. + +If a [count] is given in normal mode, the mapping works as though that many +lines were selected in visual-line mode. + +------------------------------------------------------------------------------ +3.2.4 Minimal comment map *NERDComMinimalComment* + +Default mapping: [count]cm +Mapped to: NERDCommenterMinimal +Applicable modes: normal visual-line. + +Comments the selected lines using one set of multipart delimiters if possible. + +For example: if you are programming in c and you select 5 lines and press +cm then a '/*' will be placed at the start of the top line and a '*/' +will be placed at the end of the last line. + +Sets of multipart comment delimiters that are between the top and bottom +selected lines are replaced with place holders (see |'NERDLPlace'|) if +|'NERDUsePlaceHolders'| is set for the current filetype. If it is not, then +the comment will be aborted if place holders are required to prevent illegal +syntax. + +If a [count] is given in normal mode, the mapping works as though that many +lines were selected in visual-line mode. + +------------------------------------------------------------------------------ +3.2.5 Invert comment map *NERDComInvertComment* + +Default mapping: ci +Mapped to: NERDCommenterInvert +Applicable modes: normal visual-line. + +Inverts the commented state of each selected line. If the a selected line is +commented then it is uncommented and vice versa. Each line is examined and +commented/uncommented individually. + +With this mapping, a line is only considered to be commented if it starts with +a left delimiter. + +If a [count] is given in normal mode, the mapping works as though that many +lines were selected in visual-line mode. + +------------------------------------------------------------------------------ +3.2.6 Sexy comment map *NERDComSexyComment* + +Default mapping: [count]cs +Mapped to: NERDCommenterSexy +Applicable modes: normal, visual-line. + +Comments the selected line(s) ``sexily''... see |NERDComSexyComments| for +a description of what sexy comments are. Can only be done on filetypes for +which there is at least one set of multipart comment delimiters specified. + +Sexy comments cannot be nested and lines inside a sexy comment cannot be +commented again. + +If a [count] is given in normal mode, the mapping works as though that many +lines were selected in visual-line mode. + +Related options: +|'NERDCompactSexyComs'| + +------------------------------------------------------------------------------ +3.2.7 Yank comment map *NERDComYankComment* + +Default mapping: [count]cy +Mapped to: NERDCommenterYank +Applicable modes: normal visual visual-line visual-block. + +Same as cc except that it yanks the line(s) that are commented first. + +------------------------------------------------------------------------------ +3.2.8 Comment to EOL map *NERDComEOLComment* + +Default mapping: c$ +Mapped to: NERDCommenterToEOL +Applicable modes: normal. + +Comments the current line from the current cursor position up to the end of +the line. + +------------------------------------------------------------------------------ +3.2.9 Append com to line map *NERDComAppendComment* + +Default mapping: cA +Mapped to: NERDCommenterAppend +Applicable modes: normal. + +Appends comment delimiters to the end of the current line and goes +to insert mode between the new delimiters. + +------------------------------------------------------------------------------ +3.2.10 Insert comment map *NERDComInsertComment* + +Default mapping: disabled by default. +Map it to: NERDCommenterInInsert +Applicable modes: insert. + +Adds comment delimiters at the current cursor position and inserts +between them. + +NOTE: prior to version 2.1.17 this was mapped to ctrl-c. To restore this +mapping add > + let NERDComInsertMap='' +< +to your vimrc. + +------------------------------------------------------------------------------ +3.2.11 Use alternate delims map *NERDComAltDelim* + +Default mapping: ca +Mapped to: NERDCommenterAltDelims +Applicable modes: normal. + +Changes to the alternative commenting style if one is available. For example, +if the user is editing a c++ file using // comments and they hit ca +then they will be switched over to /**/ comments. + +See also |NERDComDefaultDelims| + +------------------------------------------------------------------------------ +3.2.12 Comment aligned maps *NERDComAlignedComment* + +Default mappings: [count]cl [count]cb +Mapped to: NERDCommenterAlignLeft + NERDCommenterAlignBoth +Applicable modes: normal visual-line. + +Same as cc except that the comment delimiters are aligned on the left +side or both sides respectively. These comments are always nested if the +line(s) are already commented. + +If a [count] is given in normal mode, the mapping works as though that many +lines were selected in visual-line mode. + +------------------------------------------------------------------------------ +3.2.13 Uncomment line map *NERDComUncommentLine* + +Default mapping: [count]cu +Mapped to: NERDCommenterUncomment +Applicable modes: normal visual visual-line visual-block. + +Uncomments the current line. If multiple lines are selected in +visual mode then they are all uncommented. + +When uncommenting, if the line contains multiple sets of delimiters then the +``outtermost'' pair of delimiters will be removed. + +The script uses a set of heurisics to distinguish ``real'' delimiters from +``fake'' ones when uncommenting. See |NERDComIssues| for details. + +If a [count] is given in normal mode, the mapping works as though that many +lines were selected in visual-line mode. + +Related options: +|'NERDRemoveAltComs'| +|'NERDRemoveExtraSpaces'| + +------------------------------------------------------------------------------ +3.3 Sexy Comments *NERDComSexyComments* +These are comments that use one set of multipart comment delimiters as well as +one other marker symbol. For example: > + /* + * This is a c style sexy comment + * So there! + */ + + /* This is a c style sexy comment + * So there! + * But this one is ``compact'' style */ +< +Here the multipart delimiters are /* and */ and the marker is *. + +------------------------------------------------------------------------------ +3.4 The NERDComment function *NERDComNERDComment* + +All of the NERD commenter mappings and menu items invoke a single function +which delegates the commenting work to other functions. This function is +public and has the prototype: > + function! NERDComment(isVisual, type) +< +The arguments to this function are simple: + - isVisual: if you wish to do any kind of visual comment then set this to + 1 and the function will use the '< and '> marks to find the comment + boundries. If set to 0 then the function will operate on the current + line. + - type: is used to specify what type of commenting operation is to be + performed, and it can be one of the following: "sexy", "invert", + "minimal", "toggle", "alignLeft", "alignBoth", "norm", "nested", + "toEOL", "append", "insert", "uncomment", "yank" + +For example, if you typed > + :call NERDComment(1, 'sexy') +< +then the script would do a sexy comment on the last visual selection. + + +============================================================================== +4. Options *NERDComOptions* + +------------------------------------------------------------------------------ +4.1 Options summary *NERDComOptionsSummary* + +|'loaded_nerd_comments'| Turns off the script. +|'NERDAllowAnyVisualDelims'| Allows multipart alternative delims to + be used when commenting in + visual/visual-block mode. +|'NERDBlockComIgnoreEmpty'| Forces right delims to be placed when + doing visual-block comments. +|'NERDCommentWholeLinesInVMode'| Changes behaviour of visual comments. +|'NERDCreateDefaultMappings'| Turn the default mappings on/off. +|'NERDDefaultNesting'| Tells the script to use nested comments + by default. +|'NERDMenuMode'| Specifies how the NERD commenter menu + will appear (if at all). +|'NERDLPlace'| Specifies what to use as the left + delimiter placeholder when nesting + comments. +|'NERDUsePlaceHolders'| Specifies which filetypes may use + placeholders when nesting comments. +|'NERDRemoveAltComs'| Tells the script whether to remove + alternative comment delimiters when + uncommenting. +|'NERDRemoveExtraSpaces'| Tells the script to always remove the + extra spaces when uncommenting + (regardless of whether NERDSpaceDelims + is set) +|'NERDRPlace'| Specifies what to use as the right + delimiter placeholder when nesting + comments. +|'NERDSpaceDelims'| Specifies whether to add extra spaces + around delimiters when commenting, and + whether to remove them when + uncommenting. +|'NERDCompactSexyComs'| Specifies whether to use the compact + style sexy comments. + +------------------------------------------------------------------------------ +4.3 Options details *NERDComOptionsDetails* + +To enable any of the below options you should put the given line in your +~/.vimrc + + *'loaded_nerd_comments'* +If this script is driving you insane you can turn it off by setting this +option > + let loaded_nerd_comments=1 +< +------------------------------------------------------------------------------ + *'NERDAllowAnyVisualDelims'* +Values: 0 or 1. +Default: 1. + +If set to 1 then, when doing a visual or visual-block comment (but not a +visual-line comment), the script will choose the right delimiters to use for +the comment. This means either using the current delimiters if they are +multipart or using the alternative delimiters if THEY are multipart. For +example if we are editing the following java code: > + float foo = 1221; + float bar = 324; + System.out.println(foo * bar); +< +If we are using // comments and select the "foo" and "bar" in visual-block +mode, as shown left below (where '|'s are used to represent the visual-block +boundary), and comment it then the script will use the alternative delims as +shown on the right: > + + float |foo| = 1221; float /*foo*/ = 1221; + float |bar| = 324; float /*bar*/ = 324; + System.out.println(foo * bar); System.out.println(foo * bar); +< +------------------------------------------------------------------------------ + *'NERDBlockComIgnoreEmpty'* +Values: 0 or 1. +Default: 1. + +This option affects visual-block mode commenting. If this option is turned +on, lines that begin outside the right boundary of the selection block will be +ignored. + +For example, if you are commenting this chunk of c code in visual-block mode +(where the '|'s are used to represent the visual-block boundary) > + #include + #include + #include + |int| main(){ + | | printf("SUCK THIS\n"); + | | while(1){ + | | fork(); + | | } + |} | +< +If NERDBlockComIgnoreEmpty=0 then this code will become: > + #include + #include + #include + /*int*/ main(){ + /* */ printf("SUCK THIS\n"); + /* */ while(1){ + /* */ fork(); + /* */ } + /*} */ +< +Otherwise, the code block would become: > + #include + #include + #include + /*int*/ main(){ + printf("SUCK THIS\n"); + while(1){ + fork(); + } + /*} */ +< +------------------------------------------------------------------------------ + *'NERDCommentWholeLinesInVMode'* +Values: 0, 1 or 2. +Default: 0. + +By default the script tries to comment out exactly what is selected in visual +mode (v). For example if you select and comment the following c code (using | +to represent the visual boundary): > + in|t foo = 3; + int bar =| 9; + int baz = foo + bar; +< +This will result in: > + in/*t foo = 3;*/ + /*int bar =*/ 9; + int baz = foo + bar; +< +But some people prefer it if the whole lines are commented like: > + /*int foo = 3;*/ + /*int bar = 9;*/ + int baz = foo + bar; +< +If you prefer the second option then stick this line in your vimrc: > + let NERDCommentWholeLinesInVMode=1 +< + +If the filetype you are editing only has no multipart delimiters (for example +a shell script) and you hadnt set this option then the above would become > + in#t foo = 3; + #int bar = 9; +< +(where # is the comment delimiter) as this is the closest the script can +come to commenting out exactly what was selected. If you prefer for whole +lines to be commented out when there is no multipart delimiters but the EXACT +text that was selected to be commented out if there IS multipart delimiters +then stick the following line in your vimrc: > + let NERDCommentWholeLinesInVMode=2 +< + +Note that this option does not affect the behaviour of commenting in +|visual-block| mode. + +------------------------------------------------------------------------------ + *'NERDCreateDefaultMappings'* +Values: 0 or 1. +Default: 1. + +If set to 0, none of the default mappings will be created. + +See also |NERDComMappings|. + +------------------------------------------------------------------------------ + *'NERDRemoveAltComs'* +Values: 0 or 1. +Default: 1. + +When uncommenting a line (for a filetype with an alternative commenting style) +this option tells the script whether to look for, and remove, comment +delimiters of the alternative style. + +For example, if you are editing a c++ file using // style comments and you go +cu on this line: > + /* This is a c++ comment baby! */ +< +It will not be uncommented if the NERDRemoveAltComs is set to 0. + +------------------------------------------------------------------------------ + *'NERDRemoveExtraSpaces'* +Values: 0 or 1. +Default: 1. + +By default, the NERD commenter will remove spaces around comment delimiters if +either: +1. |'NERDSpaceDelims'| is set to 1. +2. NERDRemoveExtraSpaces is set to 1. + +This means that if we have the following lines in a c code file: > + /* int foo = 5; */ + /* int bar = 10; */ + int baz = foo + bar +< +If either of the above conditions hold then if these lines are uncommented +they will become: > + int foo = 5; + int bar = 10; + int baz = foo + bar +< +Otherwise they would become: > + int foo = 5; + int bar = 10; + int baz = foo + bar +< +If you want the spaces to be removed only if |'NERDSpaceDelims'| is set then +set NERDRemoveExtraSpaces to 0. + +------------------------------------------------------------------------------ + *'NERDLPlace'* + *'NERDRPlace'* +Values: arbitrary string. +Default: + NERDLPlace: "[>" + NERDRPlace: "<]" + +These options are used to control the strings used as place-holder delimiters. +Place holder delimiters are used when performing nested commenting when the +filetype supports commenting styles with both left and right delimiters. +To set these options use lines like: > + let NERDLPlace="FOO" + let NERDRPlace="BAR" +< +Following the above example, if we have line of c code: > + /* int horse */ +< +and we comment it with cn it will be changed to: > + /*FOO int horse BAR*/ +< +When we uncomment this line it will go back to what it was. + +------------------------------------------------------------------------------ + *'NERDMenuMode'* +Values: 0, 1, 2, 3. +Default: 3 + +This option can take 4 values: + "0": Turns the menu off. + "1": Turns the 'comment' menu on with no menu shortcut. + "2": Turns the 'comment 'menu on with -c as the shortcut. + "3": Turns the 'Plugin -> comment' menu on with -c as the shortcut. + +------------------------------------------------------------------------------ + *'NERDUsePlaceHolders'* +Values: 0 or 1. +Default 1. + +This option is used to specify whether place-holder delimiters should be used +when creating a nested comment. + +------------------------------------------------------------------------------ + *'NERDSpaceDelims'* +Values: 0 or 1. +Default 0. + +Some people prefer a space after the left delimiter and before the right +delimiter like this: > + /* int foo=2; */ +< +as opposed to this: > + /*int foo=2;*/ +< +If you want spaces to be added then set NERDSpaceDelims to 1 in your vimrc. + +See also |'NERDRemoveExtraSpaces'|. + +------------------------------------------------------------------------------ + *'NERDCompactSexyComs'* +Values: 0 or 1. +Default 0. + +Some people may want their sexy comments to be like this: > + /* Hi There! + * This is a sexy comment + * in c */ +< +As opposed to like this: > + /* + * Hi There! + * This is a sexy comment + * in c + */ +< +If this option is set to 1 then the top style will be used. + +------------------------------------------------------------------------------ + *'NERDDefaultNesting'* +Values: 0 or 1. +Default 1. + +When this option is set to 1, comments are nested automatically. That is, if +you hit cc on a line that is already commented it will be commented +again. + +------------------------------------------------------------------------------ +3.3 Default delimiter customisation *NERDComDefaultDelims* + +If you want the NERD commenter to use the alternative delimiters for a +specific filetype by default then put a line of this form into your vimrc: > + let NERD__alt_style=1 +< +Example: java uses // style comments by default, but you want it to default to +/* */ style comments instead. You would put this line in your vimrc: > + let NERD_java_alt_style=1 +< + +See |NERDComAltDelim| for switching commenting styles at runtime. + +============================================================================== +5. Key mapping customisation *NERDComMappings* + +To change a mapping just map another key combo to the internal mapping. +For example, to remap the |NERDComComment| mapping to ",omg" you would put +this line in your vimrc: > + map ,omg NERDCommenterComment +< +This will stop the corresponding default mappings from being created. + +See the help for the mapping in question to see which mapping to +map to. + +See also |'NERDCreateDefaultMappings'|. + +============================================================================== +6. Issues with the script *NERDComIssues* + + +------------------------------------------------------------------------------ +6.1 Delimiter detection heuristics *NERDComHeuristics* + +Heuristics are used to distinguish the real comment delimiters + +Because we have comment mappings that place delimiters in the middle of lines, +removing comment delimiters is a bit tricky. This is because if comment +delimiters appear in a line doesnt mean they really ARE delimiters. For +example, Java uses // comments but the line > + System.out.println("//"); +< +clearly contains no real comment delimiters. + +To distinguish between ``real'' comment delimiters and ``fake'' ones we use a +set of heuristics. For example, one such heuristic states that any comment +delimiter that has an odd number of non-escaped " characters both preceding +and following it on the line is not a comment because it is probably part of a +string. These heuristics, while usually pretty accurate, will not work for all +cases. + +------------------------------------------------------------------------------ +6.2 Nesting issues *NERDComNesting* + +If we have some line of code like this: > + /*int foo */ = /*5 + 9;*/ +< +This will not be uncommented legally. The NERD commenter will remove the +"outter most" delimiters so the line will become: > + int foo */ = /*5 + 9; +< +which almost certainly will not be what you want. Nested sets of comments will +uncomment fine though. Eg: > + /*int/* foo =*/ 5 + 9;*/ +< +will become: > + int/* foo =*/ 5 + 9; +< +(Note that in the above examples I have deliberately not used place holders +for simplicity) + +============================================================================== +7. About *NERDComAbout* + +The author of the NERD commenter is Martyzillatron --- the half robot, half +dinosaur bastard son of Megatron and Godzilla. He enjoys destroying +metropolises and eating tourist busses. + +Drop him a line at martin_grenfell at msn.com. He would love to hear from you. +its a lonely life being the worlds premier terror machine. How would you feel +if your face looked like a toaster and a t-rex put together? :( + +The latest stable versions can be found at + http://www.vim.org/scripts/script.php?script_id=1218 + +The latest dev versions are on github + http://github.com/scrooloose/nerdcommenter + +============================================================================== +8. Changelog *NERDComChangelog* + +2.3.0 + - remove all filetypes which have a &commentstring in the standard vim + runtime for vim > 7.0 unless the script stores an alternate set of + delimiters + - make the script complain if the user doesnt have filetype plugins enabled + - use instead of comma to start the default mappings + - fix a couple of bugs with sexy comments - thanks to Tim Smart + - lots of refactoring + +2.2.2 + - remove the NERDShutup option and the message is suppresses, this makes + the plugin silently rely on &commentstring for unknown filetypes. + - add support for dhcpd, limits, ntp, resolv, rgb, sysctl, udevconf and + udevrules. Thanks to Thilo Six. + - match filetypes case insensitively + - add support for mp (metapost), thanks to Andrey Skvortsov. + - add support for htmlcheetah, thanks to Simon Hengel. + - add support for javacc, thanks to Matt Tolton. + - make <%# %> the default delims for eruby, thanks to tpope. + - add support for javascript.jquery, thanks to Ivan Devat. + - add support for cucumber and pdf. Fix sass and railslog delims, + thanks to tpope + +2.2.1 + - add support for newlisp and clojure, thanks to Matthew Lee Hinman. + - fix automake comments, thanks to Elias Pipping + - make haml comments default to -# with / as the alternative delimiter, + thanks to tpope + - add support for actionscript and processing thanks to Edwin Benavides + - add support for ps1 (powershell), thanks to Jason Mills + - add support for hostsaccess, thanks to Thomas Rowe + - add support for CVScommit + - add support for asciidoc, git and gitrebase. Thanks to Simon Ruderich. + - use # for gitcommit comments, thanks to Simon Ruderich. + - add support for mako and genshi, thanks to Keitheis. + - add support for conkyrc, thanks to David + - add support for SVNannotate, thanks to Miguel Jaque Barbero. + - add support for sieve, thanks to Stefan Walk + - add support for objj, thanks to Adam Thorsen. + +2.2.0 + - rewrote the mappings system to be more "standard". + - removed all the mapping options. Now, mappings to mappings are + used + - see :help NERDComMappings, and :help NERDCreateDefaultMappings for + more info + - remove "prepend comments" and "right aligned comments". + - add support for applescript, calbire, man, SVNcommit, potwiki, txt2tags and SVNinfo. + Thanks to nicothakis, timberke, sgronblo, mntnoe, Bernhard Grotz, John + O'Shea, François and Giacomo Mariani respectively. + - bugfix for haskell delimiters. Thanks to mntnoe. +2.1.18 + - add support for llvm. Thanks to nicothakis. + - add support for xquery. Thanks to Phillip Kovalev. +2.1.17 + - fixed haskell delimiters (hackily). Thanks to Elias Pipping. + - add support for mailcap. Thanks to Pascal Brueckner. + - add support for stata. Thanks to Jerónimo Carballo. + - applied a patch from ewfalor to fix an error in the help file with the + NERDMapleader doc + - disable the insert mode ctrl-c mapping by default, see :help + NERDComInsertComment if you wish to restore it + +============================================================================== +9. Credits *NERDComCredits* + +Thanks to the follow people for suggestions and patches: + +Nick Brettell +Matthew Hawkins +Mathieu Clabaut +Greg Searle +Nguyen +Litchi +Jorge Scandaliaris +Shufeng Zheng +Martin Stubenschrott +Markus Erlmann +Brent Rice +Richard Willis +Igor Prischepoff +Harry +David Bourgeois +Eike Von Seggern +Torsten Blix +Alexander Bosecke +Stefano Zacchiroli +Norick Chen +Joseph Barker +Gary Church +Tim Carey-Smith +Markus Klinik +Anders +Seth Mason +James Hales +Heptite +Cheng Fang +Yongwei Wu +David Miani +Jeremy Hinegardner +Marco +Ingo Karkat +Zhang Shuhan +tpope +Ben Schmidt +David Fishburn +Erik Falor +JaGoTerr +Elias Pipping +mntnoe +Mark S. + + +Thanks to the following people for sending me new filetypes to support: + +The hackers The filetypes~ +Sam R verilog +Jonathan Derque context, plaintext and mail +Vigil fetchmail +Michael Brunner kconfig +Antono Vasiljev netdict +Melissa Reid omlet +Ilia N Ternovich quickfix +John O'Shea RTF, SVNcommitlog and vcscommit, SVNCommit +Anders occam +Mark Woodward csv +fREW gentoo-package-mask, + gentoo-package-keywords, + gentoo-package-use, and vo_base +Alexey verilog_systemverilog, systemverilog +Lizendir fstab +Michael Böhler autoit, autohotkey and docbk +Aaron Small cmake +Ramiro htmldjango and django +Stefano Zacchiroli debcontrol, debchangelog, mkd +Alex Tarkovsky ebuild and eclass +Jorge Rodrigues gams +Rainer Müller Objective C +Jason Mills Groovy, ps1 +Normandie Azucena vera +Florian Apolloner ldif +David Fishburn lookupfile +Niels Aan de Brugh rst +Don Hatlestad ahk +Christophe Benz Desktop and xsd +Eyolf Østrem lilypond, bbx and lytex +Ingo Karkat dosbatch +Nicolas Weber markdown, objcpp +tinoucas gentoo-conf-d +Greg Weber D, haml +Bruce Sherrod velocity +timberke cobol, calibre +Aaron Schaefer factor +Mr X asterisk, mplayerconf +Kuchma Michael plsql +Brett Warneke spectre +Pipp lhaskell +Renald Buter scala +Vladimir Lomov asymptote +Marco mrxvtrc, aap +nicothakis SVNAnnotate, CVSAnnotate, SVKAnnotate, + SVNdiff, gitAnnotate, gitdiff, dtrace + llvm, applescript +Chen Xing Wikipedia +Jacobo Diaz dakota, patran +Li Jin gentoo-env-d, gentoo-init-d, + gentoo-make-conf, grub, modconf, sudoers +SpookeyPeanut rib +Greg Jandl pyrex/cython +Christophe Benz services, gitcommit +A Pontus vimperator +Stromnov slice, bzr +Martin Kustermann pamconf +Indriði Einarsson mason +Chris map +Krzysztof A. Adamski group +Pascal Brueckner mailcap +Jerónimo Carballo stata +Phillip Kovalev xquery +Bernhard Grotz potwiki +sgronblo man +François txt2tags +Giacomo Mariani SVNinfo +Matthew Lee Hinman newlisp, clojure +Elias Pipping automake +Edwin Benavides actionscript, processing +Thomas Rowe hostsaccess +Simon Ruderich asciidoc, git, gitcommit, gitrebase +Keitheis mako, genshi +David conkyrc +Miguel Jaque Barbero SVNannotate +Stefan Walk sieve +Adam Thorsen objj +Thilo Six dhcpd, limits, ntp, resolv, rgb, sysctl, + udevconf, udevrules +Andrey Skvortsov mp +Simon Hengel htmlcheetah +Matt Tolton javacc +Ivan Devat javascript.jquery +tpope cucumber,pdf +============================================================================== +10. License *NERDComLicense* + +The NERD commenter is released under the wtfpl. +See http://sam.zoy.org/wtfpl/COPYING. diff --git a/doc/NERD_tree.txt b/doc/NERD_tree.txt new file mode 100644 index 0000000..2e2278c --- /dev/null +++ b/doc/NERD_tree.txt @@ -0,0 +1,1222 @@ +*NERD_tree.txt* A tree explorer plugin that owns your momma! + + + + omg its ... ~ + + ________ ________ _ ____________ ____ __________ ____________~ + /_ __/ / / / ____/ / | / / ____/ __ \/ __ \ /_ __/ __ \/ ____/ ____/~ + / / / /_/ / __/ / |/ / __/ / /_/ / / / / / / / /_/ / __/ / __/ ~ + / / / __ / /___ / /| / /___/ _, _/ /_/ / / / / _, _/ /___/ /___ ~ + /_/ /_/ /_/_____/ /_/ |_/_____/_/ |_/_____/ /_/ /_/ |_/_____/_____/ ~ + + + Reference Manual~ + + + + +============================================================================== +CONTENTS *NERDTree-contents* + + 1.Intro...................................|NERDTree| + 2.Functionality provided..................|NERDTreeFunctionality| + 2.1.Global commands...................|NERDTreeGlobalCommands| + 2.2.Bookmarks.........................|NERDTreeBookmarks| + 2.2.1.The bookmark table..........|NERDTreeBookmarkTable| + 2.2.2.Bookmark commands...........|NERDTreeBookmarkCommands| + 2.2.3.Invalid bookmarks...........|NERDTreeInvalidBookmarks| + 2.3.NERD tree mappings................|NERDTreeMappings| + 2.4.The NERD tree menu................|NERDTreeMenu| + 3.Options.................................|NERDTreeOptions| + 3.1.Option summary....................|NERDTreeOptionSummary| + 3.2.Option details....................|NERDTreeOptionDetails| + 4.The NERD tree API.......................|NERDTreeAPI| + 4.1.Key map API.......................|NERDTreeKeymapAPI| + 4.2.Menu API..........................|NERDTreeMenuAPI| + 5.About...................................|NERDTreeAbout| + 6.Changelog...............................|NERDTreeChangelog| + 7.Credits.................................|NERDTreeCredits| + 8.License.................................|NERDTreeLicense| + +============================================================================== +1. Intro *NERDTree* + +What is this "NERD tree"?? + +The NERD tree allows you to explore your filesystem and to open files and +directories. It presents the filesystem to you in the form of a tree which you +manipulate with the keyboard and/or mouse. It also allows you to perform +simple filesystem operations. + +The following features and functionality are provided by the NERD tree: + * Files and directories are displayed in a hierarchical tree structure + * Different highlighting is provided for the following types of nodes: + * files + * directories + * sym-links + * windows .lnk files + * read-only files + * executable files + * Many (customisable) mappings are provided to manipulate the tree: + * Mappings to open/close/explore directory nodes + * Mappings to open files in new/existing windows/tabs + * Mappings to change the current root of the tree + * Mappings to navigate around the tree + * ... + * Directories and files can be bookmarked. + * Most NERD tree navigation can also be done with the mouse + * Filtering of tree content (can be toggled at runtime) + * custom file filters to prevent e.g. vim backup files being displayed + * optional displaying of hidden files (. files) + * files can be "turned off" so that only directories are displayed + * The position and size of the NERD tree window can be customised + * The order in which the nodes in the tree are listed can be customised. + * A model of your filesystem is created/maintained as you explore it. This + has several advantages: + * All filesystem information is cached and is only re-read on demand + * If you revisit a part of the tree that you left earlier in your + session, the directory nodes will be opened/closed as you left them + * The script remembers the cursor position and window position in the NERD + tree so you can toggle it off (or just close the tree window) and then + reopen it (with NERDTreeToggle) the NERD tree window will appear exactly + as you left it + * You can have a separate NERD tree for each tab, share trees across tabs, + or a mix of both. + * By default the script overrides the default file browser (netw), so if + you :edit a directory a (slighly modified) NERD tree will appear in the + current window + * A programmable menu system is provided (simulates right clicking on a + node) + * one default menu plugin is provided to perform basic filesytem + operations (create/delete/move/copy files/directories) + * There's an API for adding your own keymappings + + +============================================================================== +2. Functionality provided *NERDTreeFunctionality* + +------------------------------------------------------------------------------ +2.1. Global Commands *NERDTreeGlobalCommands* + +:NERDTree [ | ] *:NERDTree* + Opens a fresh NERD tree. The root of the tree depends on the argument + given. There are 3 cases: If no argument is given, the current directory + will be used. If a directory is given, that will be used. If a bookmark + name is given, the corresponding directory will be used. For example: > + :NERDTree /home/marty/vim7/src + :NERDTree foo (foo is the name of a bookmark) +< +:NERDTreeFromBookmark *:NERDTreeFromBookmark* + Opens a fresh NERD tree with the root initialized to the dir for + . This only reason to use this command over :NERDTree is for + the completion (which is for bookmarks rather than directories). + +:NERDTreeToggle [ | ] *:NERDTreeToggle* + If a NERD tree already exists for this tab, it is reopened and rendered + again. If no NERD tree exists for this tab then this command acts the + same as the |:NERDTree| command. + +:NERDTreeMirror *:NERDTreeMirror* + Shares an existing NERD tree, from another tab, in the current tab. + Changes made to one tree are reflected in both as they are actually the + same buffer. + + If only one other NERD tree exists, that tree is automatically mirrored. If + more than one exists, the script will ask which tree to mirror. + +:NERDTreeClose *:NERDTreeClose* + Close the NERD tree in this tab. + +:NERDTreeFind *:NERDTreeFind* + Find the current file in the tree. If no tree exists for the current tab, + or the file is not under the current root, then initialize a new tree where + the root is the directory of the current file. + +------------------------------------------------------------------------------ +2.2. Bookmarks *NERDTreeBookmarks* + +Bookmarks in the NERD tree are a way to tag files or directories of interest. +For example, you could use bookmarks to tag all of your project directories. + +------------------------------------------------------------------------------ +2.2.1. The Bookmark Table *NERDTreeBookmarkTable* + +If the bookmark table is active (see |NERDTree-B| and +|'NERDTreeShowBookmarks'|), it will be rendered above the tree. You can double +click bookmarks or use the |NERDTree-o| mapping to activate them. See also, +|NERDTree-t| and |NERDTree-T| + +------------------------------------------------------------------------------ +2.2.2. Bookmark commands *NERDTreeBookmarkCommands* + +Note that the following commands are only available in the NERD tree buffer. + +:Bookmark + Bookmark the current node as . If there is already a + bookmark, it is overwritten. must not contain spaces. + +:BookmarkToRoot + Make the directory corresponding to the new root. If a treenode + corresponding to is already cached somewhere in the tree then + the current tree will be used, otherwise a fresh tree will be opened. + Note that if points to a file then its parent will be used + instead. + +:RevealBookmark + If the node is cached under the current root then it will be revealed + (i.e. directory nodes above it will be opened) and the cursor will be + placed on it. + +:OpenBookmark + must point to a file. The file is opened as though |NERDTree-o| + was applied. If the node is cached under the current root then it will be + revealed and the cursor will be placed on it. + +:ClearBookmarks [] + Remove all the given bookmarks. If no bookmarks are given then remove all + bookmarks on the current node. + +:ClearAllBookmarks + Remove all bookmarks. + +:ReadBookmarks + Re-read the bookmarks in the |'NERDTreeBookmarksFile'|. + +See also |:NERDTree| and |:NERDTreeFromBookmark|. + +------------------------------------------------------------------------------ +2.2.3. Invalid Bookmarks *NERDTreeInvalidBookmarks* + +If invalid bookmarks are detected, the script will issue an error message and +the invalid bookmarks will become unavailable for use. + +These bookmarks will still be stored in the bookmarks file (see +|'NERDTreeBookmarksFile'|), down the bottom. There will always be a blank line +after the valid bookmarks but before the invalid ones. + +Each line in the bookmarks file represents one bookmark. The proper format is: + + +After you have corrected any invalid bookmarks, either restart vim, or go +:ReadBookmarks from the NERD tree window. + +------------------------------------------------------------------------------ +2.3. NERD tree Mappings *NERDTreeMappings* + +Default Description~ help-tag~ +Key~ + +o.......Open files, directories and bookmarks....................|NERDTree-o| +go......Open selected file, but leave cursor in the NERDTree.....|NERDTree-go| +t.......Open selected node/bookmark in a new tab.................|NERDTree-t| +T.......Same as 't' but keep the focus on the current tab........|NERDTree-T| +i.......Open selected file in a split window.....................|NERDTree-i| +gi......Same as i, but leave the cursor on the NERDTree..........|NERDTree-gi| +s.......Open selected file in a new vsplit.......................|NERDTree-s| +gs......Same as s, but leave the cursor on the NERDTree..........|NERDTree-gs| +O.......Recursively open the selected directory..................|NERDTree-O| +x.......Close the current nodes parent...........................|NERDTree-x| +X.......Recursively close all children of the current node.......|NERDTree-X| +e.......Edit the current dif.....................................|NERDTree-e| + +...............same as |NERDTree-o|. +double-click.......same as the |NERDTree-o| map. +middle-click.......same as |NERDTree-i| for files, same as + |NERDTree-e| for dirs. + +D.......Delete the current bookmark .............................|NERDTree-D| + +P.......Jump to the root node....................................|NERDTree-P| +p.......Jump to current nodes parent.............................|NERDTree-p| +K.......Jump up inside directories at the current tree depth.....|NERDTree-K| +J.......Jump down inside directories at the current tree depth...|NERDTree-J| +...Jump down to the next sibling of the current directory...|NERDTree-C-J| +...Jump up to the previous sibling of the current directory.|NERDTree-C-K| + +C.......Change the tree root to the selected dir.................|NERDTree-C| +u.......Move the tree root up one directory......................|NERDTree-u| +U.......Same as 'u' except the old root node is left open........|NERDTree-U| +r.......Recursively refresh the current directory................|NERDTree-r| +R.......Recursively refresh the current root.....................|NERDTree-R| +m.......Display the NERD tree menu...............................|NERDTree-m| +cd......Change the CWD to the dir of the selected node...........|NERDTree-cd| + +I.......Toggle whether hidden files displayed....................|NERDTree-I| +f.......Toggle whether the file filters are used.................|NERDTree-f| +F.......Toggle whether files are displayed.......................|NERDTree-F| +B.......Toggle whether the bookmark table is displayed...........|NERDTree-B| + +q.......Close the NERDTree window................................|NERDTree-q| +A.......Zoom (maximize/minimize) the NERDTree window.............|NERDTree-A| +?.......Toggle the display of the quick help.....................|NERDTree-?| + +------------------------------------------------------------------------------ + *NERDTree-o* +Default key: o +Map option: NERDTreeMapActivateNode +Applies to: files and directories. + +If a file node is selected, it is opened in the previous window. + +If a directory is selected it is opened or closed depending on its current +state. + +If a bookmark that links to a directory is selected then that directory +becomes the new root. + +If a bookmark that links to a file is selected then that file is opened in the +previous window. + +------------------------------------------------------------------------------ + *NERDTree-go* +Default key: go +Map option: None +Applies to: files. + +If a file node is selected, it is opened in the previous window, but the +cursor does not move. + +The key combo for this mapping is always "g" + NERDTreeMapActivateNode (see +|NERDTree-o|). + +------------------------------------------------------------------------------ + *NERDTree-t* +Default key: t +Map option: NERDTreeMapOpenInTab +Applies to: files and directories. + +Opens the selected file in a new tab. If a directory is selected, a fresh +NERD Tree for that directory is opened in a new tab. + +If a bookmark which points to a directory is selected, open a NERD tree for +that directory in a new tab. If the bookmark points to a file, open that file +in a new tab. + +------------------------------------------------------------------------------ + *NERDTree-T* +Default key: T +Map option: NERDTreeMapOpenInTabSilent +Applies to: files and directories. + +The same as |NERDTree-t| except that the focus is kept in the current tab. + +------------------------------------------------------------------------------ + *NERDTree-i* +Default key: i +Map option: NERDTreeMapOpenSplit +Applies to: files. + +Opens the selected file in a new split window and puts the cursor in the new +window. + +------------------------------------------------------------------------------ + *NERDTree-gi* +Default key: gi +Map option: None +Applies to: files. + +The same as |NERDTree-i| except that the cursor is not moved. + +The key combo for this mapping is always "g" + NERDTreeMapOpenSplit (see +|NERDTree-i|). + +------------------------------------------------------------------------------ + *NERDTree-s* +Default key: s +Map option: NERDTreeMapOpenVSplit +Applies to: files. + +Opens the selected file in a new vertically split window and puts the cursor in +the new window. + +------------------------------------------------------------------------------ + *NERDTree-gs* +Default key: gs +Map option: None +Applies to: files. + +The same as |NERDTree-s| except that the cursor is not moved. + +The key combo for this mapping is always "g" + NERDTreeMapOpenVSplit (see +|NERDTree-s|). + +------------------------------------------------------------------------------ + *NERDTree-O* +Default key: O +Map option: NERDTreeMapOpenRecursively +Applies to: directories. + +Recursively opens the selelected directory. + +All files and directories are cached, but if a directory would not be +displayed due to file filters (see |'NERDTreeIgnore'| |NERDTree-f|) or the +hidden file filter (see |'NERDTreeShowHidden'|) then its contents are not +cached. This is handy, especially if you have .svn directories. + +------------------------------------------------------------------------------ + *NERDTree-x* +Default key: x +Map option: NERDTreeMapCloseDir +Applies to: files and directories. + +Closes the parent of the selected node. + +------------------------------------------------------------------------------ + *NERDTree-X* +Default key: X +Map option: NERDTreeMapCloseChildren +Applies to: directories. + +Recursively closes all children of the selected directory. + +Tip: To quickly "reset" the tree, use |NERDTree-P| with this mapping. + +------------------------------------------------------------------------------ + *NERDTree-e* +Default key: e +Map option: NERDTreeMapOpenExpl +Applies to: files and directories. + +|:edit|s the selected directory, or the selected file's directory. This could +result in a NERD tree or a netrw being opened, depending on +|'NERDTreeHijackNetrw'|. + +------------------------------------------------------------------------------ + *NERDTree-D* +Default key: D +Map option: NERDTreeMapDeleteBookmark +Applies to: lines in the bookmarks table + +Deletes the currently selected bookmark. + +------------------------------------------------------------------------------ + *NERDTree-P* +Default key: P +Map option: NERDTreeMapJumpRoot +Applies to: no restrictions. + +Jump to the tree root. + +------------------------------------------------------------------------------ + *NERDTree-p* +Default key: p +Map option: NERDTreeMapJumpParent +Applies to: files and directories. + +Jump to the parent node of the selected node. + +------------------------------------------------------------------------------ + *NERDTree-K* +Default key: K +Map option: NERDTreeMapJumpFirstChild +Applies to: files and directories. + +Jump to the first child of the current nodes parent. + +If the cursor is already on the first node then do the following: + * loop back thru the siblings of the current nodes parent until we find an + open dir with children + * go to the first child of that node + +------------------------------------------------------------------------------ + *NERDTree-J* +Default key: J +Map option: NERDTreeMapJumpLastChild +Applies to: files and directories. + +Jump to the last child of the current nodes parent. + +If the cursor is already on the last node then do the following: + * loop forward thru the siblings of the current nodes parent until we find + an open dir with children + * go to the last child of that node + +------------------------------------------------------------------------------ + *NERDTree-C-J* +Default key: +Map option: NERDTreeMapJumpNextSibling +Applies to: files and directories. + +Jump to the next sibling of the selected node. + +------------------------------------------------------------------------------ + *NERDTree-C-K* +Default key: +Map option: NERDTreeMapJumpPrevSibling +Applies to: files and directories. + +Jump to the previous sibling of the selected node. + +------------------------------------------------------------------------------ + *NERDTree-C* +Default key: C +Map option: NERDTreeMapChdir +Applies to: directories. + +Make the selected directory node the new tree root. If a file is selected, its +parent is used. + +------------------------------------------------------------------------------ + *NERDTree-u* +Default key: u +Map option: NERDTreeMapUpdir +Applies to: no restrictions. + +Move the tree root up a dir (like doing a "cd .."). + +------------------------------------------------------------------------------ + *NERDTree-U* +Default key: U +Map option: NERDTreeMapUpdirKeepOpen +Applies to: no restrictions. + +Like |NERDTree-u| except that the old tree root is kept open. + +------------------------------------------------------------------------------ + *NERDTree-r* +Default key: r +Map option: NERDTreeMapRefresh +Applies to: files and directories. + +If a dir is selected, recursively refresh that dir, i.e. scan the filesystem +for changes and represent them in the tree. + +If a file node is selected then the above is done on it's parent. + +------------------------------------------------------------------------------ + *NERDTree-R* +Default key: R +Map option: NERDTreeMapRefreshRoot +Applies to: no restrictions. + +Recursively refresh the tree root. + +------------------------------------------------------------------------------ + *NERDTree-m* +Default key: m +Map option: NERDTreeMapMenu +Applies to: files and directories. + +Display the NERD tree menu. See |NERDTreeMenu| for details. + +------------------------------------------------------------------------------ + *NERDTree-cd* +Default key: cd +Map option: NERDTreeMapChdir +Applies to: files and directories. + +Change vims current working directory to that of the selected node. + +------------------------------------------------------------------------------ + *NERDTree-I* +Default key: I +Map option: NERDTreeMapToggleHidden +Applies to: no restrictions. + +Toggles whether hidden files (i.e. "dot files") are displayed. + +------------------------------------------------------------------------------ + *NERDTree-f* +Default key: f +Map option: NERDTreeMapToggleFilters +Applies to: no restrictions. + +Toggles whether file filters are used. See |'NERDTreeIgnore'| for details. + +------------------------------------------------------------------------------ + *NERDTree-F* +Default key: F +Map option: NERDTreeMapToggleFiles +Applies to: no restrictions. + +Toggles whether file nodes are displayed. + +------------------------------------------------------------------------------ + *NERDTree-B* +Default key: B +Map option: NERDTreeMapToggleBookmarks +Applies to: no restrictions. + +Toggles whether the bookmarks table is displayed. + +------------------------------------------------------------------------------ + *NERDTree-q* +Default key: q +Map option: NERDTreeMapQuit +Applies to: no restrictions. + +Closes the NERDtree window. + +------------------------------------------------------------------------------ + *NERDTree-A* +Default key: A +Map option: NERDTreeMapToggleZoom +Applies to: no restrictions. + +Maximize (zoom) and minimize the NERDtree window. + +------------------------------------------------------------------------------ + *NERDTree-?* +Default key: ? +Map option: NERDTreeMapHelp +Applies to: no restrictions. + +Toggles whether the quickhelp is displayed. + +------------------------------------------------------------------------------ +2.3. The NERD tree menu *NERDTreeMenu* + +The NERD tree has a menu that can be programmed via the an API (see +|NERDTreeMenuAPI|). The idea is to simulate the "right click" menus that most +file explorers have. + +The script comes with two default menu plugins: exec_menuitem.vim and +fs_menu.vim. fs_menu.vim adds some basic filesystem operations to the menu for +creating/deleting/moving/copying files and dirs. exec_menuitem.vim provides a +menu item to execute executable files. + +Related tags: |NERDTree-m| |NERDTreeApi| + +============================================================================== +3. Customisation *NERDTreeOptions* + + +------------------------------------------------------------------------------ +3.1. Customisation summary *NERDTreeOptionSummary* + +The script provides the following options that can customise the behaviour the +NERD tree. These options should be set in your vimrc. + +|'loaded_nerd_tree'| Turns off the script. + +|'NERDChristmasTree'| Tells the NERD tree to make itself colourful + and pretty. + +|'NERDTreeAutoCenter'| Controls whether the NERD tree window centers + when the cursor moves within a specified + distance to the top/bottom of the window. +|'NERDTreeAutoCenterThreshold'| Controls the sensitivity of autocentering. + +|'NERDTreeCaseSensitiveSort'| Tells the NERD tree whether to be case + sensitive or not when sorting nodes. + +|'NERDTreeChDirMode'| Tells the NERD tree if/when it should change + vim's current working directory. + +|'NERDTreeHighlightCursorline'| Tell the NERD tree whether to highlight the + current cursor line. + +|'NERDTreeHijackNetrw'| Tell the NERD tree whether to replace the netrw + autocommands for exploring local directories. + +|'NERDTreeIgnore'| Tells the NERD tree which files to ignore. + +|'NERDTreeBookmarksFile'| Where the bookmarks are stored. + +|'NERDTreeMouseMode'| Tells the NERD tree how to handle mouse + clicks. + +|'NERDTreeQuitOnOpen'| Closes the tree window after opening a file. + +|'NERDTreeShowBookmarks'| Tells the NERD tree whether to display the + bookmarks table on startup. + +|'NERDTreeShowFiles'| Tells the NERD tree whether to display files + in the tree on startup. + +|'NERDTreeShowHidden'| Tells the NERD tree whether to display hidden + files on startup. + +|'NERDTreeShowLineNumbers'| Tells the NERD tree whether to display line + numbers in the tree window. + +|'NERDTreeSortOrder'| Tell the NERD tree how to sort the nodes in + the tree. + +|'NERDTreeStatusline'| Set a statusline for NERD tree windows. + +|'NERDTreeWinPos'| Tells the script where to put the NERD tree + window. + +|'NERDTreeWinSize'| Sets the window size when the NERD tree is + opened. + +------------------------------------------------------------------------------ +3.2. Customisation details *NERDTreeOptionDetails* + +To enable any of the below options you should put the given line in your +~/.vimrc + + *'loaded_nerd_tree'* +If this plugin is making you feel homicidal, it may be a good idea to turn it +off with this line in your vimrc: > + let loaded_nerd_tree=1 +< +------------------------------------------------------------------------------ + *'NERDChristmasTree'* +Values: 0 or 1. +Default: 1. + +If this option is set to 1 then some extra syntax highlighting elements are +added to the nerd tree to make it more colourful. + +Set it to 0 for a more vanilla looking tree. + +------------------------------------------------------------------------------ + *'NERDTreeAutoCenter'* +Values: 0 or 1. +Default: 1 + +If set to 1, the NERD tree window will center around the cursor if it moves to +within |'NERDTreeAutoCenterThreshold'| lines of the top/bottom of the window. + +This is ONLY done in response to tree navigation mappings, +i.e. |NERDTree-J| |NERDTree-K| |NERDTree-C-J| |NERDTree-C-K| |NERDTree-p| +|NERDTree-P| + +The centering is done with a |zz| operation. + +------------------------------------------------------------------------------ + *'NERDTreeAutoCenterThreshold'* +Values: Any natural number. +Default: 3 + +This option controls the "sensitivity" of the NERD tree auto centering. See +|'NERDTreeAutoCenter'| for details. + +------------------------------------------------------------------------------ + *'NERDTreeCaseSensitiveSort'* +Values: 0 or 1. +Default: 0. + +By default the NERD tree does not sort nodes case sensitively, i.e. nodes +could appear like this: > + bar.c + Baz.c + blarg.c + boner.c + Foo.c +< +But, if you set this option to 1 then the case of the nodes will be taken into +account. The above nodes would then be sorted like this: > + Baz.c + Foo.c + bar.c + blarg.c + boner.c +< +------------------------------------------------------------------------------ + *'NERDTreeChDirMode'* + +Values: 0, 1 or 2. +Default: 0. + +Use this option to tell the script when (if at all) to change the current +working directory (CWD) for vim. + +If it is set to 0 then the CWD is never changed by the NERD tree. + +If set to 1 then the CWD is changed when the NERD tree is first loaded to the +directory it is initialized in. For example, if you start the NERD tree with > + :NERDTree /home/marty/foobar +< +then the CWD will be changed to /home/marty/foobar and will not be changed +again unless you init another NERD tree with a similar command. + +If the option is set to 2 then it behaves the same as if set to 1 except that +the CWD is changed whenever the tree root is changed. For example, if the CWD +is /home/marty/foobar and you make the node for /home/marty/foobar/baz the new +root then the CWD will become /home/marty/foobar/baz. + +------------------------------------------------------------------------------ + *'NERDTreeHighlightCursorline'* +Values: 0 or 1. +Default: 1. + +If set to 1, the current cursor line in the NERD tree buffer will be +highlighted. This is done using the |'cursorline'| option. + +------------------------------------------------------------------------------ + *'NERDTreeHijackNetrw'* +Values: 0 or 1. +Default: 1. + +If set to 1, doing a > + :edit +< +will open up a "secondary" NERD tree instead of a netrw in the target window. + +Secondary NERD trees behaves slighly different from a regular trees in the +following respects: + 1. 'o' will open the selected file in the same window as the tree, + replacing it. + 2. you can have as many secondary tree as you want in the same tab. + +------------------------------------------------------------------------------ + *'NERDTreeIgnore'* +Values: a list of regular expressions. +Default: ['\~$']. + +This option is used to specify which files the NERD tree should ignore. It +must be a list of regular expressions. When the NERD tree is rendered, any +files/dirs that match any of the regex's in 'NERDTreeIgnore' wont be +displayed. + +For example if you put the following line in your vimrc: > + let NERDTreeIgnore=['\.vim$', '\~$'] +< +then all files ending in .vim or ~ will be ignored. + +Note: to tell the NERD tree not to ignore any files you must use the following +line: > + let NERDTreeIgnore=[] +< + +The file filters can be turned on and off dynamically with the |NERDTree-f| +mapping. + +------------------------------------------------------------------------------ + *'NERDTreeBookmarksFile'* +Values: a path +Default: $HOME/.NERDTreeBookmarks + +This is where bookmarks are saved. See |NERDTreeBookmarkCommands|. + +------------------------------------------------------------------------------ + *'NERDTreeMouseMode'* +Values: 1, 2 or 3. +Default: 1. + +If set to 1 then a double click on a node is required to open it. +If set to 2 then a single click will open directory nodes, while a double +click will still be required for file nodes. +If set to 3 then a single click will open any node. + +Note: a double click anywhere on a line that a tree node is on will +activate it, but all single-click activations must be done on name of the node +itself. For example, if you have the following node: > + | | |-application.rb +< +then (to single click activate it) you must click somewhere in +'application.rb'. + +------------------------------------------------------------------------------ + *'NERDTreeQuitOnOpen'* + +Values: 0 or 1. +Default: 0 + +If set to 1, the NERD tree window will close after opening a file with the +|NERDTree-o|, |NERDTree-i|, |NERDTree-t| and |NERDTree-T| mappings. + +------------------------------------------------------------------------------ + *'NERDTreeShowBookmarks'* +Values: 0 or 1. +Default: 0. + +If this option is set to 1 then the bookmarks table will be displayed. + +This option can be toggled dynamically, per tree, with the |NERDTree-B| +mapping. + +------------------------------------------------------------------------------ + *'NERDTreeShowFiles'* +Values: 0 or 1. +Default: 1. + +If this option is set to 1 then files are displayed in the NERD tree. If it is +set to 0 then only directories are displayed. + +This option can be toggled dynamically, per tree, with the |NERDTree-F| +mapping and is useful for drastically shrinking the tree when you are +navigating to a different part of the tree. + +------------------------------------------------------------------------------ + *'NERDTreeShowHidden'* +Values: 0 or 1. +Default: 0. + +This option tells vim whether to display hidden files by default. This option +can be dynamically toggled, per tree, with the |NERDTree-I| mapping. Use one +of the follow lines to set this option: > + let NERDTreeShowHidden=0 + let NERDTreeShowHidden=1 +< + +------------------------------------------------------------------------------ + *'NERDTreeShowLineNumbers'* +Values: 0 or 1. +Default: 0. + +This option tells vim whether to display line numbers for the NERD tree +window. Use one of the follow lines to set this option: > + let NERDTreeShowLineNumbers=0 + let NERDTreeShowLineNumbers=1 +< + +------------------------------------------------------------------------------ + *'NERDTreeSortOrder'* +Values: a list of regular expressions. +Default: ['\/$', '*', '\.swp$', '\.bak$', '\~$'] + +This option is set to a list of regular expressions which are used to +specify the order of nodes under their parent. + +For example, if the option is set to: > + ['\.vim$', '\.c$', '\.h$', '*', 'foobar'] +< +then all .vim files will be placed at the top, followed by all .c files then +all .h files. All files containing the string 'foobar' will be placed at the +end. The star is a special flag: it tells the script that every node that +doesnt match any of the other regexps should be placed here. + +If no star is present in 'NERDTreeSortOrder' then one is automatically +appended to the array. + +The regex '\/$' should be used to match directory nodes. + +After this sorting is done, the files in each group are sorted alphabetically. + +Other examples: > + (1) ['*', '\/$'] + (2) [] + (3) ['\/$', '\.rb$', '\.php$', '*', '\.swp$', '\.bak$', '\~$'] +< +1. Directories will appear last, everything else will appear above. +2. Everything will simply appear in alphabetical order. +3. Dirs will appear first, then ruby and php. Swap files, bak files and vim + backup files will appear last with everything else preceding them. + +------------------------------------------------------------------------------ + *'NERDTreeStatusline'* +Values: Any valid statusline setting. +Default: %{b:NERDTreeRoot.path.strForOS(0)} + +Tells the script what to use as the |'statusline'| setting for NERD tree +windows. + +Note that the statusline is set using |:let-&| not |:set| so escaping spaces +isn't necessary. + +Setting this option to -1 will will deactivate it so that your global +statusline setting is used instead. + +------------------------------------------------------------------------------ + *'NERDTreeWinPos'* +Values: "left" or "right" +Default: "left". + +This option is used to determine where NERD tree window is placed on the +screen. + +This option makes it possible to use two different explorer plugins +simultaneously. For example, you could have the taglist plugin on the left of +the window and the NERD tree on the right. + +------------------------------------------------------------------------------ + *'NERDTreeWinSize'* +Values: a positive integer. +Default: 31. + +This option is used to change the size of the NERD tree when it is loaded. + +============================================================================== +4. The NERD tree API *NERDTreeAPI* + +The NERD tree script allows you to add custom key mappings and menu items via +a set of API calls. Any scripts that use this API should be placed in +~/.vim/nerdtree_plugin/ (*nix) or ~/vimfiles/nerdtree_plugin (windows). + +The script exposes some prototype objects that can be used to manipulate the +tree and/or get information from it: > + g:NERDTreePath + g:NERDTreeDirNode + g:NERDTreeFileNode + g:NERDTreeBookmark +< +See the code/comments in NERD_tree.vim to find how to use these objects. The +following code conventions are used: + * class members start with a capital letter + * instance members start with a lower case letter + * private members start with an underscore + +See this blog post for more details: + http://got-ravings.blogspot.com/2008/09/vim-pr0n-prototype-based-objects.html + +------------------------------------------------------------------------------ +4.1. Key map API *NERDTreeKeymapAPI* + +NERDTreeAddKeyMap({options}) *NERDTreeAddKeyMap()* + Adds a new keymapping for all NERD tree buffers. + {options} must be a dictionary, and must contain the following keys: + "key" - the trigger key for the new mapping + "callback" - the function the new mapping will be bound to + "quickhelpText" - the text that will appear in the quickhelp (see + |NERDTree-?|) + + Example: > + call NERDTreeAddKeyMap({ + \ 'key': 'b', + \ 'callback': 'NERDTreeEchoCurrentNode', + \ 'quickhelpText': 'echo full path of current node' }) + + function! NERDTreeEchoCurrentNode() + let n = g:NERDTreeFileNode.GetSelected() + if n != {} + echomsg 'Current node: ' . n.path.str() + endif + endfunction +< + This code should sit in a file like ~/.vim/nerdtree_plugin/mymapping.vim. + It adds a (rather useless) mapping on 'b' which echos the full path to the + current node. + +------------------------------------------------------------------------------ +4.2. Menu API *NERDTreeMenuAPI* + +NERDTreeAddSubmenu({options}) *NERDTreeAddSubmenu()* + Creates and returns a new submenu. + + {options} must be a dictionary and must contain the following keys: + "text" - the text of the submenu that the user will see + "shortcut" - a shortcut key for the submenu (need not be unique) + + The following keys are optional: + "isActiveCallback" - a function that will be called to determine whether + this submenu item will be displayed or not. The callback function must return + 0 or 1. + "parent" - the parent submenu of the new submenu (returned from a previous + invocation of NERDTreeAddSubmenu()). If this key is left out then the new + submenu will sit under the top level menu. + + See below for an example. + +NERDTreeAddMenuItem({options}) *NERDTreeAddMenuItem()* + Adds a new menu item to the NERD tree menu (see |NERDTreeMenu|). + + {options} must be a dictionary and must contain the + following keys: + "text" - the text of the menu item which the user will see + "shortcut" - a shortcut key for the menu item (need not be unique) + "callback" - the function that will be called when the user activates the + menu item. + + The following keys are optional: + "isActiveCallback" - a function that will be called to determine whether + this menu item will be displayed or not. The callback function must return + 0 or 1. + "parent" - if the menu item belongs under a submenu then this key must be + specified. This value for this key will be the object that + was returned when the submenu was created with |NERDTreeAddSubmenu()|. + + See below for an example. + +NERDTreeAddMenuSeparator([{options}]) *NERDTreeAddMenuSeparator()* + Adds a menu separator (a row of dashes). + + {options} is an optional dictionary that may contain the following keys: + "isActiveCallback" - see description in |NERDTreeAddMenuItem()|. + +Below is an example of the menu API in action. > + call NERDTreeAddMenuSeparator() + + call NERDTreeAddMenuItem({ + \ 'text': 'a (t)op level menu item', + \ 'shortcut': 't', + \ 'callback': 'SomeFunction' }) + + let submenu = NERDTreeAddSubmenu({ + \ 'text': 'a (s)ub menu', + \ 'shortcut': 's' }) + + call NERDTreeAddMenuItem({ + \ 'text': '(n)ested item 1', + \ 'shortcut': 'n', + \ 'callback': 'SomeFunction', + \ 'parent': submenu }) + + call NERDTreeAddMenuItem({ + \ 'text': '(n)ested item 2', + \ 'shortcut': 'n', + \ 'callback': 'SomeFunction', + \ 'parent': submenu }) +< +This will create the following menu: > + -------------------- + a (t)op level menu item + a (s)ub menu +< +Where selecting "a (s)ub menu" will lead to a second menu: > + (n)ested item 1 + (n)ested item 2 +< +When any of the 3 concrete menu items are selected the function "SomeFunction" +will be called. + +------------------------------------------------------------------------------ +NERDTreeRender() *NERDTreeRender()* + Re-renders the NERD tree buffer. Useful if you change the state of the + tree and you want to it to be reflected in the UI. + +============================================================================== +5. About *NERDTreeAbout* + +The author of the NERD tree is a terrible terrible monster called Martyzilla +who gobbles up small children with milk and sugar for breakfast. + +He can be reached at martin.grenfell at gmail dot com. He would love to hear +from you, so feel free to send him suggestions and/or comments about this +plugin. Don't be shy --- the worst he can do is slaughter you and stuff you in +the fridge for later ;) + +The latest stable versions can be found at + http://www.vim.org/scripts/script.php?script_id=1658 + +The latest dev versions are on github + http://github.com/scrooloose/nerdtree + + +============================================================================== +6. Changelog *NERDTreeChangelog* + +4.1.0 + features: + - NERDTreeFind to reveal the node for the current buffer in the tree, + see |NERDTreeFind|. This effectively merges the FindInNERDTree plugin (by + Doug McInnes) into the script. + - make NERDTreeQuitOnOpen apply to the t/T keymaps too. Thanks to Stefan + Ritter and Rémi Prévost. + - truncate the root node if wider than the tree window. Thanks to Victor + Gonzalez. + + bugfixes: + - really fix window state restoring + - fix some win32 path escaping issues. Thanks to Stephan Baumeister, Ricky, + jfilip1024, and Chris Chambers + +4.0.0 + - add a new programmable menu system (see :help NERDTreeMenu). + - add new APIs to add menus/menu-items to the menu system as well as + custom key mappings to the NERD tree buffer (see :help NERDTreeAPI). + - removed the old API functions + - added a mapping to maximize/restore the size of nerd tree window, thanks + to Guillaume Duranceau for the patch. See :help NERDTree-A for details. + + - fix a bug where secondary nerd trees (netrw hijacked trees) and + NERDTreeQuitOnOpen didnt play nicely, thanks to Curtis Harvey. + - fix a bug where the script ignored directories whose name ended in a dot, + thanks to Aggelos Orfanakos for the patch. + - fix a bug when using the x mapping on the tree root, thanks to Bryan + Venteicher for the patch. + - fix a bug where the cursor position/window size of the nerd tree buffer + wasnt being stored on closing the window, thanks to Richard Hart. + - fix a bug where NERDTreeMirror would mirror the wrong tree + +3.1.1 + - fix a bug where a non-listed no-name buffer was getting created every + time the tree windows was created, thanks to Derek Wyatt and owen1 + - make behave the same as the 'o' mapping + - some helptag fixes in the doc, thanks strull + - fix a bug when using :set nohidden and opening a file where the previous + buf was modified. Thanks iElectric + - other minor fixes + +3.1.0 + New features: + - add mappings to open files in a vsplit, see :help NERDTree-s and :help + NERDTree-gs + - make the statusline for the nerd tree window default to something + hopefully more useful. See :help 'NERDTreeStatusline' + Bugfixes: + - make the hijack netrw functionality work when vim is started with "vim + " (thanks to Alf Mikula for the patch). + - fix a bug where the CWD wasnt being changed for some operations even when + NERDTreeChDirMode==2 (thanks to Lucas S. Buchala) + - add -bar to all the nerd tree :commands so they can chain with other + :commands (thanks to tpope) + - fix bugs when ignorecase was set (thanks to nach) + - fix a bug with the relative path code (thanks to nach) + - fix a bug where doing a :cd would cause :NERDTreeToggle to fail (thanks nach) + + +3.0.1 + Bugfixes: + - fix bugs with :NERDTreeToggle and :NERDTreeMirror when 'hidden + was not set + - fix a bug where :NERDTree would fail if was relative and + didnt start with a ./ or ../ Thanks to James Kanze. + - make the q mapping work with secondary (:e style) trees, + thanks to jamessan + - fix a bunch of small bugs with secondary trees + + More insane refactoring. + +3.0.0 + - hijack netrw so that doing an :edit will put a NERD tree in + the window rather than a netrw browser. See :help 'NERDTreeHijackNetrw' + - allow sharing of trees across tabs, see :help :NERDTreeMirror + - remove "top" and "bottom" as valid settings for NERDTreeWinPos + - change the '' mapping to 'i' + - change the 'H' mapping to 'I' + - lots of refactoring + +============================================================================== +7. Credits *NERDTreeCredits* + +Thanks to the following people for testing, bug reports, ideas etc. Without +you I probably would have got bored of the hacking the NERD tree and +just downloaded pr0n instead. + + Tim Carey-Smith (halorgium) + Vigil + Nick Brettell + Thomas Scott Urban + Terrance Cohen + Yegappan Lakshmanan + Jason Mills + Michael Geddes (frogonwheels) + Yu Jun + Michael Madsen + AOYAMA Shotaro + Zhang Weiwu + Niels Aan de Brugh + Olivier Yiptong + Zhang Shuhan + Cory Echols + Piotr Czachur + Yuan Jiang + Matan Nassau + Maxim Kim + Charlton Wang + Matt Wozniski (godlygeek) + knekk + Sean Chou + Ryan Penn + Simon Peter Nicholls + Michael Foobar + Tomasz Chomiuk + Denis Pokataev + Tim Pope (tpope) + James Kanze + James Vega (jamessan) + Frederic Chanal (nach) + Alf Mikula + Lucas S. Buchala + Curtis Harvey + Guillaume Duranceau + Richard Hart (hates) + Doug McInnes + Stefan Ritter + Rémi Prévost + Victor Gonzalez + Stephan Baumeister + Ricky + jfilip1024 + Chris Chambers + +============================================================================== +8. License *NERDTreeLicense* + +The NERD tree is released under the wtfpl. +See http://sam.zoy.org/wtfpl/COPYING. diff --git a/doc/acp.jax b/doc/acp.jax new file mode 100644 index 0000000..12e55ce --- /dev/null +++ b/doc/acp.jax @@ -0,0 +1,298 @@ +*acp.txt* 補完メニューの自動ポップアップ + + Copyright (c) 2007-2009 Takeshi NISHIDA + +AutoComplPop *autocomplpop* *acp* + +概要 |acp-introduction| +インストール |acp-installation| +使い方 |acp-usage| +コマンド |acp-commands| +オプション |acp-options| +SPECIAL THANKS |acp-thanks| +CHANGELOG |acp-changelog| +あばうと |acp-about| + + +============================================================================== +概要 *acp-introduction* + +このプラグインは、インサートモードで文字を入力したりカーソルを動かしたときに補 +完メニューを自動的に開くようにします。しかし、続けて文字を入力するのを妨げたり +はしません。 + + +============================================================================== +インストール *acp-installation* + +ZIPファイルをランタイムディレクトリに展開します。 + +以下のようにファイルが配置されるはずです。 +> + /plugin/acp.vim + /doc/acp.txt + ... +< +もしランタイムディレクトリが他のプラグインとごた混ぜになるのが嫌なら、ファイル +を新規ディレクトリに配置し、そのディレクトリのパスを 'runtimepath' に追加して +ください。アンインストールも楽になります。 + +その後 FuzzyFinder のヘルプを有効にするためにタグファイルを更新してください。 +詳しくは|add-local-help|を参照してください。 + + +============================================================================== +使い方 *acp-usage* + +このプラグインがインストールされていれば、自動ポップアップは vim の開始時から +有効になります。 + +カーソル直前のテキストに応じて、利用する補完の種類を切り替えます。デフォルトの +補完動作は次の通りです: + + 補完モード filetype カーソル直前のテキスト ~ + キーワード補完 * 2文字のキーワード文字 + ファイル名補完 * ファイル名文字 + パスセパレータ + + 0文字以上のファイル名文字 + オムニ補完 ruby ".", "::" or 単語を構成する文字以外 + ":" + オムニ補完 python "." + オムニ補完 xml "<", ""以外の文字列 + " ") + オムニ補完 html/xhtml "<", ""以外の文字列 + " ") + オムニ補完 css (":", ";", "{", "^", "@", or "!") + + 0個または1個のスペース + +さらに、設定を行うことで、ユーザー定義補完と snipMate トリガー補完 +(|acp-snipMate|) を自動ポップアップさせることができます。 + +これらの補完動作はカスタマイズ可能です。 + + *acp-snipMate* +snipMate トリガー補完 ~ + +snipMate トリガー補完では、snipMate プラグイン +(http://www.vim.org/scripts/script.php?script_id=2540) が提供するスニペットの +トリガーを補完してそれを展開することができます。 + +この自動ポップアップを有効にするには、次の関数を plugin/snipMate.vim に追加す +る必要があります: +> + fun! GetSnipsInCurrentScope() + let snips = {} + for scope in [bufnr('%')] + split(&ft, '\.') + ['_'] + call extend(snips, get(s:snippets, scope, {}), 'keep') + call extend(snips, get(s:multi_snips, scope, {}), 'keep') + endfor + return snips + endf +< +そして|g:acp_behaviorSnipmateLength|オプションを 1 にしてください。 + +この自動ポップアップには制限があり、カーソル直前の単語は大文字英字だけで構成さ +れていなければなりません。 + + *acp-perl-omni* +Perl オムニ補完 ~ + +AutoComplPop は perl-completion.vim +(http://www.vim.org/scripts/script.php?script_id=2852) をサポートしています。 + +この自動ポップアップを有効にするには、|g:acp_behaviorPerlOmniLength|オプション +を 0 以上にしてください。 + + +============================================================================== +コマンド *acp-commands* + + *:AcpEnable* +:AcpEnable + 自動ポップアップを有効にします。 + + *:AcpDisable* +:AcpDisable + 自動ポップアップを無効にします。 + + *:AcpLock* +:AcpLock + 自動ポップアップを一時的に停止します。 + + 別のスクリプトへの干渉を回避する目的なら、このコマンドと|:AcpUnlock| + を利用することを、|:AcpDisable|と|:AcpEnable| を利用するよりも推奨しま + す。 + + *:AcpUnlock* +:AcpUnlock + |:AcpLock| で停止された自動ポップアップを再開します。 + + +============================================================================== +オプション *acp-options* + + *g:acp_enableAtStartup* > + let g:acp_enableAtStartup = 1 +< + 真なら vim 開始時から自動ポップアップが有効になります。 + + *g:acp_mappingDriven* > + let g:acp_mappingDriven = 0 +< + 真なら|CursorMovedI|イベントではなくキーマッピングで自動ポップアップを + 行うようにします。カーソルを移動するたびに補完が行われることで重いなど + の不都合がある場合に利用してください。ただし他のプラグインとの相性問題 + や日本語入力での不具合が発生する可能性があります。(逆も然り。) + + *g:acp_ignorecaseOption* > + let g:acp_ignorecaseOption = 1 +< + 自動ポップアップ時に、'ignorecase' に一時的に設定する値 + + *g:acp_completeOption* > + let g:acp_completeOption = '.,w,b,k' +< + 自動ポップアップ時に、'complete' に一時的に設定する値 + + *g:acp_completeoptPreview* > + let g:acp_completeoptPreview = 0 +< + 真なら自動ポップアップ時に、 'completeopt' へ "preview" を追加します。 + + *g:acp_behaviorUserDefinedFunction* > + let g:acp_behaviorUserDefinedFunction = '' +< + ユーザー定義補完の|g:acp_behavior-completefunc|。空ならこの補完は行わ + れません。。 + + *g:acp_behaviorUserDefinedMeets* > + let g:acp_behaviorUserDefinedMeets = '' +< + ユーザー定義補完の|g:acp_behavior-meets|。空ならこの補完は行われません + 。 + + *g:acp_behaviorSnipmateLength* > + let g:acp_behaviorSnipmateLength = -1 +< + snipMate トリガー補完の自動ポップアップを行うのに必要なカーソルの直前 + のパターン。 + + *g:acp_behaviorKeywordCommand* > + let g:acp_behaviorKeywordCommand = "\" +< + キーワード補完のコマンド。このオプションには普通 "\" か "\" + を設定します。 + + *g:acp_behaviorKeywordLength* > + let g:acp_behaviorKeywordLength = 2 +< + キーワード補完の自動ポップアップを行うのに必要なカーソルの直前のキーワ + ード文字数。負数ならこの補完は行われません。 + + *g:acp_behaviorKeywordIgnores* > + let g:acp_behaviorKeywordIgnores = [] +< + 文字列のリスト。カーソル直前の単語がこれらの内いずれかの先頭部分にマッ + チする場合、この補完は行われません。 + + 例えば、 "get" で始まる補完キーワードが多過ぎて、"g", "ge", "get" を入 + 力したときの自動ポップアップがレスポンスの低下を引き起こしている場合、 + このオプションに ["get"] を設定することでそれを回避することができます。 + + *g:acp_behaviorFileLength* > + let g:acp_behaviorFileLength = 0 +< + ファイル名補完の自動ポップアップを行うのに必要なカーソルの直前のキーワ + ード文字数。負数ならこの補完は行われません。 + + *g:acp_behaviorRubyOmniMethodLength* > + let g:acp_behaviorRubyOmniMethodLength = 0 +< + メソッド補完のための、Ruby オムニ補完の自動ポップアップを行うのに必要 + なカーソルの直前のキーワード文字数。負数ならこの補完は行われません。 + + *g:acp_behaviorRubyOmniSymbolLength* > + let g:acp_behaviorRubyOmniSymbolLength = 1 +< + シンボル補完のための、Ruby オムニ補完の自動ポップアップを行うのに必要 + なカーソルの直前のキーワード文字数。負数ならこの補完は行われません。 + + *g:acp_behaviorPythonOmniLength* > + let g:acp_behaviorPythonOmniLength = 0 +< + Python オムニ補完の自動ポップアップを行うのに必要なカーソルの直前のキ + ーワード文字数。負数ならこの補完は行われません。 + + *g:acp_behaviorPerlOmniLength* > + let g:acp_behaviorPerlOmniLength = -1 +< + Perl オムニ補完の自動ポップアップを行うのに必要なカーソルの直前のキー + ワード文字数。負数ならこの補完は行われません。 + + See also: |acp-perl-omni| + + *g:acp_behaviorXmlOmniLength* > + let g:acp_behaviorXmlOmniLength = 0 +< + XML オムニ補完の自動ポップアップを行うのに必要なカーソルの直前のキーワ + ード文字数。負数ならこの補完は行われません。 + + *g:acp_behaviorHtmlOmniLength* > + let g:acp_behaviorHtmlOmniLength = 0 +< + HTML オムニ補完の自動ポップアップを行うのに必要なカーソルの直前のキー + ワード文字数。負数ならこの補完は行われません。 + + *g:acp_behaviorCssOmniPropertyLength* > + let g:acp_behaviorCssOmniPropertyLength = 1 +< + プロパティ補完のための、CSS オムニ補完の自動ポップアップを行うのに必要 + なカーソルの直前のキーワード文字数。負数ならこの補完は行われません。 + + *g:acp_behaviorCssOmniValueLength* > + let g:acp_behaviorCssOmniValueLength = 0 +< + 値補完のための、CSS オムニ補完の自動ポップアップを行うのに必要なカーソ + ルの直前のキーワード文字数。負数ならこの補完は行われません。 + + *g:acp_behavior* > + let g:acp_behavior = {} +< + + これは内部仕様がわかっている人向けのオプションで、他のオプションでの設 + 定より優先されます。 + + |Dictionary|型で、キーはファイルタイプに対応します。 '*' はデフォルト + を表します。値はリスト型です。補完候補が得られるまでリストの先頭アイテ + ムから順に評価します。各要素は|Dictionary|で詳細は次の通り: + + "command": *g:acp_behavior-command* + 補完メニューをポップアップするためのコマンド。 + + "completefunc": *g:acp_behavior-completefunc* + 'completefunc' に設定する関数。 "command" が "" のときだけ + 意味があります。 + + "meets": *g:acp_behavior-meets* + この補完を行うかどうかを判断する関数の名前。この関数はカーソル直前の + テキストを引数に取り、補完を行うなら非 0 の値を返します。 + + "onPopupClose": *g:acp_behavior-onPopupClose* + この補完のポップアップメニューが閉じられたときに呼ばれる関数の名前。 + この関数が 0 を返した場合、続いて行われる予定の補完は抑制されます。 + + "repeat": *g:acp_behavior-repeat* + 真なら最後の補完が自動的に繰り返されます。 + + +============================================================================== +あばうと *acp-about* *acp-contact* *acp-author* + +作者: Takeshi NISHIDA +ライセンス: MIT Licence +URL: http://www.vim.org/scripts/script.php?script_id=1879 + http://bitbucket.org/ns9tks/vim-autocomplpop/ + +バグや要望など ~ + +こちらへどうぞ: http://bitbucket.org/ns9tks/vim-autocomplpop/issues/ + +============================================================================== + vim:tw=78:ts=8:ft=help:norl: + diff --git a/doc/acp.txt b/doc/acp.txt new file mode 100644 index 0000000..324c88b --- /dev/null +++ b/doc/acp.txt @@ -0,0 +1,512 @@ +*acp.txt* Automatically opens popup menu for completions. + + Copyright (c) 2007-2009 Takeshi NISHIDA + +AutoComplPop *autocomplpop* *acp* + +INTRODUCTION |acp-introduction| +INSTALLATION |acp-installation| +USAGE |acp-usage| +COMMANDS |acp-commands| +OPTIONS |acp-options| +SPECIAL THANKS |acp-thanks| +CHANGELOG |acp-changelog| +ABOUT |acp-about| + + +============================================================================== +INTRODUCTION *acp-introduction* + +With this plugin, your vim comes to automatically opens popup menu for +completions when you enter characters or move the cursor in Insert mode. It +won't prevent you continuing entering characters. + + +============================================================================== +INSTALLATION *acp-installation* + +Put all files into your runtime directory. If you have the zip file, extract +it to your runtime directory. + +You should place the files as follows: +> + /plugin/acp.vim + /doc/acp.txt + ... +< +If you disgust to jumble up this plugin and other plugins in your runtime +directory, put the files into new directory and just add the directory path to +'runtimepath'. It's easy to uninstall the plugin. + +And then update your help tags files to enable fuzzyfinder help. See +|add-local-help| for details. + + +============================================================================== +USAGE *acp-usage* + +Once this plugin is installed, auto-popup is enabled at startup by default. + +Which completion method is used depends on the text before the cursor. The +default behavior is as follows: + + kind filetype text before the cursor ~ + Keyword * two keyword characters + Filename * a filename character + a path separator + + 0 or more filename character + Omni ruby ".", "::" or non-word character + ":" + (|+ruby| required.) + Omni python "." (|+python| required.) + Omni xml "<", "" characters + " ") + Omni html/xhtml "<", "" characters + " ") + Omni css (":", ";", "{", "^", "@", or "!") + + 0 or 1 space + +Also, you can make user-defined completion and snipMate's trigger completion +(|acp-snipMate|) auto-popup if the options are set. + +These behavior are customizable. + + *acp-snipMate* +snipMate's Trigger Completion ~ + +snipMate's trigger completion enables you to complete a snippet trigger +provided by snipMate plugin +(http://www.vim.org/scripts/script.php?script_id=2540) and expand it. + + +To enable auto-popup for this completion, add following function to +plugin/snipMate.vim: +> + fun! GetSnipsInCurrentScope() + let snips = {} + for scope in [bufnr('%')] + split(&ft, '\.') + ['_'] + call extend(snips, get(s:snippets, scope, {}), 'keep') + call extend(snips, get(s:multi_snips, scope, {}), 'keep') + endfor + return snips + endf +< +And set |g:acp_behaviorSnipmateLength| option to 1. + +There is the restriction on this auto-popup, that the word before cursor must +consist only of uppercase characters. + + *acp-perl-omni* +Perl Omni-Completion ~ + +AutoComplPop supports perl-completion.vim +(http://www.vim.org/scripts/script.php?script_id=2852). + +To enable auto-popup for this completion, set |g:acp_behaviorPerlOmniLength| +option to 0 or more. + + +============================================================================== +COMMANDS *acp-commands* + + *:AcpEnable* +:AcpEnable + enables auto-popup. + + *:AcpDisable* +:AcpDisable + disables auto-popup. + + *:AcpLock* +:AcpLock + suspends auto-popup temporarily. + + For the purpose of avoiding interruption to another script, it is + recommended to insert this command and |:AcpUnlock| than |:AcpDisable| + and |:AcpEnable| . + + *:AcpUnlock* +:AcpUnlock + resumes auto-popup suspended by |:AcpLock| . + + +============================================================================== +OPTIONS *acp-options* + + *g:acp_enableAtStartup* > + let g:acp_enableAtStartup = 1 +< + If non-zero, auto-popup is enabled at startup. + + *g:acp_mappingDriven* > + let g:acp_mappingDriven = 0 +< + If non-zero, auto-popup is triggered by key mappings instead of + |CursorMovedI| event. This is useful to avoid auto-popup by moving + cursor in Insert mode. + + *g:acp_ignorecaseOption* > + let g:acp_ignorecaseOption = 1 +< + Value set to 'ignorecase' temporarily when auto-popup. + + *g:acp_completeOption* > + let g:acp_completeOption = '.,w,b,k' +< + Value set to 'complete' temporarily when auto-popup. + + *g:acp_completeoptPreview* > + let g:acp_completeoptPreview = 0 +< + If non-zero, "preview" is added to 'completeopt' when auto-popup. + + *g:acp_behaviorUserDefinedFunction* > + let g:acp_behaviorUserDefinedFunction = '' +< + |g:acp_behavior-completefunc| for user-defined completion. If empty, + this completion will be never attempted. + + *g:acp_behaviorUserDefinedMeets* > + let g:acp_behaviorUserDefinedMeets = '' +< + |g:acp_behavior-meets| for user-defined completion. If empty, this + completion will be never attempted. + + *g:acp_behaviorSnipmateLength* > + let g:acp_behaviorSnipmateLength = -1 +< + Pattern before the cursor, which are needed to attempt + snipMate-trigger completion. + + *g:acp_behaviorKeywordCommand* > + let g:acp_behaviorKeywordCommand = "\" +< + Command for keyword completion. This option is usually set "\" or + "\". + + *g:acp_behaviorKeywordLength* > + let g:acp_behaviorKeywordLength = 2 +< + Length of keyword characters before the cursor, which are needed to + attempt keyword completion. If negative value, this completion will be + never attempted. + + *g:acp_behaviorKeywordIgnores* > + let g:acp_behaviorKeywordIgnores = [] +< + List of string. If a word before the cursor matches to the front part + of one of them, keyword completion won't be attempted. + + E.g., when there are too many keywords beginning with "get" for the + completion and auto-popup by entering "g", "ge", or "get" causes + response degradation, set ["get"] to this option and avoid it. + + *g:acp_behaviorFileLength* > + let g:acp_behaviorFileLength = 0 +< + Length of filename characters before the cursor, which are needed to + attempt filename completion. If negative value, this completion will + be never attempted. + + *g:acp_behaviorRubyOmniMethodLength* > + let g:acp_behaviorRubyOmniMethodLength = 0 +< + Length of keyword characters before the cursor, which are needed to + attempt ruby omni-completion for methods. If negative value, this + completion will be never attempted. + + *g:acp_behaviorRubyOmniSymbolLength* > + let g:acp_behaviorRubyOmniSymbolLength = 1 +< + Length of keyword characters before the cursor, which are needed to + attempt ruby omni-completion for symbols. If negative value, this + completion will be never attempted. + + *g:acp_behaviorPythonOmniLength* > + let g:acp_behaviorPythonOmniLength = 0 +< + Length of keyword characters before the cursor, which are needed to + attempt python omni-completion. If negative value, this completion + will be never attempted. + + *g:acp_behaviorPerlOmniLength* > + let g:acp_behaviorPerlOmniLength = -1 +< + Length of keyword characters before the cursor, which are needed to + attempt perl omni-completion. If negative value, this completion will + be never attempted. + + See also: |acp-perl-omni| + + *g:acp_behaviorXmlOmniLength* > + let g:acp_behaviorXmlOmniLength = 0 +< + Length of keyword characters before the cursor, which are needed to + attempt XML omni-completion. If negative value, this completion will + be never attempted. + + *g:acp_behaviorHtmlOmniLength* > + let g:acp_behaviorHtmlOmniLength = 0 +< + Length of keyword characters before the cursor, which are needed to + attempt HTML omni-completion. If negative value, this completion will + be never attempted. + + *g:acp_behaviorCssOmniPropertyLength* > + let g:acp_behaviorCssOmniPropertyLength = 1 +< + Length of keyword characters before the cursor, which are needed to + attempt CSS omni-completion for properties. If negative value, this + completion will be never attempted. + + *g:acp_behaviorCssOmniValueLength* > + let g:acp_behaviorCssOmniValueLength = 0 +< + Length of keyword characters before the cursor, which are needed to + attempt CSS omni-completion for values. If negative value, this + completion will be never attempted. + + *g:acp_behavior* > + let g:acp_behavior = {} +< + This option is for advanced users. This setting overrides other + behavior options. This is a |Dictionary|. Each key corresponds to a + filetype. '*' is default. Each value is a list. These are attempted in + sequence until completion item is found. Each element is a + |Dictionary| which has following items: + + "command": *g:acp_behavior-command* + Command to be fed to open popup menu for completions. + + "completefunc": *g:acp_behavior-completefunc* + 'completefunc' will be set to this user-provided function during the + completion. Only makes sense when "command" is "". + + "meets": *g:acp_behavior-meets* + Name of the function which dicides whether or not to attempt this + completion. It will be attempted if this function returns non-zero. + This function takes a text before the cursor. + + "onPopupClose": *g:acp_behavior-onPopupClose* + Name of the function which is called when popup menu for this + completion is closed. Following completions will be suppressed if + this function returns zero. + + "repeat": *g:acp_behavior-repeat* + If non-zero, the last completion is automatically repeated. + + +============================================================================== +SPECIAL THANKS *acp-thanks* + +- Daniel Schierbeck +- Ingo Karkat + + +============================================================================== +CHANGELOG *acp-changelog* + +2.14.1 + - Changed the way of auto-popup for avoiding an issue about filename + completion. + - Fixed a bug that popup menu was opened twice when auto-popup was done. + +2.14 + - Added the support for perl-completion.vim. + +2.13 + - Changed to sort snipMate's triggers. + - Fixed a bug that a wasted character was inserted after snipMate's trigger + completion. + +2.12.1 + - Changed to avoid a strange behavior with Microsoft IME. + +2.12 + - Added g:acp_behaviorKeywordIgnores option. + - Added g:acp_behaviorUserDefinedMeets option and removed + g:acp_behaviorUserDefinedPattern. + - Changed to do auto-popup only when a buffer is modified. + - Changed the structure of g:acp_behavior option. + - Changed to reflect a change of behavior options (named g:acp_behavior*) + any time it is done. + - Fixed a bug that completions after omni completions or snipMate's trigger + completion were never attempted when no candidate for the former + completions was found. + +2.11.1 + - Fixed a bug that a snipMate's trigger could not be expanded when it was + completed. + +2.11 + - Implemented experimental feature which is snipMate's trigger completion. + +2.10 + - Improved the response by changing not to attempt any completion when + keyword characters are entered after a word which has been found that it + has no completion candidate at the last attempt of completions. + - Improved the response by changing to close popup menu when was + pressed and the text before the cursor would not match with the pattern of + current behavior. + +2.9 + - Changed default behavior to support XML omni completion. + - Changed default value of g:acp_behaviorKeywordCommand option. + The option with "\" cause a problem which inserts a match without + when 'dictionary' has been set and keyword completion is done. + - Changed to show error message when incompatible with a installed vim. + +2.8.1 + - Fixed a bug which inserted a selected match to the next line when + auto-wrapping (enabled with 'formatoptions') was performed. + +2.8 + - Added g:acp_behaviorUserDefinedFunction option and + g:acp_behaviorUserDefinedPattern option for users who want to make custom + completion auto-popup. + - Fixed a bug that setting 'spell' on a new buffer made typing go crazy. + +2.7 + - Changed naming conventions for filenames, functions, commands, and options + and thus renamed them. + - Added g:acp_behaviorKeywordCommand option. If you prefer the previous + behavior for keyword completion, set this option "\". + - Changed default value of g:acp_ignorecaseOption option. + + The following were done by Ingo Karkat: + + - ENH: Added support for setting a user-provided 'completefunc' during the + completion, configurable via g:acp_behavior. + - BUG: When the configured completion is or , the command to + restore the original text (in on_popup_post()) must be reverted, too. + - BUG: When using a custom completion function () that also uses + an s:...() function name, the s:GetSidPrefix() function dynamically + determines the wrong SID. Now calling s:DetermineSidPrefix() once during + sourcing and caching the value in s:SID. + - BUG: Should not use custom defined completion mappings. Now + consistently using unmapped completion commands everywhere. (Beforehand, + s:PopupFeeder.feed() used mappings via feedkeys(..., 'm'), but + s:PopupFeeder.on_popup_post() did not due to its invocation via + :map-expr.) + +2.6: + - Improved the behavior of omni completion for HTML/XHTML. + +2.5: + - Added some options to customize behavior easily: + g:AutoComplPop_BehaviorKeywordLength + g:AutoComplPop_BehaviorFileLength + g:AutoComplPop_BehaviorRubyOmniMethodLength + g:AutoComplPop_BehaviorRubyOmniSymbolLength + g:AutoComplPop_BehaviorPythonOmniLength + g:AutoComplPop_BehaviorHtmlOmniLength + g:AutoComplPop_BehaviorCssOmniPropertyLength + g:AutoComplPop_BehaviorCssOmniValueLength + +2.4: + - Added g:AutoComplPop_MappingDriven option. + +2.3.1: + - Changed to set 'lazyredraw' while a popup menu is visible to avoid + flickering. + - Changed a behavior for CSS. + - Added support for GetLatestVimScripts. + +2.3: + - Added a behavior for Python to support omni completion. + - Added a behavior for CSS to support omni completion. + +2.2: + - Changed not to work when 'paste' option is set. + - Fixed AutoComplPopEnable command and AutoComplPopDisable command to + map/unmap "i" and "R". + +2.1: + - Fixed the problem caused by "." command in Normal mode. + - Changed to map "i" and "R" to feed completion command after starting + Insert mode. + - Avoided the problem caused by Windows IME. + +2.0: + - Changed to use CursorMovedI event to feed a completion command instead of + key mapping. Now the auto-popup is triggered by moving the cursor. + - Changed to feed completion command after starting Insert mode. + - Removed g:AutoComplPop_MapList option. + +1.7: + - Added behaviors for HTML/XHTML. Now supports the omni completion for + HTML/XHTML. + - Changed not to show expressions for CTRL-R =. + - Changed not to set 'nolazyredraw' while a popup menu is visible. + +1.6.1: + - Changed not to trigger the filename completion by a text which has + multi-byte characters. + +1.6: + - Redesigned g:AutoComplPop_Behavior option. + - Changed default value of g:AutoComplPop_CompleteOption option. + - Changed default value of g:AutoComplPop_MapList option. + +1.5: + - Implemented continuous-completion for the filename completion. And added + new option to g:AutoComplPop_Behavior. + +1.4: + - Fixed the bug that the auto-popup was not suspended in fuzzyfinder. + - Fixed the bug that an error has occurred with Ruby-omni-completion unless + Ruby interface. + +1.3: + - Supported Ruby-omni-completion by default. + - Supported filename completion by default. + - Added g:AutoComplPop_Behavior option. + - Added g:AutoComplPop_CompleteoptPreview option. + - Removed g:AutoComplPop_MinLength option. + - Removed g:AutoComplPop_MaxLength option. + - Removed g:AutoComplPop_PopupCmd option. + +1.2: + - Fixed bugs related to 'completeopt'. + +1.1: + - Added g:AutoComplPop_IgnoreCaseOption option. + - Added g:AutoComplPop_NotEnableAtStartup option. + - Removed g:AutoComplPop_LoadAndEnable option. +1.0: + - g:AutoComplPop_LoadAndEnable option for a startup activation is added. + - AutoComplPopLock command and AutoComplPopUnlock command are added to + suspend and resume. + - 'completeopt' and 'complete' options are changed temporarily while + completing by this script. + +0.4: + - The first match are selected when the popup menu is Opened. You can insert + the first match with CTRL-Y. + +0.3: + - Fixed the problem that the original text is not restored if 'longest' is + not set in 'completeopt'. Now the plugin works whether or not 'longest' is + set in 'completeopt', and also 'menuone'. + +0.2: + - When completion matches are not found, insert CTRL-E to stop completion. + - Clear the echo area. + - Fixed the problem in case of dividing words by symbols, popup menu is + not opened. + +0.1: + - First release. + + +============================================================================== +ABOUT *acp-about* *acp-contact* *acp-author* + +Author: Takeshi NISHIDA +Licence: MIT Licence +URL: http://www.vim.org/scripts/script.php?script_id=1879 + http://bitbucket.org/ns9tks/vim-autocomplpop/ + +Bugs/Issues/Suggestions/Improvements ~ + +Please submit to http://bitbucket.org/ns9tks/vim-autocomplpop/issues/ . + +============================================================================== + vim:tw=78:ts=8:ft=help:norl: + diff --git a/doc/bufexplorer.txt b/doc/bufexplorer.txt new file mode 100644 index 0000000..06e9223 --- /dev/null +++ b/doc/bufexplorer.txt @@ -0,0 +1,513 @@ +*bufexplorer.txt* Buffer Explorer Last Change: 22 Oct 2010 + +Buffer Explorer *buffer-explorer* *bufexplorer* + Version 7.2.8 + +Plugin for easily exploring (or browsing) Vim |:buffers|. + +|bufexplorer-installation| Installation +|bufexplorer-usage| Usage +|bufexplorer-windowlayout| Window Layout +|bufexplorer-customization| Customization +|bufexplorer-changelog| Change Log +|bufexplorer-todo| Todo +|bufexplorer-credits| Credits + +For Vim version 7.0 and above. +This plugin is only available if 'compatible' is not set. + +{Vi does not have any of this} + +============================================================================== +INSTALLATION *bufexplorer-installation* + +To install: + - Download the bufexplorer.zip. + - Extract the zip archive into your runtime directory. + The archive contains plugin/bufexplorer.vim, and doc/bufexplorer.txt. + - Start Vim or goto an existing instance of Vim. + - Execute the following command: +> + :helptag /doc +< + This will generate all the help tags for any file located in the doc + directory. + +============================================================================== +USAGE *bufexplorer-usage* + +To start exploring in the current window, use: > + \be or :BufExplorer +To start exploring in a newly split horizontal window, use: > + \bs or :BufExplorerHorizontalSplit +To start exploring in a newly split vertical window, use: > + \bv or :BufExplorerVerticalSplit + +If you would like to use something other than '\', you may simply change the +leader (see |mapleader|). + +Note: If the current buffer is modified when bufexplorer started, the current + window is always split and the new bufexplorer is displayed in that new + window. + +Commands to use once exploring: + + Toggle help information. + Opens the buffer that is under the cursor into the current + window. + Opens the buffer that is under the cursor into the current + window. + Opens the buffer that is under the cursor in another tab. + d |:delete|the buffer under the cursor from the list. The + buffer's 'buflisted' is cleared. This allows for the buffer to + be displayed again using the 'show unlisted' command. + R Toggles relative path/absolute path. + T Toggles to show only buffers for this tab or not. + D |:wipeout|the buffer under the cursor from the list. When a + buffers is wiped, it will not be shown when unlisted buffer are + displayed. + f Toggles whether you are taken to the active window when + selecting a buffer or not. + o Opens the buffer that is under the cursor into the current + window. + p Toggles the showing of a split filename/pathname. + q Quit exploring. + r Reverses the order the buffers are listed in. + s Selects the order the buffers are listed in. Either by buffer + number, file name, file extension, most recently used (MRU), or + full path. + t Opens the buffer that is under the cursor in another tab. + u Toggles the showing of "unlisted" buffers. + +Once invoked, Buffer Explorer displays a sorted list (MRU is the default +sort method) of all the buffers that are currently opened. You are then +able to move the cursor to the line containing the buffer's name you are +wanting to act upon. Once you have selected the buffer you would like, +you can then either open it, close it(delete), resort the list, reverse +the sort, quit exploring and so on... + +=============================================================================== +WINDOW LAYOUT *bufexplorer-windowlayout* + +------------------------------------------------------------------------------- +" Press for Help +" Sorted by mru | Locate buffer | Absolute Split path +"= + 01 %a bufexplorer.txt C:\Vim\vimfiles\doc line 87 + 02 # bufexplorer.vim c:\Vim\vimfiles\plugin line 1 +------------------------------------------------------------------------------- + | | | | | + | | | | +-- Current Line #. + | | | +-- Relative/Full Path + | | +-- Buffer Name. + | +-- Buffer Attributes. See|:buffers|for more information. + +-- Buffer Number. See|:buffers|for more information. + +=============================================================================== +CUSTOMIZATION *bufexplorer-customization* + + *g:bufExplorerChgWin* +If set, bufexplorer will bring up the selected buffer in the window specified +by g:bufExplorerChgWin. + + *g:bufExplorerDefaultHelp* +To control whether the default help is displayed or not, use: > + let g:bufExplorerDefaultHelp=0 " Do not show default help. + let g:bufExplorerDefaultHelp=1 " Show default help. +The default is to show the default help. + + *g:bufExplorerDetailedHelp* +To control whether detailed help is display by, use: > + let g:bufExplorerDetailedHelp=0 " Do not show detailed help. + let g:bufExplorerDetailedHelp=1 " Show detailed help. +The default is NOT to show detailed help. + + *g:bufExplorerFindActive* +To control whether you are taken to the active window when selecting a buffer, +use: > + let g:bufExplorerFindActive=0 " Do not go to active window. + let g:bufExplorerFindActive=1 " Go to active window. +The default is to be taken to the active window. + + *g:bufExplorerFuncRef* +When a buffer is selected, the functions specified either singly or as a list +will be called. + + *g:bufExplorerReverseSort* +To control whether to sort the buffer in reverse order or not, use: > + let g:bufExplorerReverseSort=0 " Do not sort in reverse order. + let g:bufExplorerReverseSort=1 " Sort in reverse order. +The default is NOT to sort in reverse order. + + *g:bufExplorerShowDirectories* +Directories usually show up in the list from using a command like ":e .". +To control whether to show directories in the buffer list or not, use: > + let g:bufExplorerShowDirectories=1 " Show directories. + let g:bufExplorerShowDirectories=0 " Don't show directories. +The default is to show directories. + + *g:bufExplorerShowRelativePath* +To control whether to show absolute paths or relative to the current +directory, use: > + let g:bufExplorerShowRelativePath=0 " Show absolute paths. + let g:bufExplorerShowRelativePath=1 " Show relative paths. +The default is to show absolute paths. + + *g:bufExplorerShowTabBuffer* +To control weither or not to show buffers on for the specific tab or not, use: > + let g:bufExplorerShowTabBuffer=0 " No. + let g:bufExplorerShowTabBuffer=1 " Yes. +The default is not to show. + + *g:bufExplorerShowUnlisted* +To control whether to show unlisted buffer or not, use: > + let g:bufExplorerShowUnlisted=0 " Do not show unlisted buffers. + let g:bufExplorerShowUnlisted=1 " Show unlisted buffers. +The default is to NOT show unlisted buffers. + + *g:bufExplorerSortBy* +To control what field the buffers are sorted by, use: > + let g:bufExplorerSortBy='extension' " Sort by file extension. + let g:bufExplorerSortBy='fullpath' " Sort by full file path name. + let g:bufExplorerSortBy='mru' " Sort by most recently used. + let g:bufExplorerSortBy='name' " Sort by the buffer's name. + let g:bufExplorerSortBy='number' " Sort by the buffer's number. +The default is to sort by mru. + + *g:bufExplorerSplitBelow* +To control where the new split window will be placed above or below the +current window, use: > + let g:bufExplorerSplitBelow=1 " Split new window below current. + let g:bufExplorerSplitBelow=0 " Split new window above current. +The default is to use what ever is set by the global &splitbelow +variable. + + *g:bufExplorerSplitOutPathName* +To control whether to split out the path and file name or not, use: > + let g:bufExplorerSplitOutPathName=1 " Split the path and file name. + let g:bufExplorerSplitOutPathName=0 " Don't split the path and file + " name. +The default is to split the path and file name. + + *g:bufExplorerSplitRight* +To control where the new vsplit window will be placed to the left or right of +current window, use: > + let g:bufExplorerSplitRight=0 " Split left. + let g:bufExplorerSplitRight=1 " Split right. +The default is to use the global &splitright. + +=============================================================================== +CHANGE LOG *bufexplorer-changelog* + +7.2.8 - Enhancements: + * Thanks to Charles Campbell for integrating bufexplorer with GDBMGR. + http://mysite.verizon.net/astronaut/vim/index.html#GDBMGR +7.2.7 - Fix: + * My 1st attempt to fix the "cache" issue where buffers information + has changed but the cache/display does not reflect those changes. + More work still needs to be done. +7.2.6 - Fix: + * Thanks to Michael Henry for pointing out that I totally forgot to + update the inline help to reflect the previous change to the 'd' + and 'D' keys. Opps! +7.2.5 - Fix: + * Philip Morant suggested switching the command (bwipe) associated + with the 'd' key with the command (bdelete) associated with the 'D' + key. This made sense since the 'd' key is more likely to be used + compared to the 'D' key. +7.2.4 - Fix: + * I did not implement the patch provided by Godefroid Chapelle + correctly. I missed one line which happened to be the most + important one :) +7.2.3 - Enhancements: + * Thanks to David Fishburn for helping me out with a much needed + code overhaul as well as some awesome performance enhancements. + He also reworked the handling of tabs. + * Thanks to Vladimir Dobriakov for making the suggestions on + enhancing the documentation to include a better explaination of + what is contained in the main bufexplorer window. + * Thanks to Yuriy Ershov for added code that when the bufexplorer + window is opened, the cursor is now positioned at the line with the + active buffer (useful in non-MRU sort modes). + * Yuriy also added the abiltiy to cycle through the sort fields in + reverse order. + Fixes: + * Thanks to Michael Henry for supplying a patch that allows + bufexplorer to be opened even when there is one buffer or less. + * Thanks to Godefroid Chapelle for supplying a patch that fixed + MRU sort order after loading a session. +7.2.2 - Fixes: + * Thanks to David L. Dight for spotting and fixing an issue when + using ctrl^. bufexplorer would incorrectly handle the previous + buffer so that when ctrl^ was pressed the incorrect file was opened. +7.2.1 - Fixes: + * Thanks to Dimitar for spotting and fixing a feature that was + inadvertently left out of the previous version. The feature was + when bufexplorer was used together with WinManager, you could use + the tab key to open a buffer in a split window. +7.2.0 - Enhancements: + * For all those missing the \bs and \bv commands, these have now + returned. Thanks to Phil O'Connell for asking for the return of + these missing features and helping test out this version. + Fixes: + * Fixed problem with the bufExplorerFindActive code not working + correctly. + * Fixed an incompatibility between bufexplorer and netrw that caused + buffers to be incorrectly removed from the MRU list. +7.1.7 - Fixes: + * TaCahiroy fixed several issues related to opening a buffer in a + tab. +7.1.6 - Fixes: + * Removed ff=unix from modeline in bufexplorer.txt. Found by Bill + McCarthy. +7.1.5 - Fixes: + * Could not open unnamed buffers. Fixed by TaCahiroy. +7.1.4 - Fixes: + * Sometimes when a file's path has 'white space' in it, extra buffers + would be created containing each piece of the path. i.e: + opening c:\document and settings\test.txt would create a buffer + named "and" and a buffer named "Documents". This was reported and + fixed by TaCa Yoss. +7.1.3 - Fixes: + * Added code to allow only one instance of the plugin to run at a + time. Thanks Dennis Hostetler. +7.1.2 - Fixes: + * Fixed a jumplist issue spotted by JiangJun. I overlooked the + 'jumplist' and with a couple calls to 'keepjumps', everything is + fine again. + * Went back to just having a plugin file, no autoload file. By having + the autoload, WinManager was no longer working and without really + digging into the cause, it was easier to go back to using just a + plugin file. +7.1.1 - Fixes: + * A problem spotted by Thomas Arendsen Hein. + When running Vim (7.1.94), error E493 was being thrown. + Enhancements: + * Added 'D' for 'delete' buffer as the 'd' command was a 'wipe' + buffer. +7.1.0 - Another 'major' update, some by Dave Larson, some by me. + * Making use of 'autoload' now to make the plugin load quicker. + * Removed '\bs' and '\bv'. These are now controlled by the user. The + user can issue a ':sp' or ':vs' to create a horizontal or vertical + split window and then issue a '\be' + * Added handling of tabs. +7.0.17 - Fixed issue with 'drop' command. + Various enhancements and improvements. +7.0.16 - Fixed issue reported by Liu Jiaping on non Windows systems, which was + ... + Open file1, open file2, modify file1, open bufexplorer, you get the + following error: + + --------8<-------- + Error detected while processing function + 14_StartBufExplorer..14_SplitOpen: + line 4: + E37: No write since last change (add ! to override) + + But the worse thing is, when I want to save the current buffer and + type ':w', I get another error message: + E382: Cannot write, 'buftype' option is set + --------8<-------- + +7.0.15 - Thanks to Mark Smithfield for suggesting bufexplorer needed to handle + the ':args' command. +7.0.14 - Thanks to Randall Hansen for removing the requirement of terminal + versions to be recompiled with 'gui' support so the 'drop' command + would work. The 'drop' command is really not needed in terminal + versions. +7.0.13 - Fixed integration with WinManager. + Thanks to Dave Eggum for another update. + - Fix: The detailed help didn't display the mapping for toggling + the split type, even though the split type is displayed. + - Fixed incorrect description in the detailed help for toggling + relative or full paths. + - Deprecated s:ExtractBufferNbr(). Vim's str2nr() does the same + thing. + - Created a s:Set() function that sets a variable only if it hasn't + already been defined. It's useful for initializing all those + default settings. + - Removed checks for repetitive command definitions. They were + unnecessary. + - Made the help highlighting a little more fancy. + - Minor reverse compatibility issue: Changed ambiguous setting + names to be more descriptive of what they do (also makes the code + easier to follow): + Changed bufExplorerSortDirection to bufExplorerReverseSort + Changed bufExplorerSplitType to bufExplorerSplitVertical + Changed bufExplorerOpenMode to bufExplorerUseCurrentWindow + - When the BufExplorer window closes, all the file-local marks are + now deleted. This may have the benefit of cleaning up some of the + jumplist. + - Changed the name of the parameter for StartBufExplorer from + "split" to "open". The parameter is a string which specifies how + the buffer will be open, not if it is split or not. + - Deprecated DoAnyMoreBuffersExist() - it is a one line function + only used in one spot. + - Created four functions (SplitOpen(), RebuildBufferList(), + UpdateHelpStatus() and ReSortListing()) all with one purpose - to + reduce repeated code. + - Changed the name of AddHeader() to CreateHelp() to be more + descriptive of what it does. It now returns an array instead of + updating the window directly. This has the benefit of making the + code more efficient since the text the function returns is used a + little differently in the two places the function is called. + - Other minor simplifications. +7.0.12 - MAJOR Update. + This version will ONLY run with Vim version 7.0 or greater. + Dave Eggum has made some 'significant' updates to this latest + version: + - Added BufExplorerGetAltBuf() global function to be used in the + user’s rulerformat. + - Added g:bufExplorerSplitRight option. + - Added g:bufExplorerShowRelativePath option with mapping. + - Added current line highlighting. + - The split type can now be changed whether bufexplorer is opened + in split mode or not. + - Various major and minor bug fixes and speed improvements. + - Sort by extension. + Other improvements/changes: + - Changed the help key from '?' to to be more 'standard'. + - Fixed splitting of vertical bufexplorer window. + Hopefully I have not forgot something :) +7.0.11 - Fixed a couple of highlighting bugs, reported by David Eggum. He also + changed passive voice to active on a couple of warning messages. +7.0.10 - Fixed bug report by Xiangjiang Ma. If the 'ssl' option is set, + the slash character used when displaying the path was incorrect. +7.0.9 - Martin Grenfell found and eliminated an annoying bug in the + bufexplorer/winmanager integration. The bug was were an + annoying message would be displayed when a window was split or + a new file was opened in a new window. Thanks Martin! +7.0.8 - Thanks to Mike Li for catching a bug in the WinManager integration. + The bug was related to the incorrect displaying of the buffer + explorer's window title. +7.0.7 - Thanks to Jeremy Cowgar for adding a new enhancement. This + enhancement allows the user to press 'S', that is capital S, which + will open the buffer under the cursor in a newly created split + window. +7.0.6 - Thanks to Larry Zhang for finding a bug in the "split" buffer code. + If you force set g:bufExplorerSplitType='v' in your vimrc, and if you + tried to do a \bs to split the bufexplorer window, it would always + split horizontal, not vertical. He also found that I had a typeo in + that the variable g:bufExplorerSplitVertSize was all lower case in + the documentation which was incorrect. +7.0.5 - Thanks to Mun Johl for pointing out a bug that if a buffer was + modified, the '+' was not showing up correctly. +7.0.4 - Fixed a problem discovered first by Xiangjiang Ma. Well since I've + been using vim 7.0 and not 6.3, I started using a function (getftype) + that is not in 6.3. So for backward compatibility, I conditionaly use + this function now. Thus, the g:bufExplorerShowDirectories feature is + only available when using vim 7.0 and above. +7.0.3 - Thanks to Erwin Waterlander for finding a problem when the last + buffer was deleted. This issue got me to rewrite the buffer display + logic (which I've wanted to do for sometime now). + Also great thanks to Dave Eggum for coming up with idea for + g:bufExplorerShowDirectories. Read the above information about this + feature. +7.0.2 - Thanks to Thomas Arendsen Hein for finding a problem when a user + has the default help turned off and then brought up the explorer. An + E493 would be displayed. +7.0.1 - Thanks to Erwin Waterlander for finding a couple problems. + The first problem allowed a modified buffer to be deleted. Opps! The + second problem occurred when several files were opened, BufExplorer + was started, the current buffer was deleted using the 'd' option, and + then BufExplorer was exited. The deleted buffer was still visible + while it is not in the buffers list. Opps again! +7.0.0 - Thanks to Shankar R. for suggesting to add the ability to set + the fixed width (g:bufExplorerSplitVertSize) of a new window + when opening bufexplorer vertically and fixed height + (g:bufExplorerSplitHorzSize) of a new window when opening + bufexplorer horizontally. By default, the windows are normally + split to use half the existing width or height. +6.3.0 - Added keepjumps so that the jumps list would not get cluttered with + bufexplorer related stuff. +6.2.3 - Thanks to Jay Logan for finding a bug in the vertical split position + of the code. When selecting that the window was to be split + vertically by doing a '\bv', from then on, all splits, i.e. '\bs', + were split vertically, even though g:bufExplorerSplitType was not set + to 'v'. +6.2.2 - Thanks to Patrik Modesto for adding a small improvement. For some + reason his bufexplorer window was always showing up folded. He added + 'setlocal nofoldenable' and it was fixed. +6.2.1 - Thanks goes out to Takashi Matsuo for added the 'fullPath' sorting + logic and option. +6.2.0 - Thanks goes out to Simon Johann-Ganter for spotting and fixing a + problem in that the last search pattern is overridden by the search + pattern for blank lines. +6.1.6 - Thanks to Artem Chuprina for finding a pesky bug that has been around + for sometime now. The key mapping was causing the buffer + explored to close prematurely when vim was run in an xterm. The + key mapping is now removed. +6.1.5 - Thanks to Khorev Sergey. Added option to show default help or not. +6.1.4 - Thanks goes out to Valery Kondakoff for suggesting the addition of + setlocal nonumber and foldcolumn=0. This allows for line numbering + and folding to be turned off temporarily while in the explorer. +6.1.3 - Added folding. Did some code cleanup. Added the ability to force the + newly split window to be temporarily vertical, which was suggested by + Thomas Glanzmann. +6.1.2 - Now pressing the key will quit, just like 'q'. + Added folds to hide winmanager configuration. + If anyone had the 'C' option in their cpoptions they would receive + a E10 error on startup of BufExplorer. cpo is now saved, updated and + restored. Thanks to Charles E Campbell, Jr. + Attempted to make sure there can only be one BufExplorer window open + at a time. +6.1.1 - Thanks to Brian D. Goodwin for adding toupper to FileNameCmp. This + way buffers sorted by name will be in the correct order regardless of + case. +6.0.16 - Thanks to Andre Pang for the original patch/idea to get bufexplorer + to work in insertmode/modeless mode (evim). Added Initialize + and Cleanup autocommands to handle commands that need to be + performed when starting or leaving bufexplorer. +6.0.15 - Srinath Avadhanulax added a patch for winmanager.vim. +6.0.14 - Fix a few more bug that I thought I already had fixed. Thanks + to Eric Bloodworth for adding 'Open Mode/Edit in Place'. Added + vertical splitting. +6.0.13 - Thanks to Charles E Campbell, Jr. for pointing out some embarrassing + typos that I had in the documentation. I guess I need to run + the spell checker more :o) +6.0.12 - Thanks to Madoka Machitani, for the tip on adding the augroup command + around the MRUList autocommands. +6.0.11 - Fixed bug report by Xiangjiang Ma. '"=' was being added to the + search history which messed up hlsearch. +6.0.10 - Added the necessary hooks so that the Srinath Avadhanula's + winmanager.vim script could more easily integrate with this script. + Tried to improve performance. +6.0.9 - Added MRU (Most Recently Used) sort ordering. +6.0.8 - Was not resetting the showcmd command correctly. + Added nifty help file. +6.0.7 - Thanks to Brett Carlane for some great enhancements. Some are added, + some are not, yet. Added highlighting of current and alternate + filenames. Added splitting of path/filename toggle. Reworked + ShowBuffers(). + Changed my email address. +6.0.6 - Copyright notice added. Needed this so that it could be distributed + with Debian Linux. Fixed problem with the SortListing() function + failing when there was only one buffer to display. +6.0.5 - Fixed problems reported by David Pascoe, in that you where unable to + hit 'd' on a buffer that belonged to a files that no longer existed + and that the 'yank' buffer was being overridden by the help text when + the bufexplorer was opened. +6.0.4 - Thanks to Charles Campbell, Jr. for making this plugin more plugin + *compliant*, adding default keymappings of be and bs + as well as fixing the 'w:sortDirLabel not being defined' bug. +6.0.3 - Added sorting capabilities. Sort taken from explorer.vim. +6.0.2 - Can't remember. (2001-07-25) +6.0.1 - Initial release. + +=============================================================================== +TODO *bufexplorer-todo* + +- Nothing as of now, buf if you have any suggestions, drop me an email. + +=============================================================================== +CREDITS *bufexplorer-credits* + +Author: Jeff Lanzarotta + +Credit must go out to Bram Moolenaar and all the Vim developers for +making the world's best editor (IMHO). I also want to thank everyone who +helped and gave me suggestions. I wouldn't want to leave anyone out so I +won't list names. + +=============================================================================== +vim:tw=78:noet:wrap:ts=8:ft=help:norl: diff --git a/doc/conque_term.txt b/doc/conque_term.txt new file mode 100644 index 0000000..7689ef7 --- /dev/null +++ b/doc/conque_term.txt @@ -0,0 +1,645 @@ +*ConqueTerm* Plugin to run a shell inside a Vim buffer + +The ConqueTerm plugin will turn a Vim buffer into a terminal emulator, allowing +you to run and interact with a shell or shell application inside the buffer. + + 1. Installation |conque-term-setup| + 1.1 Requirements for Unix |conque-term-requirements| + 1.2 Requirements for Windows |conque-term-windows| + 1.3 Installation |conque-term-installation| + 2. Usage |conque-term-usage| + 2.1 General Usage |conque-term-gen-usage| + 2.2 Special keys |conque-term-special-keys| + 2.2.1 Send text to Conque |conque-term-send| + 2.2.2 Toggle terminal input mode |conque-term-input-mode| + 2.2.3 Sending the key press |conque-term-esc| + 3. Configuration |conque-term-options| + 3.1 General |conque-config-general| + 3.1.1 Python version |ConqueTerm_PyVersion| + 3.1.2 Fast mode |ConqueTerm_FastMode| + 3.1.3 Color support |ConqueTerm_Color| + 3.1.4 Session Support |ConqueTerm_SessionSupport| + 3.1.5 Keep updating terminal buffer |ConqueTerm_ReadUnfocused| + 3.1.6 Insert mode when entering buffer |ConqueTerm_InsertOnEnter| + 3.1.7 Close buffer when program exits |ConqueTerm_CloseOnEnd| + 3.1.8 Hide start messages |ConqueTerm_StartMessages| + 3.1.9 Regex for highlighting your prompt |ConqueTerm_PromptRegex| + 3.1.10 Syntax type |ConqueTerm_Syntax| + 3.2 Keyboard |conque-config-keyboard| + 3.2.1 The key |ConqueTerm_EscKey| + 3.2.2 Toggle terminal input mode |ConqueTerm_ToggleKey| + 3.2.3 Enable in insert mode |ConqueTerm_CWInsert| + 3.2.4 Execute current file in Conque |ConqueTerm_ExecFileKey| + 3.2.5 Send current file contents to Conque|ConqueTerm_SendFileKey| + 3.2.6 Send selected text to Conque |ConqueTerm_SendVisKey| + 3.2.7 Function Keys |ConqueTerm_SendFunctionKeys| + 3.3 Unix |conque-config-unix| + 3.3.1 Choose your terminal type |ConqueTerm_TERM| + 3.4 Windows |conque-config-windows| + 3.4.1 Python executable |ConqueTerm_PyExe| + 3.4.2 Windows character code page |ConqueTerm_CodePage| + 3.4.3 Terminal color method |ConqueTerm_ColorMode| + 4. VimScript API |conque-term-api| + 4.1 conque_term#open() |conque-term-open| + 4.2 conque_term#subprocess() |conque-term-subprocess| + 4.3 conque_term#get_instance() |conque-term-get-instance| + 4.4 CONQUE_OBJECT.write() |conque-term-write| + 4.5 CONQUE_OBJECT.writeln() |conque-term-writeln| + 4.6 CONQUE_OBJECT.read() |conque-term-read| + 4.7 CONQUE_OBJECT.set_callback() |conque-term-set-callback| + 4.8 CONQUE_OBJECT.close() |conque-term-close| + 4.9 Registering functions |conque-term-events| + 5. Misc |conque-term-misc| + 5.1 Known bugs |conque-term-bugs| + 5.2 Contribute |conque-term-contribute| + 5.3 Feedback |conque-term-feedback| + +============================================================================== + +1. Installation *conque-term-setup* + +Conque is designed for both Unix and Windows operating systems, however the +requirements are slightly different. Please check section below corresponding +to your installed OS. + +1.1 Requirements for Unix *conque-term-requirements* + + * [G]Vim 7.0+ with +python and/or +python3 + * Python 2.3+ and/or 3.x + * Unix-like OS: Linux, OS X, Solaris, Cygwin, etc + +The most common stumbling block is getting a version of Vim which has the +python interface enabled. Most all software package managers will have a copy +of Vim with Python support, so that is often the easiest way to get it. If +you're compiling Vim from source, be sure to use the --enable-pythoninterp +option, or --enable-python3interp for Python 3. On OS X the best option is +MacVim, which installs with Python support by default. + +1.2 Requirements for Windows *conque-term-windows* + + * [G]Vim 7.3 with +python and/or +python3 + * Python 2.7 and/or 3.1 + * Modern Windows OS (XP or later) + +Conque only officially supports the latest GVim 7.3 Windows installer +available at www.vim.org. If you are currently using Vim 7.2 or earlier you +will need to upgrade to 7.3 for Windows support. The Windows installer already +has the +python/+python3 interface built in. + +The official 7.3 release of Vim for Windows only works with Python versions +2.7 and/or 3.1. You can download and install Python from their website +http://www.python.org/download + +If you are compiling Vim + Python from source on Windows, the requirements +become only Vim 7.3+ and Python 2.7+. + + +1.3 Installation *conque-term-installation* + +Download the latest vimball from http://conque.googlecode.com + +Open the .vba file with Vim and run the following commands: +> + :so % + :q +< +That's it! The :ConqueTerm command will be available the next time you start +Vim. You can delete the .vba file when you've verified Conque was successfully +installed. + +============================================================================== + +2. Usage *conque-term-usage* + +2.1 General Usage *conque-term-gen-usage* + +Type :ConqueTerm to launch an application in the current buffer. Eg: +> + :ConqueTerm bash + :ConqueTerm mysql -h localhost -u joe_lunchbox Menu + :ConqueTerm Powershell.exe +< +Use :ConqueTermSplit or :ConqueTermVSplit to open Conque in a new horizontal +or vertical buffer. Use :ConqueTermTab to open Conque in a new tab. + +In insert mode you can interact with the shell as you would expect in a +normal terminal. All key presses will be sent to the terminal, including +control characters. See |conque-term-special-keys| for more information, +particularly regarding the key. + +In normal mode you can use Vim commands to browse your terminal output and +scroll back through the history. Most all Vim functionality will work, such +as searching, yanking or highlighting text. + + +2.2 Special keys *conque-term-special-keys* + +There are several keys which can be configured to have special behavior with +Conque. + +2.2.1 Send text to Conque *conque-term-send* + +Conque gives you three different commands to send text from a different +buffer, probably a source code file, to the Conque terminal buffer. All three +are configurable to use your choice of key combinations. + +To send a visually selected range of text to an existing terminal buffer, +press the key. + +To send the entire contents of the file you are editing to an existing +terminal buffer, press the key. + +Finally, to execute the current file in a new terminal buffer press the +key. This will split the screen with a new Conque buffer. The file you are +editing must be executable for this command to work. + +See |conque-term-options| for information about configuring these commands. + +2.2.2 Toggle terminal input mode *conque-term-input-mode* + +If you want to use insert mode to edit the terminal screen, press . You +will now be able to edit the terminal output freely without your cursor +jumping the the active prompt line. This may be useful if you want to reformat +terminal output for readability. + +While the terminal is paused new output will not be displayed on the screen +until you press again to resume. + +You can configure Conque to use a different key with the |ConqueTerm_ToggleKey| +option. + +2.2.3 Sending the key press *conque-term-esc* + +By default if you press the key in a Conque buffer you will leave insert +mode. But what if you want the character to be sent to your terminal? +There are two options. By default, pressing twice will send one +character to the terminal and you will remain in insert mode, while pressing +it once will leave insert mode. + +Alternatively you can use the |ConqueTerm_EscKey| option to choose a +different key for leaving insert mode. If a custom key is set, then all +key presses will be sent to the terminal. + +2.3 Registering functions *conque-term-register* + +Conque allows you to write your own VimScript functions which will be called +at certain events. See the API section |conque-term-events| for more. + +============================================================================== + +3. Options *conque-term-options* + +You can set the following options in your .vimrc (default values shown) + +3.1 General *conque-config-general* + +3.1.1 Python version *ConqueTerm_PyVersion* + +Conque will work with either Python 2.x or 3.x, assuming the interfaces have +been installed. By default it will try to use Python 2 first, then will try +Python 3. If you want Conque to use Python 3, set this variable to 3. + +Note: even if you set this to 3, if you don't have the python3 interface +Conque will fall back to using Python 2. +> + let g:ConqueTerm_PyVersion = 2 +< +3.1.2 Fast Mode *ConqueTerm_FastMode* + +Disable features which could make Conque run slowly. This includes most +terminal colors and some unicode support. Set this to 1 to enable fast mode. +> + let g:ConqueTerm_FastMode = 0 +< +3.1.3 Color support *ConqueTerm_Color* + +Terminal colors have the potential to slow down terminal screen rendering, +depending on how many colors are used and how fast the computer is. This +option allows you to choose how much color support will be enabled. + +If set to 0, terminal colors will be disabled. This will allow the terminal to +render most quickly. Syntax highlighting will still work. For example +highlighting quoted strings or MySQL output. + +If set to 1, terminal colors will be enabled, but only for the most recent 200 +lines of terminal output. Older output will be periodically stripped of color +highlighting to keep the display responsive. + +If set to 2, terminal colors will always be enabled. If your programs don't +use color output very frequently this is a good choice. + +Note: Color support is automatically disabled in "fast mode". +> + let g:ConqueTerm_Color = 1 +< +3.1.4 Session Support *ConqueTerm_SessionSupport* + +Vim's :mksession command allows you to save your current buffer configuration +to a file, which can be loaded at a later time after you've closed Vim. + +By default, Conque buffers are not restored. This is mostly for safety +reasons; you may not want Vim to automatically re-run a destructive command. + +However, if you're not working with missile launch code, and want Vim to +restart your Conque buffers when you load a session file, set this variable +to 1. Note your original subprocess and shell output will not be restored, but +the same command will be started in your buffer. +> + let g:ConqueTerm_SessionSupport = 0 +< +3.1.5 Keep updating terminal buffer *ConqueTerm_ReadUnfocused* + +If set to 1 then your Conque buffers will continue to update after you've +switched to another buffer. + +Note: Conque buffers may continue to update, but they will not scroll down as +new lines are added beyond the bottom of the visible buffer area. This is a +limitation of the Vim scripting language for which I haven't found a +workaround. +> + let g:ConqueTerm_ReadUnfocused = 1 +< +3.1.6 Insert mode when entering buffer *ConqueTerm_InsertOnEnter* + +If set to 1 then you will automatically go into insert mode when you enter the +buffer. This diverges from normal Vim behavior. If 0 you will still be in +normal mode. +> + let g:ConqueTerm_InsertOnEnter = 0 +< +3.1.7 Close buffer when program exits *ConqueTerm_CloseOnEnd* + +If you want your terminal buffer to be closed and permanently deleted when the +program running inside of it exits, set this option to 1. Otherwise the buffer +will become a simple text buffer after the program exits, and you can edit the +program output in insert mode. +> + let g:ConqueTerm_CloseOnEnd = 0 +< +3.1.8 Hide start messages *ConqueTerm_StartMessages* + +Unused. Startup messages are always hidden. +> + let g:ConqueTerm_StartMessages = 0 +< +3.1.9 Regex for highlighting your prompt *ConqueTerm_PromptRegex* + +Use this regular expression for sytax highlighting your terminal prompt. Your +terminal will generally run faster if you use Vim highlighting instead of +terminal colors for your prompt. You can also use it to do more advanced +syntax highlighting for the prompt line. +> + let g:ConqueTerm_PromptRegex = '^\w\+@[0-9A-Za-z_.-]\+:[0-9A-Za-z_./\~,:-]\+\$' +< +3.1.10 Choose Vim syntax type *ConqueTerm_Syntax* + +Set the buffer syntax. The default 'conque' has highlighting for MySQL, but +not much else. +> + let g:ConqueTerm_Syntax = 'conque' +< +3.2 Keyboard *conque-config-keyboard* + +3.2.1 The key *ConqueTerm_EscKey* + +If a custom key is set, then all key presses will be sent to the +terminal and you must use this custom key to leave insert mode. If left to the +default value of '' then you must press it twice to send the escape +character to the terminal, while pressing it once will leave insert mode. + +Note: You cannot use a key which is internally coded with the escape +character. This includes the keys and often the and keys. +Picking a control key, such as will be your best bet. +> + let g:ConqueTerm_EscKey = '' +< +3.2.2 Toggle terminal input mode *ConqueTerm_ToggleKey* + +Press this key to pause terminal input and output display. You will then be +able to edit the terminal screen as if it were a normal text buffer. Press +this key again to resume terminal mode. +> + let g:ConqueTerm_ToggleKey = '' +< +3.2.3 Enable in insert mode *ConqueTerm_CWInsert* + +If set to 1 then you can leave the Conque buffer using the commands +while you're still in insert mode. If set to 0 then the character will +be sent to the terminal. If both this option and ConqueTerm_InsertOnEnter are +set you can go in and out of the terminal buffer while never leaving insert +mode. +> + let g:ConqueTerm_CWInsert = 0 +< +3.2.4 Execute current file in Conque *ConqueTerm_ExecFileKey* + +Press this key to execute the file you're currently editing in a Conque +buffer. Is equivelent to running the command :ConqueTermSplit YOUR_FILE. Your +file must be executable for this command to work correctly. +> + let g:ConqueTerm_ExecFileKey = '' +< +3.2.5 Send current file contents to Conque *ConqueTerm_SendFileKey* + +Press this key to send your entire file contents to the most recently opened +Conque buffer as keyboard input. +> + let g:ConqueTerm_SendFileKey = '' +< +3.2.6 Send selected text to Conque *ConqueTerm_SendVisKey* + +Use this key to send the currently selected text to the most recently created +Conque buffer. +> + let g:ConqueTerm_SendVisKey = '' +< +3.2.7 Function Keys *ConqueTerm_SendFunctionKeys* + +By default, function keys (the F1-F12 row at the top of your keyboard) are not +passed to the terminal. Set this option to 1 to send these key events. + +Note: Unless you configured |ConqueTerm_SendVisKey| and |ConqueTerm_ToggleKey| +to use different keys, and will not be sent to the terminal even if +you set this option to 1. +> + let g:ConqueTerm_SendFunctionKeys = 0 +< +3.3 Unix *conque-config-unix* + +3.3.1 Choose your terminal type, Unix ONLY *ConqueTerm_TERM* + +Use this option to tell Conque what type of terminal it should identify itself +as. Conque officially uses the more limited VT100 terminal type for +developement and testing, although it supports some more advanced features +such as colors and title strings. + +You can change this setting to a more advanced type, namely 'xterm', but your +results may vary depending on which programs you're running. +> + let g:ConqueTerm_TERM = 'vt100' +< +3.4 Windows *conque-config-windows* + +3.4.1 Python executable, Windows ONLY *ConqueTerm_PyExe* + +The Windows version of Conque needs to know the path to the python.exe +executable for the version of Python Conque is using. If you installed Python +in the default location, or added the Python directory to your system path, +Conque should be able to find python.exe without you changing this variable. + +For example, you might set this to 'C:\Program Files\Python27\python.exe' +> + let g:ConqueTerm_PyExe = '' +< +3.4.2 Windows character code page *ConqueTerm_CodePage* + +Set the "code page" Windows will use for your console. Leave this value set to +zero to use the environment code page. + +Note: Displaying unicode characters on Conque for Windows needs work. +> + let g:ConqueTerm_CodePage = 0 +< +3.4.3 Terminal color method, Windows ONLY *ConqueTerm_ColorMode* + +Vim syntax highlighting by coordinate (e.g. the 3-7th characters on the 42nd +line) can be very slow. If you set this variable to 'conceal', you can use +the new conceal feature to render terminal colors. Requires Vim 7.3 and only +works on the Windows version of Conque. This will make colors render faster, +however it will also add hidden characters to the screen, which may be +annoying if you're copying and pasting terminal output out of the Conque +buffer. Set this to an empty string '' to disable concealed highlighting. +> + let g:ConqueTerm_ColorMode = 'conceal' +< +============================================================================== + +4. VimScript API (Beta) *conque-term-api* + +The Conque scripting API allows you to create and interact with Conque +terminals with the VimScript language. This API is still in beta stage. + +4.1 conque_term#open({command}, [buf_opts], [remain]) *conque-term-open* + +The open() function will create a new terminal buffer and start your command. + +The {command} must be an executable, either an absolute path or relative to +your system path. + +You can pass in a list of vim commands [buf_opts] which will be executed after +the new buffer is created but before the command is started. These are +typically commands to alter the size, position or configuration of the buffer +window. + +Note: If you don't pass in a command such as 'split', the terminal will open +in the current buffer. + +If you don't want the new terminal buffer to become the new active buffer, set + [remain] to 1. Only works if you create a split screen using [options]. + +Returns a Conque terminal object. + +Examples: +> + let my_terminal = conque_term#open('/bin/bash') + let my_terminal = conque_term#open('ipython', ['split', 'resize 20'], 1) +< +4.2 conque_term#subprocess({command}) *conque-term-subprocess* + +Starts a new subprocess with your {command}, but no terminal buffer is ever +created. This may be useful if you need asynchronous interaction with a +subprocess, but want to handle the output on your own. + +Returns a Conque terminal object. + +Example: +> + let my_subprocess = conque_term#subprocess('tail -f /var/log/foo.log') +< +4.3 conque_term#get_instance( [terminal_number] ) *conque-term-get-instance* + +Use the get_instance() function to retrieve an existing terminal object. The +terminal could have been created either with the user command :ConqueTerm or +with an API call to conque_term#open() or subprocess(). + +Use the optional [terminal_number] to retrieve a specific terminal instance. +Otherwise if the current buffer is a Conque terminal, it will be returned, +else the most recently created terminal. The terminal number is what you see +at the end of a terminal buffer name, e.g. "bash - 2". + +Returns a Conque terminal object. + +Example: +> + nnoremap :call conque_term#get_instance().writeln('clear') +< +4.4 CONQUE_OBJECT.write({text}) *conque-term-write* + +Once you have a terminal object from open(), subprocess() or get_instance() +you can send text input to it with the write() method. + +No return value. + +Examples: +> + call my_terminal.write("whoami\n") + call my_terminal.write("\") +< +4.5 CONQUE_OBJECT.writeln({text}) *conque-term-writeln* + +The same as write() except adds a \n character to the end if your input. + +Examples: +> + call my_subprocess.writeln('make') +< +4.6 CONQUE_OBJECT.read( [timeout], [update_buffer] ) *conque-term-read* + +Read new output from a Conque terminal subprocess. New output will be returned +as a string, and the terminal buffer will also be updated by default. + +If you are reading immediately after calling the write() method, you may want +to wait [timeout] milliseconds for output to be ready. + +If you want to prevent the output from being displayed in the terminal buffer, +set [update_buffer] to 0. This option has no effect if the terminal was +created with the subprocess() function, since there never is a buffer to +update. + +Returns output string. + +Note: The terminal buffer will not automatically scroll down if the new output +extends beyond the bottom of the visible buffer. Vim doesn't allow "unfocused" +buffers to be scrolled at the current version, although hopefully this will +change. + +Examples: +> + call my_terminal.writeln('whoami') + let output = my_terminal.read(500) + call my_terminal.writeln('ls -lha') + let output = my_terminal.read(1000, 1) +< +4.7 CONQUE_OBJECT.set_callback( {funcname} ) *conque-term-set-callback* + +Register a callback function for this subprocess instance. This function will +automatically be called whenever new output is available. Only practical with +subprocess() objects. + +Conque checkes for new subprocess output once a second when Vim is idle. If +new output is found your function will be called. + +Pass in the callback function name {funcname} as a string. + +No return value. + +Note: this method requires the g:ConqueTerm_ReadUnfocused option to be set. + +Note: this method is experimental, results may vary. + +Example: +> + let sp = conque_term#subprocess('tail -f /home/joe/log/error_log') + + function! MyErrorAlert(output) + echo a:output + endfunction + + call sp.set_callback('MyErrorAlert') +< +4.8 CONQUE_OBJECT.close() *conque-term-close* + +Kill your terminal subprocess. Sends the ABORT signal. You probably want to +close your subprocess in a more graceful manner with the write() method, but +this can be used when needed. Does not close the terminal buffer, if it +exists. + +This method will be called on all existing Conque subprocesses when Vim exits. + +Example: +> + let term = conque_term#open('ping google.com', ['belowright split']) + call term.read(5000) + call term.close() +< +4.9 Registering functions *conque-term-events* + +Conque provides the option to register callback functions which will be +executed at several different events. The currently available events are: + + after_startup After your application has loaded into the buffer. + buffer_enter When you switch to a Conque buffer. + buffer_leave When you leave a Conque buffer. + +You may use the function conque_term#register_function(event, function_name) +to add additional hooks at a particular event. The second argument should be +the name of a callback function which has one parameter, the current +terminal object (see|conque-term-api|for more about terminal objects). + +For example: +> + function MyConqueStartup(term) + + " set buffer syntax using the name of the program currently running + let syntax_associations = { 'ipython': 'python', 'irb': 'ruby' } + + if has_key(syntax_associations, a:term.program_name) + execute 'setlocal syntax=' . syntax_associations[a:term.program_name] + else + execute 'setlocal syntax=' . a:term.program_name + endif + + " shrink window height to 10 rows + resize 10 + + " silly example of terminal api usage + if a:term.program_name == 'bash' + call a:term.writeln('svn up ~/projects/*') + endif + + endfunction + + call conque_term#register_function('after_startup', 'MyConqueStartup') +< + +============================================================================== + +5. Misc *conque-term-misc* + + +5.1 Known bugs *conque-term-bugs* + +The following are known limitations: + + - Font/color highlighting is imperfect and slow. If you don't care about + color in your shell, set g:ConqueTerm_Color = 0 in your .vimrc + - Conque only supports the extended ASCII character set for input, not utf-8. + - VT100 escape sequence support is not complete. + - Alt/Meta key support in Vim isn't great in general, and conque is no + exception. Pressing x or instead of works in + most cases. + + +5.2 Contribute *conque-term-contribute* + +The two contributions most in need are improvements to Vim itself. I currently +use hacks to capture key press input from the user, and to poll the terminal +for more output. The Vim todo.txt document lists proposed improvements to give +users this behavior without hacks. Having a key press event should allow +Conque to work with multi- byte input. If you are a Vim developer, please +consider prioritizing these two items: + + - todo.txt (Autocommands, line ~3137) + 8 Add an event like CursorHold that is triggered repeatedly, not just + once after typing something. + + +5.3 Feedback *conque-term-feedback* + +Bugs, suggestions and patches are all welcome. + +For more information visit http://conque.googlecode.com + +Check out the latest from svn at http://conque.googlecode.com/svn/trunk/ + + vim:tw=78:ts=8:ft=help:norl: diff --git a/doc/snipMate.txt b/doc/snipMate.txt new file mode 100644 index 0000000..704d44a --- /dev/null +++ b/doc/snipMate.txt @@ -0,0 +1,286 @@ +*snipMate.txt* Plugin for using TextMate-style snippets in Vim. + +snipMate *snippet* *snippets* *snipMate* +Last Change: July 13, 2009 + +|snipMate-description| Description +|snipMate-syntax| Snippet syntax +|snipMate-usage| Usage +|snipMate-settings| Settings +|snipMate-features| Features +|snipMate-disadvantages| Disadvantages to TextMate +|snipMate-contact| Contact + +For Vim version 7.0 or later. +This plugin only works if 'compatible' is not set. +{Vi does not have any of these features.} + +============================================================================== +DESCRIPTION *snipMate-description* + +snipMate.vim implements some of TextMate's snippets features in Vim. A +snippet is a piece of often-typed text that you can insert into your +document using a trigger word followed by a . + +For instance, in a C file using the default installation of snipMate.vim, if +you type "for" in insert mode, it will expand a typical for loop in C: > + + for (i = 0; i < count; i++) { + + } + + +To go to the next item in the loop, simply over to it; if there is +repeated code, such as the "i" variable in this example, you can simply +start typing once it's highlighted and all the matches specified in the +snippet will be updated. To go in reverse, use . + +============================================================================== +SYNTAX *snippet-syntax* + +Snippets can be defined in two ways. They can be in their own file, named +after their trigger in 'snippets//.snippet', or they can be +defined together in a 'snippets/.snippets' file. Note that dotted +'filetype' syntax is supported -- e.g., you can use > + + :set ft=html.eruby + +to activate snippets for both HTML and eRuby for the current file. + +The syntax for snippets in *.snippets files is the following: > + + snippet trigger + expanded text + more expanded text + +Note that the first hard tab after the snippet trigger is required, and not +expanded in the actual snippet. The syntax for *.snippet files is the same, +only without the trigger declaration and starting indentation. + +Also note that snippets must be defined using hard tabs. They can be expanded +to spaces later if desired (see |snipMate-indenting|). + +"#" is used as a line-comment character in *.snippets files; however, they can +only be used outside of a snippet declaration. E.g.: > + + # this is a correct comment + snippet trigger + expanded text + snippet another_trigger + # this isn't a comment! + expanded text +< +This should hopefully be obvious with the included syntax highlighting. + + *snipMate-${#}* +Tab stops ~ + +By default, the cursor is placed at the end of a snippet. To specify where the +cursor is to be placed next, use "${#}", where the # is the number of the tab +stop. E.g., to place the cursor first on the id of a
tag, and then allow +the user to press to go to the middle of it: + > + snippet div +
+ ${2} +
+< + *snipMate-placeholders* *snipMate-${#:}* *snipMate-$#* +Placeholders ~ + +Placeholder text can be supplied using "${#:text}", where # is the number of +the tab stop. This text then can be copied throughout the snippet using "$#", +given # is the same number as used before. So, to make a C for loop: > + + snippet for + for (${2:i}; $2 < ${1:count}; $1++) { + ${4} + } + +This will cause "count" to first be selected and change if the user starts +typing. When is pressed, the "i" in ${2}'s position will be selected; +all $2 variables will default to "i" and automatically be updated if the user +starts typing. +NOTE: "$#" syntax is used only for variables, not for tab stops as in TextMate. + +Variables within variables are also possible. For instance: > + + snippet opt + + +Will, as usual, cause "option" to first be selected and update all the $1 +variables if the user starts typing. Since one of these variables is inside of +${2}, this text will then be used as a placeholder for the next tab stop, +allowing the user to change it if he wishes. + +To copy a value throughout a snippet without supplying default text, simply +use the "${#:}" construct without the text; e.g.: > + + snippet foo + ${1:}bar$1 +< *snipMate-commands* +Interpolated Vim Script ~ + +Snippets can also contain Vim script commands that are executed (via |eval()|) +when the snippet is inserted. Commands are given inside backticks (`...`); for +TextMates's functionality, use the |system()| function. E.g.: > + + snippet date + `system("date +%Y-%m-%d")` + +will insert the current date, assuming you are on a Unix system. Note that you +can also (and should) use |strftime()| for this example. + +Filename([{expr}] [, {defaultText}]) *snipMate-filename* *Filename()* + +Since the current filename is used often in snippets, a default function +has been defined for it in snipMate.vim, appropriately called Filename(). + +With no arguments, the default filename without an extension is returned; +the first argument specifies what to place before or after the filename, +and the second argument supplies the default text to be used if the file +has not been named. "$1" in the first argument is replaced with the filename; +if you only want the filename to be returned, the first argument can be left +blank. Examples: > + + snippet filename + `Filename()` + snippet filename_with_default + `Filename('', 'name')` + snippet filename_foo + `filename('$1_foo')` + +The first example returns the filename if it the file has been named, and an +empty string if it hasn't. The second returns the filename if it's been named, +and "name" if it hasn't. The third returns the filename followed by "_foo" if +it has been named, and an empty string if it hasn't. + + *multi_snip* +To specify that a snippet can have multiple matches in a *.snippets file, use +this syntax: > + + snippet trigger A description of snippet #1 + expand this text + snippet trigger A description of snippet #2 + expand THIS text! + +In this example, when "trigger" is typed, a numbered menu containing all +of the descriptions of the "trigger" will be shown; when the user presses the +corresponding number, that snippet will then be expanded. + +To create a snippet with multiple matches using *.snippet files, +simply place all the snippets in a subdirectory with the trigger name: +'snippets///.snippet'. + +============================================================================== +USAGE *snipMate-usage* + + *'snippets'* *g:snippets_dir* +Snippets are by default looked for any 'snippets' directory in your +'runtimepath'. Typically, it is located at '~/.vim/snippets/' on *nix or +'$HOME\vimfiles\snippets\' on Windows. To change that location or add another +one, change the g:snippets_dir variable in your |.vimrc| to your preferred +directory, or use the |ExtractSnips()|function. This will be used by the +|globpath()| function, and so accepts the same syntax as it (e.g., +comma-separated paths). + +ExtractSnipsFile({directory}, {filetype}) *ExtractSnipsFile()* *.snippets* + +ExtractSnipsFile() extracts the specified *.snippets file for the given +filetype. A .snippets file contains multiple snippet declarations for the +filetype. It is further explained above, in |snippet-syntax|. + +ExtractSnips({directory}, {filetype}) *ExtractSnips()* *.snippet* + +ExtractSnips() extracts *.snippet files from the specified directory and +defines them as snippets for the given filetype. The directory tree should +look like this: 'snippets//.snippet'. If the snippet has +multiple matches, it should look like this: +'snippets///.snippet' (see |multi_snip|). + + *ResetSnippets()* +The ResetSnippets() function removes all snippets from memory. This is useful +to put at the top of a snippet setup file for if you would like to |:source| +it multiple times. + + *list-snippets* *i_CTRL-R_* +If you would like to see what snippets are available, simply type +in the current buffer to show a list via |popupmenu-completion|. + +============================================================================== +SETTINGS *snipMate-settings* *g:snips_author* + +The g:snips_author string (similar to $TM_FULLNAME in TextMate) should be set +to your name; it can then be used in snippets to automatically add it. E.g.: > + + let g:snips_author = 'Hubert Farnsworth' + snippet name + `g:snips_author` +< + *snipMate-expandtab* *snipMate-indenting* +If you would like your snippets to be expanded using spaces instead of tabs, +just enable 'expandtab' and set 'softtabstop' to your preferred amount of +spaces. If 'softtabstop' is not set, 'shiftwidth' is used instead. + + *snipMate-remap* +snipMate does not come with a setting to customize the trigger key, but you +can remap it easily in the two lines it's defined in the 'after' directory +under 'plugin/snipMate.vim'. For instance, to change the trigger key +to CTRL-J, just change this: > + + ino =TriggerSnippet() + snor i=TriggerSnippet() + +to this: > + ino =TriggerSnippet() + snor i=TriggerSnippet() + +============================================================================== +FEATURES *snipMate-features* + +snipMate.vim has the following features among others: + - The syntax of snippets is very similar to TextMate's, allowing + easy conversion. + - The position of the snippet is kept transparently (i.e. it does not use + markers/placeholders written to the buffer), which allows you to escape + out of an incomplete snippet, something particularly useful in Vim. + - Variables in snippets are updated as-you-type. + - Snippets can have multiple matches. + - Snippets can be out of order. For instance, in a do...while loop, the + condition can be added before the code. + - [New] File-based snippets are supported. + - [New] Triggers after non-word delimiters are expanded, e.g. "foo" + in "bar.foo". + - [New] can now be used to jump tab stops in reverse order. + +============================================================================== +DISADVANTAGES *snipMate-disadvantages* + +snipMate.vim currently has the following disadvantages to TextMate's snippets: + - There is no $0; the order of tab stops must be explicitly stated. + - Placeholders within placeholders are not possible. E.g.: > + + '${3}
' +< + In TextMate this would first highlight ' id="some_id"', and if + you hit delete it would automatically skip ${2} and go to ${3} + on the next , but if you didn't delete it it would highlight + "some_id" first. You cannot do this in snipMate.vim. + - Regex cannot be performed on variables, such as "${1/.*/\U&}" + - Placeholders cannot span multiple lines. + - Activating snippets in different scopes of the same file is + not possible. + +Perhaps some of these features will be added in a later release. + +============================================================================== +CONTACT *snipMate-contact* *snipMate-author* + +To contact the author (Michael Sanders), please email: + msanders42+snipmate gmail com + +I greatly appreciate any suggestions or improvements offered for the script. + +============================================================================== + +vim:tw=78:ts=8:ft=help:norl: diff --git a/doc/tags b/doc/tags new file mode 100644 index 0000000..4f1da67 --- /dev/null +++ b/doc/tags @@ -0,0 +1,287 @@ +'NERDAllowAnyVisualDelims' NERD_commenter.txt /*'NERDAllowAnyVisualDelims'* +'NERDBlockComIgnoreEmpty' NERD_commenter.txt /*'NERDBlockComIgnoreEmpty'* +'NERDChristmasTree' NERD_tree.txt /*'NERDChristmasTree'* +'NERDCommentWholeLinesInVMode' NERD_commenter.txt /*'NERDCommentWholeLinesInVMode'* +'NERDCompactSexyComs' NERD_commenter.txt /*'NERDCompactSexyComs'* +'NERDCreateDefaultMappings' NERD_commenter.txt /*'NERDCreateDefaultMappings'* +'NERDDefaultNesting' NERD_commenter.txt /*'NERDDefaultNesting'* +'NERDLPlace' NERD_commenter.txt /*'NERDLPlace'* +'NERDMenuMode' NERD_commenter.txt /*'NERDMenuMode'* +'NERDRPlace' NERD_commenter.txt /*'NERDRPlace'* +'NERDRemoveAltComs' NERD_commenter.txt /*'NERDRemoveAltComs'* +'NERDRemoveExtraSpaces' NERD_commenter.txt /*'NERDRemoveExtraSpaces'* +'NERDSpaceDelims' NERD_commenter.txt /*'NERDSpaceDelims'* +'NERDTreeAutoCenter' NERD_tree.txt /*'NERDTreeAutoCenter'* +'NERDTreeAutoCenterThreshold' NERD_tree.txt /*'NERDTreeAutoCenterThreshold'* +'NERDTreeBookmarksFile' NERD_tree.txt /*'NERDTreeBookmarksFile'* +'NERDTreeCaseSensitiveSort' NERD_tree.txt /*'NERDTreeCaseSensitiveSort'* +'NERDTreeChDirMode' NERD_tree.txt /*'NERDTreeChDirMode'* +'NERDTreeHighlightCursorline' NERD_tree.txt /*'NERDTreeHighlightCursorline'* +'NERDTreeHijackNetrw' NERD_tree.txt /*'NERDTreeHijackNetrw'* +'NERDTreeIgnore' NERD_tree.txt /*'NERDTreeIgnore'* +'NERDTreeMouseMode' NERD_tree.txt /*'NERDTreeMouseMode'* +'NERDTreeQuitOnOpen' NERD_tree.txt /*'NERDTreeQuitOnOpen'* +'NERDTreeShowBookmarks' NERD_tree.txt /*'NERDTreeShowBookmarks'* +'NERDTreeShowFiles' NERD_tree.txt /*'NERDTreeShowFiles'* +'NERDTreeShowHidden' NERD_tree.txt /*'NERDTreeShowHidden'* +'NERDTreeShowLineNumbers' NERD_tree.txt /*'NERDTreeShowLineNumbers'* +'NERDTreeSortOrder' NERD_tree.txt /*'NERDTreeSortOrder'* +'NERDTreeStatusline' NERD_tree.txt /*'NERDTreeStatusline'* +'NERDTreeWinPos' NERD_tree.txt /*'NERDTreeWinPos'* +'NERDTreeWinSize' NERD_tree.txt /*'NERDTreeWinSize'* +'NERDUsePlaceHolders' NERD_commenter.txt /*'NERDUsePlaceHolders'* +'loaded_nerd_comments' NERD_commenter.txt /*'loaded_nerd_comments'* +'loaded_nerd_tree' NERD_tree.txt /*'loaded_nerd_tree'* +'snippets' snipMate.txt /*'snippets'* +.snippet snipMate.txt /*.snippet* +.snippets snipMate.txt /*.snippets* +:AcpDisable acp.txt /*:AcpDisable* +:AcpEnable acp.txt /*:AcpEnable* +:AcpLock acp.txt /*:AcpLock* +:AcpUnlock acp.txt /*:AcpUnlock* +:NERDTree NERD_tree.txt /*:NERDTree* +:NERDTreeClose NERD_tree.txt /*:NERDTreeClose* +:NERDTreeFind NERD_tree.txt /*:NERDTreeFind* +:NERDTreeFromBookmark NERD_tree.txt /*:NERDTreeFromBookmark* +:NERDTreeMirror NERD_tree.txt /*:NERDTreeMirror* +:NERDTreeToggle NERD_tree.txt /*:NERDTreeToggle* +ConqueTerm conque_term.txt /*ConqueTerm* +ConqueTerm_CWInsert conque_term.txt /*ConqueTerm_CWInsert* +ConqueTerm_CloseOnEnd conque_term.txt /*ConqueTerm_CloseOnEnd* +ConqueTerm_CodePage conque_term.txt /*ConqueTerm_CodePage* +ConqueTerm_Color conque_term.txt /*ConqueTerm_Color* +ConqueTerm_ColorMode conque_term.txt /*ConqueTerm_ColorMode* +ConqueTerm_EscKey conque_term.txt /*ConqueTerm_EscKey* +ConqueTerm_ExecFileKey conque_term.txt /*ConqueTerm_ExecFileKey* +ConqueTerm_FastMode conque_term.txt /*ConqueTerm_FastMode* +ConqueTerm_InsertOnEnter conque_term.txt /*ConqueTerm_InsertOnEnter* +ConqueTerm_PromptRegex conque_term.txt /*ConqueTerm_PromptRegex* +ConqueTerm_PyExe conque_term.txt /*ConqueTerm_PyExe* +ConqueTerm_PyVersion conque_term.txt /*ConqueTerm_PyVersion* +ConqueTerm_ReadUnfocused conque_term.txt /*ConqueTerm_ReadUnfocused* +ConqueTerm_SendFileKey conque_term.txt /*ConqueTerm_SendFileKey* +ConqueTerm_SendFunctionKeys conque_term.txt /*ConqueTerm_SendFunctionKeys* +ConqueTerm_SendVisKey conque_term.txt /*ConqueTerm_SendVisKey* +ConqueTerm_SessionSupport conque_term.txt /*ConqueTerm_SessionSupport* +ConqueTerm_StartMessages conque_term.txt /*ConqueTerm_StartMessages* +ConqueTerm_Syntax conque_term.txt /*ConqueTerm_Syntax* +ConqueTerm_TERM conque_term.txt /*ConqueTerm_TERM* +ConqueTerm_ToggleKey conque_term.txt /*ConqueTerm_ToggleKey* +ExtractSnips() snipMate.txt /*ExtractSnips()* +ExtractSnipsFile() snipMate.txt /*ExtractSnipsFile()* +Filename() snipMate.txt /*Filename()* +NERDComAbout NERD_commenter.txt /*NERDComAbout* +NERDComAlignedComment NERD_commenter.txt /*NERDComAlignedComment* +NERDComAltDelim NERD_commenter.txt /*NERDComAltDelim* +NERDComAppendComment NERD_commenter.txt /*NERDComAppendComment* +NERDComChangelog NERD_commenter.txt /*NERDComChangelog* +NERDComComment NERD_commenter.txt /*NERDComComment* +NERDComCredits NERD_commenter.txt /*NERDComCredits* +NERDComDefaultDelims NERD_commenter.txt /*NERDComDefaultDelims* +NERDComEOLComment NERD_commenter.txt /*NERDComEOLComment* +NERDComFunctionality NERD_commenter.txt /*NERDComFunctionality* +NERDComFunctionalityDetails NERD_commenter.txt /*NERDComFunctionalityDetails* +NERDComFunctionalitySummary NERD_commenter.txt /*NERDComFunctionalitySummary* +NERDComHeuristics NERD_commenter.txt /*NERDComHeuristics* +NERDComInsertComment NERD_commenter.txt /*NERDComInsertComment* +NERDComInstallation NERD_commenter.txt /*NERDComInstallation* +NERDComInvertComment NERD_commenter.txt /*NERDComInvertComment* +NERDComIssues NERD_commenter.txt /*NERDComIssues* +NERDComLicense NERD_commenter.txt /*NERDComLicense* +NERDComMappings NERD_commenter.txt /*NERDComMappings* +NERDComMinimalComment NERD_commenter.txt /*NERDComMinimalComment* +NERDComNERDComment NERD_commenter.txt /*NERDComNERDComment* +NERDComNestedComment NERD_commenter.txt /*NERDComNestedComment* +NERDComNesting NERD_commenter.txt /*NERDComNesting* +NERDComOptions NERD_commenter.txt /*NERDComOptions* +NERDComOptionsDetails NERD_commenter.txt /*NERDComOptionsDetails* +NERDComOptionsSummary NERD_commenter.txt /*NERDComOptionsSummary* +NERDComSexyComment NERD_commenter.txt /*NERDComSexyComment* +NERDComSexyComments NERD_commenter.txt /*NERDComSexyComments* +NERDComToggleComment NERD_commenter.txt /*NERDComToggleComment* +NERDComUncommentLine NERD_commenter.txt /*NERDComUncommentLine* +NERDComYankComment NERD_commenter.txt /*NERDComYankComment* +NERDCommenter NERD_commenter.txt /*NERDCommenter* +NERDCommenterContents NERD_commenter.txt /*NERDCommenterContents* +NERDTree NERD_tree.txt /*NERDTree* +NERDTree-? NERD_tree.txt /*NERDTree-?* +NERDTree-A NERD_tree.txt /*NERDTree-A* +NERDTree-B NERD_tree.txt /*NERDTree-B* +NERDTree-C NERD_tree.txt /*NERDTree-C* +NERDTree-C-J NERD_tree.txt /*NERDTree-C-J* +NERDTree-C-K NERD_tree.txt /*NERDTree-C-K* +NERDTree-D NERD_tree.txt /*NERDTree-D* +NERDTree-F NERD_tree.txt /*NERDTree-F* +NERDTree-I NERD_tree.txt /*NERDTree-I* +NERDTree-J NERD_tree.txt /*NERDTree-J* +NERDTree-K NERD_tree.txt /*NERDTree-K* +NERDTree-O NERD_tree.txt /*NERDTree-O* +NERDTree-P NERD_tree.txt /*NERDTree-P* +NERDTree-R NERD_tree.txt /*NERDTree-R* +NERDTree-T NERD_tree.txt /*NERDTree-T* +NERDTree-U NERD_tree.txt /*NERDTree-U* +NERDTree-X NERD_tree.txt /*NERDTree-X* +NERDTree-cd NERD_tree.txt /*NERDTree-cd* +NERDTree-contents NERD_tree.txt /*NERDTree-contents* +NERDTree-e NERD_tree.txt /*NERDTree-e* +NERDTree-f NERD_tree.txt /*NERDTree-f* +NERDTree-gi NERD_tree.txt /*NERDTree-gi* +NERDTree-go NERD_tree.txt /*NERDTree-go* +NERDTree-gs NERD_tree.txt /*NERDTree-gs* +NERDTree-i NERD_tree.txt /*NERDTree-i* +NERDTree-m NERD_tree.txt /*NERDTree-m* +NERDTree-o NERD_tree.txt /*NERDTree-o* +NERDTree-p NERD_tree.txt /*NERDTree-p* +NERDTree-q NERD_tree.txt /*NERDTree-q* +NERDTree-r NERD_tree.txt /*NERDTree-r* +NERDTree-s NERD_tree.txt /*NERDTree-s* +NERDTree-t NERD_tree.txt /*NERDTree-t* +NERDTree-u NERD_tree.txt /*NERDTree-u* +NERDTree-x NERD_tree.txt /*NERDTree-x* +NERDTreeAPI NERD_tree.txt /*NERDTreeAPI* +NERDTreeAbout NERD_tree.txt /*NERDTreeAbout* +NERDTreeAddKeyMap() NERD_tree.txt /*NERDTreeAddKeyMap()* +NERDTreeAddMenuItem() NERD_tree.txt /*NERDTreeAddMenuItem()* +NERDTreeAddMenuSeparator() NERD_tree.txt /*NERDTreeAddMenuSeparator()* +NERDTreeAddSubmenu() NERD_tree.txt /*NERDTreeAddSubmenu()* +NERDTreeBookmarkCommands NERD_tree.txt /*NERDTreeBookmarkCommands* +NERDTreeBookmarkTable NERD_tree.txt /*NERDTreeBookmarkTable* +NERDTreeBookmarks NERD_tree.txt /*NERDTreeBookmarks* +NERDTreeChangelog NERD_tree.txt /*NERDTreeChangelog* +NERDTreeCredits NERD_tree.txt /*NERDTreeCredits* +NERDTreeFunctionality NERD_tree.txt /*NERDTreeFunctionality* +NERDTreeGlobalCommands NERD_tree.txt /*NERDTreeGlobalCommands* +NERDTreeInvalidBookmarks NERD_tree.txt /*NERDTreeInvalidBookmarks* +NERDTreeKeymapAPI NERD_tree.txt /*NERDTreeKeymapAPI* +NERDTreeLicense NERD_tree.txt /*NERDTreeLicense* +NERDTreeMappings NERD_tree.txt /*NERDTreeMappings* +NERDTreeMenu NERD_tree.txt /*NERDTreeMenu* +NERDTreeMenuAPI NERD_tree.txt /*NERDTreeMenuAPI* +NERDTreeOptionDetails NERD_tree.txt /*NERDTreeOptionDetails* +NERDTreeOptionSummary NERD_tree.txt /*NERDTreeOptionSummary* +NERDTreeOptions NERD_tree.txt /*NERDTreeOptions* +NERDTreeRender() NERD_tree.txt /*NERDTreeRender()* +NERD_commenter.txt NERD_commenter.txt /*NERD_commenter.txt* +NERD_tree.txt NERD_tree.txt /*NERD_tree.txt* +ResetSnippets() snipMate.txt /*ResetSnippets()* +acp acp.txt /*acp* +acp-about acp.txt /*acp-about* +acp-author acp.txt /*acp-author* +acp-changelog acp.txt /*acp-changelog* +acp-commands acp.txt /*acp-commands* +acp-contact acp.txt /*acp-contact* +acp-installation acp.txt /*acp-installation* +acp-introduction acp.txt /*acp-introduction* +acp-options acp.txt /*acp-options* +acp-perl-omni acp.txt /*acp-perl-omni* +acp-snipMate acp.txt /*acp-snipMate* +acp-thanks acp.txt /*acp-thanks* +acp-usage acp.txt /*acp-usage* +acp.txt acp.txt /*acp.txt* +autocomplpop acp.txt /*autocomplpop* +bufexplorer bufexplorer.txt /*bufexplorer* +bufexplorer-changelog bufexplorer.txt /*bufexplorer-changelog* +bufexplorer-credits bufexplorer.txt /*bufexplorer-credits* +bufexplorer-customization bufexplorer.txt /*bufexplorer-customization* +bufexplorer-installation bufexplorer.txt /*bufexplorer-installation* +bufexplorer-todo bufexplorer.txt /*bufexplorer-todo* +bufexplorer-usage bufexplorer.txt /*bufexplorer-usage* +bufexplorer-windowlayout bufexplorer.txt /*bufexplorer-windowlayout* +bufexplorer.txt bufexplorer.txt /*bufexplorer.txt* +buffer-explorer bufexplorer.txt /*buffer-explorer* +conque-config-general conque_term.txt /*conque-config-general* +conque-config-keyboard conque_term.txt /*conque-config-keyboard* +conque-config-unix conque_term.txt /*conque-config-unix* +conque-config-windows conque_term.txt /*conque-config-windows* +conque-term-api conque_term.txt /*conque-term-api* +conque-term-bugs conque_term.txt /*conque-term-bugs* +conque-term-close conque_term.txt /*conque-term-close* +conque-term-contribute conque_term.txt /*conque-term-contribute* +conque-term-esc conque_term.txt /*conque-term-esc* +conque-term-events conque_term.txt /*conque-term-events* +conque-term-feedback conque_term.txt /*conque-term-feedback* +conque-term-gen-usage conque_term.txt /*conque-term-gen-usage* +conque-term-get-instance conque_term.txt /*conque-term-get-instance* +conque-term-input-mode conque_term.txt /*conque-term-input-mode* +conque-term-installation conque_term.txt /*conque-term-installation* +conque-term-misc conque_term.txt /*conque-term-misc* +conque-term-open conque_term.txt /*conque-term-open* +conque-term-options conque_term.txt /*conque-term-options* +conque-term-read conque_term.txt /*conque-term-read* +conque-term-register conque_term.txt /*conque-term-register* +conque-term-requirements conque_term.txt /*conque-term-requirements* +conque-term-send conque_term.txt /*conque-term-send* +conque-term-set-callback conque_term.txt /*conque-term-set-callback* +conque-term-setup conque_term.txt /*conque-term-setup* +conque-term-special-keys conque_term.txt /*conque-term-special-keys* +conque-term-subprocess conque_term.txt /*conque-term-subprocess* +conque-term-usage conque_term.txt /*conque-term-usage* +conque-term-windows conque_term.txt /*conque-term-windows* +conque-term-write conque_term.txt /*conque-term-write* +conque-term-writeln conque_term.txt /*conque-term-writeln* +g:acp_behavior acp.txt /*g:acp_behavior* +g:acp_behavior-command acp.txt /*g:acp_behavior-command* +g:acp_behavior-completefunc acp.txt /*g:acp_behavior-completefunc* +g:acp_behavior-meets acp.txt /*g:acp_behavior-meets* +g:acp_behavior-onPopupClose acp.txt /*g:acp_behavior-onPopupClose* +g:acp_behavior-repeat acp.txt /*g:acp_behavior-repeat* +g:acp_behaviorCssOmniPropertyLength acp.txt /*g:acp_behaviorCssOmniPropertyLength* +g:acp_behaviorCssOmniValueLength acp.txt /*g:acp_behaviorCssOmniValueLength* +g:acp_behaviorFileLength acp.txt /*g:acp_behaviorFileLength* +g:acp_behaviorHtmlOmniLength acp.txt /*g:acp_behaviorHtmlOmniLength* +g:acp_behaviorKeywordCommand acp.txt /*g:acp_behaviorKeywordCommand* +g:acp_behaviorKeywordIgnores acp.txt /*g:acp_behaviorKeywordIgnores* +g:acp_behaviorKeywordLength acp.txt /*g:acp_behaviorKeywordLength* +g:acp_behaviorPerlOmniLength acp.txt /*g:acp_behaviorPerlOmniLength* +g:acp_behaviorPythonOmniLength acp.txt /*g:acp_behaviorPythonOmniLength* +g:acp_behaviorRubyOmniMethodLength acp.txt /*g:acp_behaviorRubyOmniMethodLength* +g:acp_behaviorRubyOmniSymbolLength acp.txt /*g:acp_behaviorRubyOmniSymbolLength* +g:acp_behaviorSnipmateLength acp.txt /*g:acp_behaviorSnipmateLength* +g:acp_behaviorUserDefinedFunction acp.txt /*g:acp_behaviorUserDefinedFunction* +g:acp_behaviorUserDefinedMeets acp.txt /*g:acp_behaviorUserDefinedMeets* +g:acp_behaviorXmlOmniLength acp.txt /*g:acp_behaviorXmlOmniLength* +g:acp_completeOption acp.txt /*g:acp_completeOption* +g:acp_completeoptPreview acp.txt /*g:acp_completeoptPreview* +g:acp_enableAtStartup acp.txt /*g:acp_enableAtStartup* +g:acp_ignorecaseOption acp.txt /*g:acp_ignorecaseOption* +g:acp_mappingDriven acp.txt /*g:acp_mappingDriven* +g:bufExplorerChgWin bufexplorer.txt /*g:bufExplorerChgWin* +g:bufExplorerDefaultHelp bufexplorer.txt /*g:bufExplorerDefaultHelp* +g:bufExplorerDetailedHelp bufexplorer.txt /*g:bufExplorerDetailedHelp* +g:bufExplorerFindActive bufexplorer.txt /*g:bufExplorerFindActive* +g:bufExplorerFuncRef bufexplorer.txt /*g:bufExplorerFuncRef* +g:bufExplorerReverseSort bufexplorer.txt /*g:bufExplorerReverseSort* +g:bufExplorerShowDirectories bufexplorer.txt /*g:bufExplorerShowDirectories* +g:bufExplorerShowRelativePath bufexplorer.txt /*g:bufExplorerShowRelativePath* +g:bufExplorerShowTabBuffer bufexplorer.txt /*g:bufExplorerShowTabBuffer* +g:bufExplorerShowUnlisted bufexplorer.txt /*g:bufExplorerShowUnlisted* +g:bufExplorerSortBy bufexplorer.txt /*g:bufExplorerSortBy* +g:bufExplorerSplitBelow bufexplorer.txt /*g:bufExplorerSplitBelow* +g:bufExplorerSplitOutPathName bufexplorer.txt /*g:bufExplorerSplitOutPathName* +g:bufExplorerSplitRight bufexplorer.txt /*g:bufExplorerSplitRight* +g:snippets_dir snipMate.txt /*g:snippets_dir* +g:snips_author snipMate.txt /*g:snips_author* +i_CTRL-R_ snipMate.txt /*i_CTRL-R_* +list-snippets snipMate.txt /*list-snippets* +multi_snip snipMate.txt /*multi_snip* +snipMate snipMate.txt /*snipMate* +snipMate-$# snipMate.txt /*snipMate-$#* +snipMate-${#:} snipMate.txt /*snipMate-${#:}* +snipMate-${#} snipMate.txt /*snipMate-${#}* +snipMate-author snipMate.txt /*snipMate-author* +snipMate-commands snipMate.txt /*snipMate-commands* +snipMate-contact snipMate.txt /*snipMate-contact* +snipMate-description snipMate.txt /*snipMate-description* +snipMate-disadvantages snipMate.txt /*snipMate-disadvantages* +snipMate-expandtab snipMate.txt /*snipMate-expandtab* +snipMate-features snipMate.txt /*snipMate-features* +snipMate-filename snipMate.txt /*snipMate-filename* +snipMate-indenting snipMate.txt /*snipMate-indenting* +snipMate-placeholders snipMate.txt /*snipMate-placeholders* +snipMate-remap snipMate.txt /*snipMate-remap* +snipMate-settings snipMate.txt /*snipMate-settings* +snipMate-usage snipMate.txt /*snipMate-usage* +snipMate.txt snipMate.txt /*snipMate.txt* +snippet snipMate.txt /*snippet* +snippet-syntax snipMate.txt /*snippet-syntax* +snippets snipMate.txt /*snippets* diff --git a/doc/tags-ja b/doc/tags-ja new file mode 100644 index 0000000..816c6e0 --- /dev/null +++ b/doc/tags-ja @@ -0,0 +1,44 @@ +!_TAG_FILE_ENCODING utf-8 // +:AcpDisable acp.jax /*:AcpDisable* +:AcpEnable acp.jax /*:AcpEnable* +:AcpLock acp.jax /*:AcpLock* +:AcpUnlock acp.jax /*:AcpUnlock* +acp acp.jax /*acp* +acp-about acp.jax /*acp-about* +acp-author acp.jax /*acp-author* +acp-commands acp.jax /*acp-commands* +acp-contact acp.jax /*acp-contact* +acp-installation acp.jax /*acp-installation* +acp-introduction acp.jax /*acp-introduction* +acp-options acp.jax /*acp-options* +acp-perl-omni acp.jax /*acp-perl-omni* +acp-snipMate acp.jax /*acp-snipMate* +acp-usage acp.jax /*acp-usage* +acp.txt acp.jax /*acp.txt* +autocomplpop acp.jax /*autocomplpop* +g:acp_behavior acp.jax /*g:acp_behavior* +g:acp_behavior-command acp.jax /*g:acp_behavior-command* +g:acp_behavior-completefunc acp.jax /*g:acp_behavior-completefunc* +g:acp_behavior-meets acp.jax /*g:acp_behavior-meets* +g:acp_behavior-onPopupClose acp.jax /*g:acp_behavior-onPopupClose* +g:acp_behavior-repeat acp.jax /*g:acp_behavior-repeat* +g:acp_behaviorCssOmniPropertyLength acp.jax /*g:acp_behaviorCssOmniPropertyLength* +g:acp_behaviorCssOmniValueLength acp.jax /*g:acp_behaviorCssOmniValueLength* +g:acp_behaviorFileLength acp.jax /*g:acp_behaviorFileLength* +g:acp_behaviorHtmlOmniLength acp.jax /*g:acp_behaviorHtmlOmniLength* +g:acp_behaviorKeywordCommand acp.jax /*g:acp_behaviorKeywordCommand* +g:acp_behaviorKeywordIgnores acp.jax /*g:acp_behaviorKeywordIgnores* +g:acp_behaviorKeywordLength acp.jax /*g:acp_behaviorKeywordLength* +g:acp_behaviorPerlOmniLength acp.jax /*g:acp_behaviorPerlOmniLength* +g:acp_behaviorPythonOmniLength acp.jax /*g:acp_behaviorPythonOmniLength* +g:acp_behaviorRubyOmniMethodLength acp.jax /*g:acp_behaviorRubyOmniMethodLength* +g:acp_behaviorRubyOmniSymbolLength acp.jax /*g:acp_behaviorRubyOmniSymbolLength* +g:acp_behaviorSnipmateLength acp.jax /*g:acp_behaviorSnipmateLength* +g:acp_behaviorUserDefinedFunction acp.jax /*g:acp_behaviorUserDefinedFunction* +g:acp_behaviorUserDefinedMeets acp.jax /*g:acp_behaviorUserDefinedMeets* +g:acp_behaviorXmlOmniLength acp.jax /*g:acp_behaviorXmlOmniLength* +g:acp_completeOption acp.jax /*g:acp_completeOption* +g:acp_completeoptPreview acp.jax /*g:acp_completeoptPreview* +g:acp_enableAtStartup acp.jax /*g:acp_enableAtStartup* +g:acp_ignorecaseOption acp.jax /*g:acp_ignorecaseOption* +g:acp_mappingDriven acp.jax /*g:acp_mappingDriven* diff --git a/ftdetect/clojure.vim b/ftdetect/clojure.vim new file mode 100644 index 0000000..3ec6747 --- /dev/null +++ b/ftdetect/clojure.vim @@ -0,0 +1 @@ +au BufNewFile,BufRead *.clj set filetype=clojure diff --git a/ftplugin/html_snip_helper.vim b/ftplugin/html_snip_helper.vim new file mode 100644 index 0000000..2e54570 --- /dev/null +++ b/ftplugin/html_snip_helper.vim @@ -0,0 +1,10 @@ +" Helper function for (x)html snippets +if exists('s:did_snip_helper') || &cp || !exists('loaded_snips') + finish +endif +let s:did_snip_helper = 1 + +" Automatically closes tag if in xhtml +fun! Close() + return stridx(&ft, 'xhtml') == -1 ? '' : ' /' +endf diff --git a/nerdtree_plugin/exec_menuitem.vim b/nerdtree_plugin/exec_menuitem.vim new file mode 100644 index 0000000..e7a7c53 --- /dev/null +++ b/nerdtree_plugin/exec_menuitem.vim @@ -0,0 +1,41 @@ +" ============================================================================ +" File: exec_menuitem.vim +" Description: plugin for NERD Tree that provides an execute file menu item +" Maintainer: Martin Grenfell +" Last Change: 22 July, 2009 +" License: This program is free software. It comes without any warranty, +" to the extent permitted by applicable law. You can redistribute +" it and/or modify it under the terms of the Do What The Fuck You +" Want To Public License, Version 2, as published by Sam Hocevar. +" See http://sam.zoy.org/wtfpl/COPYING for more details. +" +" ============================================================================ +if exists("g:loaded_nerdtree_exec_menuitem") + finish +endif +let g:loaded_nerdtree_exec_menuitem = 1 + +call NERDTreeAddMenuItem({ + \ 'text': '(!)Execute file', + \ 'shortcut': '!', + \ 'callback': 'NERDTreeExecFile', + \ 'isActiveCallback': 'NERDTreeExecFileActive' }) + +function! NERDTreeExecFileActive() + let node = g:NERDTreeFileNode.GetSelected() + return !node.path.isDirectory && node.path.isExecutable +endfunction + +function! NERDTreeExecFile() + let treenode = g:NERDTreeFileNode.GetSelected() + echo "==========================================================\n" + echo "Complete the command to execute (add arguments etc):\n" + let cmd = treenode.path.str({'escape': 1}) + let cmd = input(':!', cmd . ' ') + + if cmd != '' + exec ':!' . cmd + else + echo "Aborted" + endif +endfunction diff --git a/nerdtree_plugin/fs_menu.vim b/nerdtree_plugin/fs_menu.vim new file mode 100644 index 0000000..e25b38c --- /dev/null +++ b/nerdtree_plugin/fs_menu.vim @@ -0,0 +1,194 @@ +" ============================================================================ +" File: fs_menu.vim +" Description: plugin for the NERD Tree that provides a file system menu +" Maintainer: Martin Grenfell +" Last Change: 17 July, 2009 +" License: This program is free software. It comes without any warranty, +" to the extent permitted by applicable law. You can redistribute +" it and/or modify it under the terms of the Do What The Fuck You +" Want To Public License, Version 2, as published by Sam Hocevar. +" See http://sam.zoy.org/wtfpl/COPYING for more details. +" +" ============================================================================ +if exists("g:loaded_nerdtree_fs_menu") + finish +endif +let g:loaded_nerdtree_fs_menu = 1 + +call NERDTreeAddMenuItem({'text': '(a)dd a childnode', 'shortcut': 'a', 'callback': 'NERDTreeAddNode'}) +call NERDTreeAddMenuItem({'text': '(m)ove the curent node', 'shortcut': 'm', 'callback': 'NERDTreeMoveNode'}) +call NERDTreeAddMenuItem({'text': '(d)elete the curent node', 'shortcut': 'd', 'callback': 'NERDTreeDeleteNode'}) +if g:NERDTreePath.CopyingSupported() + call NERDTreeAddMenuItem({'text': '(c)copy the current node', 'shortcut': 'c', 'callback': 'NERDTreeCopyNode'}) +endif + +"FUNCTION: s:echo(msg){{{1 +function! s:echo(msg) + redraw + echomsg "NERDTree: " . a:msg +endfunction + +"FUNCTION: s:echoWarning(msg){{{1 +function! s:echoWarning(msg) + echohl warningmsg + call s:echo(a:msg) + echohl normal +endfunction + +"FUNCTION: s:promptToDelBuffer(bufnum, msg){{{1 +"prints out the given msg and, if the user responds by pushing 'y' then the +"buffer with the given bufnum is deleted +" +"Args: +"bufnum: the buffer that may be deleted +"msg: a message that will be echoed to the user asking them if they wish to +" del the buffer +function! s:promptToDelBuffer(bufnum, msg) + echo a:msg + if nr2char(getchar()) ==# 'y' + exec "silent bdelete! " . a:bufnum + endif +endfunction + +"FUNCTION: NERDTreeAddNode(){{{1 +function! NERDTreeAddNode() + let curDirNode = g:NERDTreeDirNode.GetSelected() + + let newNodeName = input("Add a childnode\n". + \ "==========================================================\n". + \ "Enter the dir/file name to be created. Dirs end with a '/'\n" . + \ "", curDirNode.path.str({'format': 'Glob'}) . g:NERDTreePath.Slash()) + + if newNodeName ==# '' + call s:echo("Node Creation Aborted.") + return + endif + + try + let newPath = g:NERDTreePath.Create(newNodeName) + let parentNode = b:NERDTreeRoot.findNode(newPath.getParent()) + + let newTreeNode = g:NERDTreeFileNode.New(newPath) + if parentNode.isOpen || !empty(parentNode.children) + call parentNode.addChild(newTreeNode, 1) + call NERDTreeRender() + call newTreeNode.putCursorHere(1, 0) + endif + catch /^NERDTree/ + call s:echoWarning("Node Not Created.") + endtry +endfunction + +"FUNCTION: NERDTreeMoveNode(){{{1 +function! NERDTreeMoveNode() + let curNode = g:NERDTreeFileNode.GetSelected() + let newNodePath = input("Rename the current node\n" . + \ "==========================================================\n" . + \ "Enter the new path for the node: \n" . + \ "", curNode.path.str()) + + if newNodePath ==# '' + call s:echo("Node Renaming Aborted.") + return + endif + + try + let bufnum = bufnr(curNode.path.str()) + + call curNode.rename(newNodePath) + call NERDTreeRender() + + "if the node is open in a buffer, ask the user if they want to + "close that buffer + if bufnum != -1 + let prompt = "\nNode renamed.\n\nThe old file is open in buffer ". bufnum . (bufwinnr(bufnum) ==# -1 ? " (hidden)" : "") .". Delete this buffer? (yN)" + call s:promptToDelBuffer(bufnum, prompt) + endif + + call curNode.putCursorHere(1, 0) + + redraw + catch /^NERDTree/ + call s:echoWarning("Node Not Renamed.") + endtry +endfunction + +" FUNCTION: NERDTreeDeleteNode() {{{1 +function! NERDTreeDeleteNode() + let currentNode = g:NERDTreeFileNode.GetSelected() + let confirmed = 0 + + if currentNode.path.isDirectory + let choice =input("Delete the current node\n" . + \ "==========================================================\n" . + \ "STOP! To delete this entire directory, type 'yes'\n" . + \ "" . currentNode.path.str() . ": ") + let confirmed = choice ==# 'yes' + else + echo "Delete the current node\n" . + \ "==========================================================\n". + \ "Are you sure you wish to delete the node:\n" . + \ "" . currentNode.path.str() . " (yN):" + let choice = nr2char(getchar()) + let confirmed = choice ==# 'y' + endif + + + if confirmed + try + call currentNode.delete() + call NERDTreeRender() + + "if the node is open in a buffer, ask the user if they want to + "close that buffer + let bufnum = bufnr(currentNode.path.str()) + if buflisted(bufnum) + let prompt = "\nNode deleted.\n\nThe file is open in buffer ". bufnum . (bufwinnr(bufnum) ==# -1 ? " (hidden)" : "") .". Delete this buffer? (yN)" + call s:promptToDelBuffer(bufnum, prompt) + endif + + redraw + catch /^NERDTree/ + call s:echoWarning("Could not remove node") + endtry + else + call s:echo("delete aborted") + endif + +endfunction + +" FUNCTION: NERDTreeCopyNode() {{{1 +function! NERDTreeCopyNode() + let currentNode = g:NERDTreeFileNode.GetSelected() + let newNodePath = input("Copy the current node\n" . + \ "==========================================================\n" . + \ "Enter the new path to copy the node to: \n" . + \ "", currentNode.path.str()) + + if newNodePath != "" + "strip trailing slash + let newNodePath = substitute(newNodePath, '\/$', '', '') + + let confirmed = 1 + if currentNode.path.copyingWillOverwrite(newNodePath) + call s:echo("Warning: copying may overwrite files! Continue? (yN)") + let choice = nr2char(getchar()) + let confirmed = choice ==# 'y' + endif + + if confirmed + try + let newNode = currentNode.copy(newNodePath) + call NERDTreeRender() + call newNode.putCursorHere(0, 0) + catch /^NERDTree/ + call s:echoWarning("Could not copy node") + endtry + endif + else + call s:echo("Copy aborted.") + endif + redraw +endfunction + +" vim: set sw=4 sts=4 et fdm=marker: diff --git a/plugin/NERD_commenter.vim b/plugin/NERD_commenter.vim new file mode 100644 index 0000000..ef88dd4 --- /dev/null +++ b/plugin/NERD_commenter.vim @@ -0,0 +1,2790 @@ +" ============================================================================ +" File: NERD_commenter.vim +" Description: vim global plugin that provides easy code commenting +" Maintainer: Martin Grenfell +" Version: 2.3.0 +" Last Change: 08th December, 2010 +" License: This program is free software. It comes without any warranty, +" to the extent permitted by applicable law. You can redistribute +" it and/or modify it under the terms of the Do What The Fuck You +" Want To Public License, Version 2, as published by Sam Hocevar. +" See http://sam.zoy.org/wtfpl/COPYING for more details. +" +" ============================================================================ + +" Section: script init stuff {{{1 +if exists("loaded_nerd_comments") + finish +endif +if v:version < 700 + echoerr "NERDCommenter: this plugin requires vim >= 7. DOWNLOAD IT! You'll thank me later!" + finish +endif +let loaded_nerd_comments = 1 + +" Function: s:InitVariable() function {{{2 +" This function is used to initialise a given variable to a given value. The +" variable is only initialised if it does not exist prior +" +" Args: +" -var: the name of the var to be initialised +" -value: the value to initialise var to +" +" Returns: +" 1 if the var is set, 0 otherwise +function s:InitVariable(var, value) + if !exists(a:var) + exec 'let ' . a:var . ' = ' . "'" . a:value . "'" + return 1 + endif + return 0 +endfunction + +" Section: space string init{{{2 +" When putting spaces after the left delim and before the right we use +" s:spaceStr for the space char. This way we can make it add anything after +" the left and before the right by modifying this variable +let s:spaceStr = ' ' +let s:lenSpaceStr = strlen(s:spaceStr) + +" Section: variable init calls {{{2 +call s:InitVariable("g:NERDAllowAnyVisualDelims", 1) +call s:InitVariable("g:NERDBlockComIgnoreEmpty", 0) +call s:InitVariable("g:NERDCommentWholeLinesInVMode", 0) +call s:InitVariable("g:NERDCompactSexyComs", 0) +call s:InitVariable("g:NERDCreateDefaultMappings", 1) +call s:InitVariable("g:NERDDefaultNesting", 1) +call s:InitVariable("g:NERDMenuMode", 3) +call s:InitVariable("g:NERDLPlace", "[>") +call s:InitVariable("g:NERDUsePlaceHolders", 1) +call s:InitVariable("g:NERDRemoveAltComs", 1) +call s:InitVariable("g:NERDRemoveExtraSpaces", 1) +call s:InitVariable("g:NERDRPlace", "<]") +call s:InitVariable("g:NERDSpaceDelims", 0) +call s:InitVariable("g:NERDDelimiterRequests", 1) + +let s:NERDFileNameEscape="[]#*$%'\" ?`!&();<>\\" +"vf ;;dA:hcs"'A {j^f(lyi(k$p0f{a A }0f{a 'left':jdd^ + +let s:delimiterMap = { + \ 'aap': { 'left': '#' }, + \ 'abc': { 'left': '%' }, + \ 'acedb': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'actionscript': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'ada': { 'left': '--', 'leftAlt': '-- ' }, + \ 'ahdl': { 'left': '--' }, + \ 'ahk': { 'left': ';', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'amiga': { 'left': ';' }, + \ 'aml': { 'left': '/*' }, + \ 'ampl': { 'left': '#' }, + \ 'apache': { 'left': '#' }, + \ 'apachestyle': { 'left': '#' }, + \ 'asciidoc': { 'left': '//' }, + \ 'applescript': { 'left': '--', 'leftAlt': '(*', 'rightAlt': '*)' }, + \ 'asm68k': { 'left': ';' }, + \ 'asm': { 'left': ';', 'leftAlt': '#' }, + \ 'asn': { 'left': '--' }, + \ 'aspvbs': { 'left': '''' }, + \ 'asterisk': { 'left': ';' }, + \ 'asy': { 'left': '//' }, + \ 'atlas': { 'left': 'C', 'right': '$' }, + \ 'autohotkey': { 'left': ';' }, + \ 'autoit': { 'left': ';' }, + \ 'ave': { 'left': "'" }, + \ 'awk': { 'left': '#' }, + \ 'basic': { 'left': "'", 'leftAlt': 'REM ' }, + \ 'bbx': { 'left': '%' }, + \ 'bc': { 'left': '#' }, + \ 'bib': { 'left': '%' }, + \ 'bindzone': { 'left': ';' }, + \ 'bst': { 'left': '%' }, + \ 'btm': { 'left': '::' }, + \ 'caos': { 'left': '*' }, + \ 'calibre': { 'left': '//' }, + \ 'catalog': { 'left': '--', 'right': '--' }, + \ 'c': { 'left': '/*','right': '*/', 'leftAlt': '//' }, + \ 'cfg': { 'left': '#' }, + \ 'cg': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'ch': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'cl': { 'left': '#' }, + \ 'clean': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'clipper': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'clojure': { 'left': ';' }, + \ 'cmake': { 'left': '#' }, + \ 'conkyrc': { 'left': '#' }, + \ 'cpp': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'crontab': { 'left': '#' }, + \ 'cs': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'csp': { 'left': '--' }, + \ 'cterm': { 'left': '*' }, + \ 'cucumber': { 'left': '#' }, + \ 'cvs': { 'left': 'CVS:' }, + \ 'd': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'dcl': { 'left': '$!' }, + \ 'dakota': { 'left': '#' }, + \ 'debcontrol': { 'left': '#' }, + \ 'debsources': { 'left': '#' }, + \ 'def': { 'left': ';' }, + \ 'desktop': { 'left': '#' }, + \ 'dhcpd': { 'left': '#' }, + \ 'diff': { 'left': '#' }, + \ 'django': { 'left': '', 'leftAlt': '{#', 'rightAlt': '#}' }, + \ 'docbk': { 'left': '' }, + \ 'dns': { 'left': ';' }, + \ 'dosbatch': { 'left': 'REM ', 'leftAlt': '::' }, + \ 'dosini': { 'left': ';' }, + \ 'dot': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'dracula': { 'left': ';' }, + \ 'dsl': { 'left': ';' }, + \ 'dtml': { 'left': '', 'right': '' }, + \ 'dylan': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'ebuild': { 'left': '#' }, + \ 'ecd': { 'left': '#' }, + \ 'eclass': { 'left': '#' }, + \ 'eiffel': { 'left': '--' }, + \ 'elf': { 'left': "'" }, + \ 'elmfilt': { 'left': '#' }, + \ 'erlang': { 'left': '%' }, + \ 'eruby': { 'left': '<%#', 'right': '%>', 'leftAlt': '' }, + \ 'expect': { 'left': '#' }, + \ 'exports': { 'left': '#' }, + \ 'factor': { 'left': '! ', 'leftAlt': '!# ' }, + \ 'fgl': { 'left': '#' }, + \ 'focexec': { 'left': '-*' }, + \ 'form': { 'left': '*' }, + \ 'foxpro': { 'left': '*' }, + \ 'fstab': { 'left': '#' }, + \ 'fvwm': { 'left': '#' }, + \ 'fx': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'gams': { 'left': '*' }, + \ 'gdb': { 'left': '#' }, + \ 'gdmo': { 'left': '--' }, + \ 'geek': { 'left': 'GEEK_COMMENT:' }, + \ 'genshi': { 'left': '', 'leftAlt': '{#', 'rightAlt': '#}' }, + \ 'gentoo-conf-d': { 'left': '#' }, + \ 'gentoo-env-d': { 'left': '#' }, + \ 'gentoo-init-d': { 'left': '#' }, + \ 'gentoo-make-conf': { 'left': '#' }, + \ 'gentoo-package-keywords': { 'left': '#' }, + \ 'gentoo-package-mask': { 'left': '#' }, + \ 'gentoo-package-use': { 'left': '#' }, + \ 'gitcommit': { 'left': '#' }, + \ 'gitconfig': { 'left': ';' }, + \ 'gitrebase': { 'left': '#' }, + \ 'gnuplot': { 'left': '#' }, + \ 'groovy': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'gsp': { 'left': '<%--', 'right': '--%>' }, + \ 'gtkrc': { 'left': '#' }, + \ 'haskell': { 'left': '{-','right': '-}', 'leftAlt': '--' }, + \ 'hb': { 'left': '#' }, + \ 'h': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'haml': { 'left': '-#', 'leftAlt': '/' }, + \ 'hercules': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'hog': { 'left': '#' }, + \ 'hostsaccess': { 'left': '#' }, + \ 'htmlcheetah': { 'left': '##' }, + \ 'htmldjango': { 'left': '', 'leftAlt': '{#', 'rightAlt': '#}' }, + \ 'htmlos': { 'left': '#', 'right': '/#' }, + \ 'ia64': { 'left': '#' }, + \ 'icon': { 'left': '#' }, + \ 'idlang': { 'left': ';' }, + \ 'idl': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'inform': { 'left': '!' }, + \ 'inittab': { 'left': '#' }, + \ 'ishd': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'iss': { 'left': ';' }, + \ 'ist': { 'left': '%' }, + \ 'java': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'javacc': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'javascript': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'javascript.jquery': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'jess': { 'left': ';' }, + \ 'jgraph': { 'left': '(*', 'right': '*)' }, + \ 'jproperties': { 'left': '#' }, + \ 'jsp': { 'left': '<%--', 'right': '--%>' }, + \ 'kix': { 'left': ';' }, + \ 'kscript': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'lace': { 'left': '--' }, + \ 'ldif': { 'left': '#' }, + \ 'lilo': { 'left': '#' }, + \ 'lilypond': { 'left': '%' }, + \ 'liquid': { 'left': '{%', 'right': '%}' }, + \ 'lisp': { 'left': ';', 'leftAlt': '#|', 'rightAlt': '|#' }, + \ 'llvm': { 'left': ';' }, + \ 'lotos': { 'left': '(*', 'right': '*)' }, + \ 'lout': { 'left': '#' }, + \ 'lprolog': { 'left': '%' }, + \ 'lscript': { 'left': "'" }, + \ 'lss': { 'left': '#' }, + \ 'lua': { 'left': '--', 'leftAlt': '--[[', 'rightAlt': ']]' }, + \ 'lynx': { 'left': '#' }, + \ 'lytex': { 'left': '%' }, + \ 'mail': { 'left': '> ' }, + \ 'mako': { 'left': '##' }, + \ 'man': { 'left': '."' }, + \ 'map': { 'left': '%' }, + \ 'maple': { 'left': '#' }, + \ 'markdown': { 'left': '' }, + \ 'masm': { 'left': ';' }, + \ 'mason': { 'left': '<% #', 'right': '%>' }, + \ 'master': { 'left': '$' }, + \ 'matlab': { 'left': '%' }, + \ 'mel': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'mib': { 'left': '--' }, + \ 'mkd': { 'left': '>' }, + \ 'mma': { 'left': '(*', 'right': '*)' }, + \ 'model': { 'left': '$', 'right': '$' }, + \ 'moduala.': { 'left': '(*', 'right': '*)' }, + \ 'modula2': { 'left': '(*', 'right': '*)' }, + \ 'modula3': { 'left': '(*', 'right': '*)' }, + \ 'monk': { 'left': ';' }, + \ 'mush': { 'left': '#' }, + \ 'named': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'nasm': { 'left': ';' }, + \ 'nastran': { 'left': '$' }, + \ 'natural': { 'left': '/*' }, + \ 'ncf': { 'left': ';' }, + \ 'newlisp': { 'left': ';' }, + \ 'nroff': { 'left': '\"' }, + \ 'nsis': { 'left': '#' }, + \ 'ntp': { 'left': '#' }, + \ 'objc': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'objcpp': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'objj': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'ocaml': { 'left': '(*', 'right': '*)' }, + \ 'occam': { 'left': '--' }, + \ 'omlet': { 'left': '(*', 'right': '*)' }, + \ 'omnimark': { 'left': ';' }, + \ 'openroad': { 'left': '//' }, + \ 'opl': { 'left': "REM" }, + \ 'ora': { 'left': '#' }, + \ 'ox': { 'left': '//' }, + \ 'pascal': { 'left': '{','right': '}', 'leftAlt': '(*', 'rightAlt': '*)' }, + \ 'patran': { 'left': '$', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'pcap': { 'left': '#' }, + \ 'pccts': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'pdf': { 'left': '%' }, + \ 'pfmain': { 'left': '//' }, + \ 'php': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'pic': { 'left': ';' }, + \ 'pike': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'pilrc': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'pine': { 'left': '#' }, + \ 'plm': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'plsql': { 'left': '--', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'po': { 'left': '#' }, + \ 'postscr': { 'left': '%' }, + \ 'pov': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'povini': { 'left': ';' }, + \ 'ppd': { 'left': '%' }, + \ 'ppwiz': { 'left': ';;' }, + \ 'processing': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'prolog': { 'left': '%', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'ps1': { 'left': '#' }, + \ 'psf': { 'left': '#' }, + \ 'ptcap': { 'left': '#' }, + \ 'python': { 'left': '#' }, + \ 'radiance': { 'left': '#' }, + \ 'ratpoison': { 'left': '#' }, + \ 'r': { 'left': '#' }, + \ 'rc': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'rebol': { 'left': ';' }, + \ 'registry': { 'left': ';' }, + \ 'remind': { 'left': '#' }, + \ 'resolv': { 'left': '#' }, + \ 'rgb': { 'left': '!' }, + \ 'rib': { 'left': '#' }, + \ 'robots': { 'left': '#' }, + \ 'sa': { 'left': '--' }, + \ 'samba': { 'left': ';', 'leftAlt': '#' }, + \ 'sass': { 'left': '//', 'leftAlt': '/*' }, + \ 'sather': { 'left': '--' }, + \ 'scala': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'scilab': { 'left': '//' }, + \ 'scsh': { 'left': ';' }, + \ 'sed': { 'left': '#' }, + \ 'sgmldecl': { 'left': '--', 'right': '--' }, + \ 'sgmllnx': { 'left': '' }, + \ 'sicad': { 'left': '*' }, + \ 'simula': { 'left': '%', 'leftAlt': '--' }, + \ 'sinda': { 'left': '$' }, + \ 'skill': { 'left': ';' }, + \ 'slang': { 'left': '%' }, + \ 'slice': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'slrnrc': { 'left': '%' }, + \ 'sm': { 'left': '#' }, + \ 'smarty': { 'left': '{*', 'right': '*}' }, + \ 'smil': { 'left': '' }, + \ 'smith': { 'left': ';' }, + \ 'sml': { 'left': '(*', 'right': '*)' }, + \ 'snnsnet': { 'left': '#' }, + \ 'snnspat': { 'left': '#' }, + \ 'snnsres': { 'left': '#' }, + \ 'snobol4': { 'left': '*' }, + \ 'spec': { 'left': '#' }, + \ 'specman': { 'left': '//' }, + \ 'spectre': { 'left': '//', 'leftAlt': '*' }, + \ 'spice': { 'left': '$' }, + \ 'sql': { 'left': '--' }, + \ 'sqlforms': { 'left': '--' }, + \ 'sqlj': { 'left': '--' }, + \ 'sqr': { 'left': '!' }, + \ 'squid': { 'left': '#' }, + \ 'st': { 'left': '"' }, + \ 'stp': { 'left': '--' }, + \ 'systemverilog': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'tads': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'tags': { 'left': ';' }, + \ 'tak': { 'left': '$' }, + \ 'tasm': { 'left': ';' }, + \ 'tcl': { 'left': '#' }, + \ 'texinfo': { 'left': "@c " }, + \ 'texmf': { 'left': '%' }, + \ 'tf': { 'left': ';' }, + \ 'tidy': { 'left': '#' }, + \ 'tli': { 'left': '#' }, + \ 'tmux': { 'left': '#' }, + \ 'trasys': { 'left': "$" }, + \ 'tsalt': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'tsscl': { 'left': '#' }, + \ 'tssgm': { 'left': "comment = '", 'right': "'" }, + \ 'txt2tags': { 'left': '%' }, + \ 'uc': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'uil': { 'left': '!' }, + \ 'vb': { 'left': "'" }, + \ 'velocity': { 'left': "##", 'right': "", 'leftAlt': '#*', 'rightAlt': '*#' }, + \ 'vera': { 'left': '/*','right': '*/', 'leftAlt': '//' }, + \ 'verilog': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'verilog_systemverilog': { 'left': '//', 'leftAlt': '/*', 'rightAlt': '*/' }, + \ 'vgrindefs': { 'left': '#' }, + \ 'vhdl': { 'left': '--' }, + \ 'vimperator': { 'left': '"' }, + \ 'virata': { 'left': '%' }, + \ 'vrml': { 'left': '#' }, + \ 'vsejcl': { 'left': '/*' }, + \ 'webmacro': { 'left': '##' }, + \ 'wget': { 'left': '#' }, + \ 'Wikipedia': { 'left': '' }, + \ 'winbatch': { 'left': ';' }, + \ 'wml': { 'left': '#' }, + \ 'wvdial': { 'left': ';' }, + \ 'xdefaults': { 'left': '!' }, + \ 'xkb': { 'left': '//' }, + \ 'xmath': { 'left': '#' }, + \ 'xpm2': { 'left': '!' }, + \ 'xquery': { 'left': '(:', 'right': ':)' }, + \ 'z8a': { 'left': ';' } + \ } + +" Section: Comment mapping functions, autocommands and commands {{{1 +" ============================================================================ +" Section: Comment enabler autocommands {{{2 +" ============================================================================ + +augroup commentEnablers + + "if the user enters a buffer or reads a buffer then we gotta set up + "the comment delimiters for that new filetype + autocmd BufEnter,BufRead * :call s:SetUpForNewFiletype(&filetype, 0) + + "if the filetype of a buffer changes, force the script to reset the + "delims for the buffer + autocmd Filetype * :call s:SetUpForNewFiletype(&filetype, 1) +augroup END + + +" Function: s:SetUpForNewFiletype(filetype) function {{{2 +" This function is responsible for setting up buffer scoped variables for the +" given filetype. +" +" Args: +" -filetype: the filetype to set delimiters for +" -forceReset: 1 if the delimiters should be reset if they have already be +" set for this buffer. +" +function s:SetUpForNewFiletype(filetype, forceReset) + let b:NERDSexyComMarker = '' + + if has_key(s:delimiterMap, a:filetype) + let b:NERDCommenterDelims = s:delimiterMap[a:filetype] + for i in ['left', 'leftAlt', 'right', 'rightAlt'] + if !has_key(b:NERDCommenterDelims, i) + let b:NERDCommenterDelims[i] = '' + endif + endfor + else + let b:NERDCommenterDelims = s:CreateDelimMapFromCms() + endif + +endfunction + +function s:CreateDelimMapFromCms() + return { + \ 'left': substitute(&commentstring, '\([^ \t]*\)\s*%s.*', '\1', ''), + \ 'right': substitute(&commentstring, '.*%s\s*\(.*\)', '\1', 'g'), + \ 'leftAlt': '', + \ 'rightAlt': '' } +endfunction + +" Function: s:SwitchToAlternativeDelimiters(printMsgs) function {{{2 +" This function is used to swap the delimiters that are being used to the +" alternative delimiters for that filetype. For example, if a c++ file is +" being edited and // comments are being used, after this function is called +" /**/ comments will be used. +" +" Args: +" -printMsgs: if this is 1 then a message is echoed to the user telling them +" if this function changed the delimiters or not +function s:SwitchToAlternativeDelimiters(printMsgs) + "if both of the alternative delimiters are empty then there is no + "alternative comment style so bail out + if b:NERDCommenterDelims['leftAlt'] == '' && b:NERDCommenterDelims['rightAlt'] == '' + if a:printMsgs + call s:NerdEcho("Cannot use alternative delimiters, none are specified", 0) + endif + return 0 + endif + + "save the current delimiters + let tempLeft = s:Left() + let tempRight = s:Right() + + "swap current delimiters for alternative + let b:NERDCommenterDelims['left'] = b:NERDCommenterDelims['leftAlt'] + let b:NERDCommenterDelims['right'] = b:NERDCommenterDelims['rightAlt'] + + "set the previously current delimiters to be the new alternative ones + let b:NERDCommenterDelims['leftAlt'] = tempLeft + let b:NERDCommenterDelims['rightAlt'] = tempRight + + "tell the user what comment delimiters they are now using + if a:printMsgs + call s:NerdEcho("Now using " . s:Left() . " " . s:Right() . " to delimit comments", 1) + endif + + return 1 +endfunction + +" Section: Comment delimiter add/removal functions {{{1 +" ============================================================================ +" Function: s:AppendCommentToLine(){{{2 +" This function appends comment delimiters at the EOL and places the cursor in +" position to start typing the comment +function s:AppendCommentToLine() + let left = s:Left({'space': 1}) + let right = s:Right({'space': 1}) + + " get the len of the right delim + let lenRight = strlen(right) + + let isLineEmpty = strlen(getline(".")) == 0 + let insOrApp = (isLineEmpty==1 ? 'i' : 'A') + + "stick the delimiters down at the end of the line. We have to format the + "comment with spaces as appropriate + execute ":normal! " . insOrApp . (isLineEmpty ? '' : ' ') . left . right . " " + + " if there is a right delimiter then we gotta move the cursor left + " by the len of the right delimiter so we insert between the delimiters + if lenRight > 0 + let leftMoveAmount = lenRight + execute ":normal! " . leftMoveAmount . "h" + endif + startinsert +endfunction + +" Function: s:CommentBlock(top, bottom, lSide, rSide, forceNested ) {{{2 +" This function is used to comment out a region of code. This region is +" specified as a bounding box by arguments to the function. +" +" Args: +" -top: the line number for the top line of code in the region +" -bottom: the line number for the bottom line of code in the region +" -lSide: the column number for the left most column in the region +" -rSide: the column number for the right most column in the region +" -forceNested: a flag indicating whether comments should be nested +function s:CommentBlock(top, bottom, lSide, rSide, forceNested ) + " we need to create local copies of these arguments so we can modify them + let top = a:top + let bottom = a:bottom + let lSide = a:lSide + let rSide = a:rSide + + "if the top or bottom line starts with tabs we have to adjust the left and + "right boundaries so that they are set as though the tabs were spaces + let topline = getline(top) + let bottomline = getline(bottom) + if s:HasLeadingTabs(topline, bottomline) + + "find out how many tabs are in the top line and adjust the left + "boundary accordingly + let numTabs = s:NumberOfLeadingTabs(topline) + if lSide < numTabs + let lSide = &ts * lSide + else + let lSide = (lSide - numTabs) + (&ts * numTabs) + endif + + "find out how many tabs are in the bottom line and adjust the right + "boundary accordingly + let numTabs = s:NumberOfLeadingTabs(bottomline) + let rSide = (rSide - numTabs) + (&ts * numTabs) + endif + + "we must check that bottom IS actually below top, if it is not then we + "swap top and bottom. Similarly for left and right. + if bottom < top + let temp = top + let top = bottom + let bottom = top + endif + if rSide < lSide + let temp = lSide + let lSide = rSide + let rSide = temp + endif + + "if the current delimiters arent multipart then we will switch to the + "alternative delims (if THEY are) as the comment will be better and more + "accurate with multipart delims + let switchedDelims = 0 + if !s:Multipart() && g:NERDAllowAnyVisualDelims && s:AltMultipart() + let switchedDelims = 1 + call s:SwitchToAlternativeDelimiters(0) + endif + + "start the commenting from the top and keep commenting till we reach the + "bottom + let currentLine=top + while currentLine <= bottom + + "check if we are allowed to comment this line + if s:CanCommentLine(a:forceNested, currentLine) + + "convert the leading tabs into spaces + let theLine = getline(currentLine) + let lineHasLeadTabs = s:HasLeadingTabs(theLine) + if lineHasLeadTabs + let theLine = s:ConvertLeadingTabsToSpaces(theLine) + endif + + "dont comment lines that begin after the right boundary of the + "block unless the user has specified to do so + if theLine !~ '^ \{' . rSide . '\}' || !g:NERDBlockComIgnoreEmpty + + "attempt to place the cursor in on the left of the boundary box, + "then check if we were successful, if not then we cant comment this + "line + call setline(currentLine, theLine) + if s:CanPlaceCursor(currentLine, lSide) + + let leftSpaced = s:Left({'space': 1}) + let rightSpaced = s:Right({'space': 1}) + + "stick the left delimiter down + let theLine = strpart(theLine, 0, lSide-1) . leftSpaced . strpart(theLine, lSide-1) + + if s:Multipart() + "stick the right delimiter down + let theLine = strpart(theLine, 0, rSide+strlen(leftSpaced)) . rightSpaced . strpart(theLine, rSide+strlen(leftSpaced)) + + let firstLeftDelim = s:FindDelimiterIndex(s:Left(), theLine) + let lastRightDelim = s:LastIndexOfDelim(s:Right(), theLine) + + if firstLeftDelim != -1 && lastRightDelim != -1 + let searchStr = strpart(theLine, 0, lastRightDelim) + let searchStr = strpart(searchStr, firstLeftDelim+strlen(s:Left())) + + "replace the outter most delims in searchStr with + "place-holders + let theLineWithPlaceHolders = s:ReplaceDelims(s:Left(), s:Right(), g:NERDLPlace, g:NERDRPlace, searchStr) + + "add the right delimiter onto the line + let theLine = strpart(theLine, 0, firstLeftDelim+strlen(s:Left())) . theLineWithPlaceHolders . strpart(theLine, lastRightDelim) + endif + endif + endif + endif + + "restore tabs if needed + if lineHasLeadTabs + let theLine = s:ConvertLeadingSpacesToTabs(theLine) + endif + + call setline(currentLine, theLine) + endif + + let currentLine = currentLine + 1 + endwhile + + "if we switched delims then we gotta go back to what they were before + if switchedDelims == 1 + call s:SwitchToAlternativeDelimiters(0) + endif +endfunction + +" Function: s:CommentLines(forceNested, alignLeft, alignRight, firstLine, lastLine) {{{2 +" This function comments a range of lines. +" +" Args: +" -forceNested: a flag indicating whether the called is requesting the comment +" to be nested if need be +" -align: should be "left" or "both" or "none" +" -firstLine/lastLine: the top and bottom lines to comment +function s:CommentLines(forceNested, align, firstLine, lastLine) + " we need to get the left and right indexes of the leftmost char in the + " block of of lines and the right most char so that we can do alignment of + " the delimiters if the user has specified + let leftAlignIndx = s:LeftMostIndx(a:forceNested, 0, a:firstLine, a:lastLine) + let rightAlignIndx = s:RightMostIndx(a:forceNested, 0, a:firstLine, a:lastLine) + + " gotta add the length of the left delimiter onto the rightAlignIndx cos + " we'll be adding a left delim to the line + let rightAlignIndx = rightAlignIndx + strlen(s:Left({'space': 1})) + + " now we actually comment the lines. Do it line by line + let currentLine = a:firstLine + while currentLine <= a:lastLine + + " get the next line, check commentability and convert spaces to tabs + let theLine = getline(currentLine) + let lineHasLeadingTabs = s:HasLeadingTabs(theLine) + let theLine = s:ConvertLeadingTabsToSpaces(theLine) + if s:CanCommentLine(a:forceNested, currentLine) + "if the user has specified forceNesting then we check to see if we + "need to switch delimiters for place-holders + if a:forceNested && g:NERDUsePlaceHolders + let theLine = s:SwapOutterMultiPartDelimsForPlaceHolders(theLine) + endif + + " find out if the line is commented using normal delims and/or + " alternate ones + let isCommented = s:IsCommented(s:Left(), s:Right(), theLine) || s:IsCommented(s:Left({'alt': 1}), s:Right({'alt': 1}), theLine) + + " check if we can comment this line + if !isCommented || g:NERDUsePlaceHolders || s:Multipart() + if a:align == "left" || a:align == "both" + let theLine = s:AddLeftDelimAligned(s:Left({'space': 1}), theLine, leftAlignIndx) + else + let theLine = s:AddLeftDelim(s:Left({'space': 1}), theLine) + endif + if a:align == "both" + let theLine = s:AddRightDelimAligned(s:Right({'space': 1}), theLine, rightAlignIndx) + else + let theLine = s:AddRightDelim(s:Right({'space': 1}), theLine) + endif + endif + endif + + " restore leading tabs if appropriate + if lineHasLeadingTabs + let theLine = s:ConvertLeadingSpacesToTabs(theLine) + endif + + " we are done with this line + call setline(currentLine, theLine) + let currentLine = currentLine + 1 + endwhile + +endfunction + +" Function: s:CommentLinesMinimal(firstLine, lastLine) {{{2 +" This function comments a range of lines in a minimal style. I +" +" Args: +" -firstLine/lastLine: the top and bottom lines to comment +function s:CommentLinesMinimal(firstLine, lastLine) + "check that minimal comments can be done on this filetype + if !s:HasMultipartDelims() + throw 'NERDCommenter.Delimiters exception: Minimal comments can only be used for filetypes that have multipart delimiters' + endif + + "if we need to use place holders for the comment, make sure they are + "enabled for this filetype + if !g:NERDUsePlaceHolders && s:DoesBlockHaveMultipartDelim(a:firstLine, a:lastLine) + throw 'NERDCommenter.Settings exception: Place holders are required but disabled.' + endif + + "get the left and right delims to smack on + let left = s:GetSexyComLeft(g:NERDSpaceDelims,0) + let right = s:GetSexyComRight(g:NERDSpaceDelims,0) + + "make sure all multipart delims on the lines are replaced with + "placeholders to prevent illegal syntax + let currentLine = a:firstLine + while(currentLine <= a:lastLine) + let theLine = getline(currentLine) + let theLine = s:ReplaceDelims(left, right, g:NERDLPlace, g:NERDRPlace, theLine) + call setline(currentLine, theLine) + let currentLine = currentLine + 1 + endwhile + + "add the delim to the top line + let theLine = getline(a:firstLine) + let lineHasLeadingTabs = s:HasLeadingTabs(theLine) + let theLine = s:ConvertLeadingTabsToSpaces(theLine) + let theLine = s:AddLeftDelim(left, theLine) + if lineHasLeadingTabs + let theLine = s:ConvertLeadingSpacesToTabs(theLine) + endif + call setline(a:firstLine, theLine) + + "add the delim to the bottom line + let theLine = getline(a:lastLine) + let lineHasLeadingTabs = s:HasLeadingTabs(theLine) + let theLine = s:ConvertLeadingTabsToSpaces(theLine) + let theLine = s:AddRightDelim(right, theLine) + if lineHasLeadingTabs + let theLine = s:ConvertLeadingSpacesToTabs(theLine) + endif + call setline(a:lastLine, theLine) +endfunction + +" Function: s:CommentLinesSexy(topline, bottomline) function {{{2 +" This function is used to comment lines in the 'Sexy' style. eg in c: +" /* +" * This is a sexy comment +" */ +" Args: +" -topline: the line num of the top line in the sexy comment +" -bottomline: the line num of the bottom line in the sexy comment +function s:CommentLinesSexy(topline, bottomline) + let left = s:GetSexyComLeft(0, 0) + let right = s:GetSexyComRight(0, 0) + + "check if we can do a sexy comment with the available delimiters + if left == -1 || right == -1 + throw 'NERDCommenter.Delimiters exception: cannot perform sexy comments with available delimiters.' + endif + + "make sure the lines arent already commented sexually + if !s:CanSexyCommentLines(a:topline, a:bottomline) + throw 'NERDCommenter.Nesting exception: cannot nest sexy comments' + endif + + + let sexyComMarker = s:GetSexyComMarker(0,0) + let sexyComMarkerSpaced = s:GetSexyComMarker(1,0) + + + " we jam the comment as far to the right as possible + let leftAlignIndx = s:LeftMostIndx(1, 1, a:topline, a:bottomline) + + "check if we should use the compact style i.e that the left/right + "delimiters should appear on the first and last lines of the code and not + "on separate lines above/below the first/last lines of code + if g:NERDCompactSexyComs + let spaceString = (g:NERDSpaceDelims ? s:spaceStr : '') + + "comment the top line + let theLine = getline(a:topline) + let lineHasTabs = s:HasLeadingTabs(theLine) + if lineHasTabs + let theLine = s:ConvertLeadingTabsToSpaces(theLine) + endif + let theLine = s:SwapOutterMultiPartDelimsForPlaceHolders(theLine) + let theLine = s:AddLeftDelimAligned(left . spaceString, theLine, leftAlignIndx) + if lineHasTabs + let theLine = s:ConvertLeadingSpacesToTabs(theLine) + endif + call setline(a:topline, theLine) + + "comment the bottom line + if a:bottomline != a:topline + let theLine = getline(a:bottomline) + let lineHasTabs = s:HasLeadingTabs(theLine) + if lineHasTabs + let theLine = s:ConvertLeadingTabsToSpaces(theLine) + endif + let theLine = s:SwapOutterMultiPartDelimsForPlaceHolders(theLine) + endif + let theLine = s:AddRightDelim(spaceString . right, theLine) + if lineHasTabs + let theLine = s:ConvertLeadingSpacesToTabs(theLine) + endif + call setline(a:bottomline, theLine) + else + + " add the left delimiter one line above the lines that are to be commented + call cursor(a:topline, 1) + execute 'normal! O' + let theLine = repeat(' ', leftAlignIndx) . left + + " Make sure tabs are respected + if !&expandtab + let theLine = s:ConvertLeadingSpacesToTabs(theLine) + endif + call setline(a:topline, theLine) + + " add the right delimiter after bottom line (we have to add 1 cos we moved + " the lines down when we added the left delim + call cursor(a:bottomline+1, 1) + execute 'normal! o' + let theLine = repeat(' ', leftAlignIndx) . repeat(' ', strlen(left)-strlen(sexyComMarker)) . right + + " Make sure tabs are respected + if !&expandtab + let theLine = s:ConvertLeadingSpacesToTabs(theLine) + endif + call setline(a:bottomline+2, theLine) + + endif + + " go thru each line adding the sexyComMarker marker to the start of each + " line in the appropriate place to align them with the comment delims + let currentLine = a:topline+1 + while currentLine <= a:bottomline + !g:NERDCompactSexyComs + " get the line and convert the tabs to spaces + let theLine = getline(currentLine) + let lineHasTabs = s:HasLeadingTabs(theLine) + if lineHasTabs + let theLine = s:ConvertLeadingTabsToSpaces(theLine) + endif + + let theLine = s:SwapOutterMultiPartDelimsForPlaceHolders(theLine) + + " add the sexyComMarker + let theLine = repeat(' ', leftAlignIndx) . repeat(' ', strlen(left)-strlen(sexyComMarker)) . sexyComMarkerSpaced . strpart(theLine, leftAlignIndx) + + if lineHasTabs + let theLine = s:ConvertLeadingSpacesToTabs(theLine) + endif + + + " set the line and move onto the next one + call setline(currentLine, theLine) + let currentLine = currentLine + 1 + endwhile + +endfunction + +" Function: s:CommentLinesToggle(forceNested, firstLine, lastLine) {{{2 +" Applies "toggle" commenting to the given range of lines +" +" Args: +" -forceNested: a flag indicating whether the called is requesting the comment +" to be nested if need be +" -firstLine/lastLine: the top and bottom lines to comment +function s:CommentLinesToggle(forceNested, firstLine, lastLine) + let currentLine = a:firstLine + while currentLine <= a:lastLine + + " get the next line, check commentability and convert spaces to tabs + let theLine = getline(currentLine) + let lineHasLeadingTabs = s:HasLeadingTabs(theLine) + let theLine = s:ConvertLeadingTabsToSpaces(theLine) + if s:CanToggleCommentLine(a:forceNested, currentLine) + + "if the user has specified forceNesting then we check to see if we + "need to switch delimiters for place-holders + if g:NERDUsePlaceHolders + let theLine = s:SwapOutterMultiPartDelimsForPlaceHolders(theLine) + endif + + let theLine = s:AddLeftDelim(s:Left({'space': 1}), theLine) + let theLine = s:AddRightDelim(s:Right({'space': 1}), theLine) + endif + + " restore leading tabs if appropriate + if lineHasLeadingTabs + let theLine = s:ConvertLeadingSpacesToTabs(theLine) + endif + + " we are done with this line + call setline(currentLine, theLine) + let currentLine = currentLine + 1 + endwhile + +endfunction + +" Function: s:CommentRegion(topline, topCol, bottomLine, bottomCol) function {{{2 +" This function comments chunks of text selected in visual mode. +" It will comment exactly the text that they have selected. +" Args: +" -topLine: the line num of the top line in the sexy comment +" -topCol: top left col for this comment +" -bottomline: the line num of the bottom line in the sexy comment +" -bottomCol: the bottom right col for this comment +" -forceNested: whether the caller wants comments to be nested if the +" line(s) are already commented +function s:CommentRegion(topLine, topCol, bottomLine, bottomCol, forceNested) + + "switch delims (if we can) if the current set isnt multipart + let switchedDelims = 0 + if !s:Multipart() && s:AltMultipart() && !g:NERDAllowAnyVisualDelims + let switchedDelims = 1 + call s:SwitchToAlternativeDelimiters(0) + endif + + "if there is only one line in the comment then just do it + if a:topLine == a:bottomLine + call s:CommentBlock(a:topLine, a:bottomLine, a:topCol, a:bottomCol, a:forceNested) + + "there are multiple lines in the comment + else + "comment the top line + call s:CommentBlock(a:topLine, a:topLine, a:topCol, strlen(getline(a:topLine)), a:forceNested) + + "comment out all the lines in the middle of the comment + let topOfRange = a:topLine+1 + let bottomOfRange = a:bottomLine-1 + if topOfRange <= bottomOfRange + call s:CommentLines(a:forceNested, "none", topOfRange, bottomOfRange) + endif + + "comment the bottom line + let bottom = getline(a:bottomLine) + let numLeadingSpacesTabs = strlen(substitute(bottom, '^\([ \t]*\).*$', '\1', '')) + call s:CommentBlock(a:bottomLine, a:bottomLine, numLeadingSpacesTabs+1, a:bottomCol, a:forceNested) + + endif + + "stick the cursor back on the char it was on before the comment + call cursor(a:topLine, a:topCol + strlen(s:Left()) + g:NERDSpaceDelims) + + "if we switched delims then we gotta go back to what they were before + if switchedDelims == 1 + call s:SwitchToAlternativeDelimiters(0) + endif + +endfunction + +" Function: s:InvertComment(firstLine, lastLine) function {{{2 +" Inverts the comments on the lines between and including the given line +" numbers i.e all commented lines are uncommented and vice versa +" Args: +" -firstLine: the top of the range of lines to be inverted +" -lastLine: the bottom of the range of lines to be inverted +function s:InvertComment(firstLine, lastLine) + + " go thru all lines in the given range + let currentLine = a:firstLine + while currentLine <= a:lastLine + let theLine = getline(currentLine) + + let sexyComBounds = s:FindBoundingLinesOfSexyCom(currentLine) + + " if the line is commented normally, uncomment it + if s:IsCommentedFromStartOfLine(s:Left(), theLine) || s:IsCommentedFromStartOfLine(s:Left({'alt': 1}), theLine) + call s:UncommentLines(currentLine, currentLine) + let currentLine = currentLine + 1 + + " check if the line is commented sexually + elseif !empty(sexyComBounds) + let numLinesBeforeSexyComRemoved = s:NumLinesInBuf() + call s:UncommentLinesSexy(sexyComBounds[0], sexyComBounds[1]) + + "move to the line after last line of the sexy comment + let numLinesAfterSexyComRemoved = s:NumLinesInBuf() + let currentLine = sexyComBounds[1] - (numLinesBeforeSexyComRemoved - numLinesAfterSexyComRemoved) + 1 + + " the line isnt commented + else + call s:CommentLinesToggle(1, currentLine, currentLine) + let currentLine = currentLine + 1 + endif + + endwhile +endfunction + +" Function: NERDComment(isVisual, type) function {{{2 +" This function is a Wrapper for the main commenting functions +" +" Args: +" -isVisual: a flag indicating whether the comment is requested in visual +" mode or not +" -type: the type of commenting requested. Can be 'sexy', 'invert', +" 'minimal', 'toggle', 'alignLeft', 'alignBoth', 'norm', +" 'nested', 'toEOL', 'append', 'insert', 'uncomment', 'yank' +function! NERDComment(isVisual, type) range + " we want case sensitivity when commenting + let oldIgnoreCase = &ignorecase + set noignorecase + + if !exists("g:did_load_ftplugin") || g:did_load_ftplugin != 1 + call s:NerdEcho("filetype plugins should be enabled. See :help NERDComInstallation and :help :filetype-plugin-on", 0) + endif + + if a:isVisual + let firstLine = line("'<") + let lastLine = line("'>") + let firstCol = col("'<") + let lastCol = col("'>") - (&selection == 'exclusive' ? 1 : 0) + else + let firstLine = a:firstline + let lastLine = a:lastline + endif + + let countWasGiven = (a:isVisual == 0 && firstLine != lastLine) + + let forceNested = (a:type == 'nested' || g:NERDDefaultNesting) + + if a:type == 'norm' || a:type == 'nested' + if a:isVisual && visualmode() == "" + call s:CommentBlock(firstLine, lastLine, firstCol, lastCol, forceNested) + elseif a:isVisual && visualmode() == "v" && (g:NERDCommentWholeLinesInVMode==0 || (g:NERDCommentWholeLinesInVMode==2 && s:HasMultipartDelims())) + call s:CommentRegion(firstLine, firstCol, lastLine, lastCol, forceNested) + else + call s:CommentLines(forceNested, "none", firstLine, lastLine) + endif + + elseif a:type == 'alignLeft' || a:type == 'alignBoth' + let align = "none" + if a:type == "alignLeft" + let align = "left" + elseif a:type == "alignBoth" + let align = "both" + endif + call s:CommentLines(forceNested, align, firstLine, lastLine) + + elseif a:type == 'invert' + call s:InvertComment(firstLine, lastLine) + + elseif a:type == 'sexy' + try + call s:CommentLinesSexy(firstLine, lastLine) + catch /NERDCommenter.Delimiters/ + call s:CommentLines(forceNested, "none", firstLine, lastLine) + catch /NERDCommenter.Nesting/ + call s:NerdEcho("Sexy comment aborted. Nested sexy cannot be nested", 0) + endtry + + elseif a:type == 'toggle' + let theLine = getline(firstLine) + + if s:IsInSexyComment(firstLine) || s:IsCommentedFromStartOfLine(s:Left(), theLine) || s:IsCommentedFromStartOfLine(s:Left({'alt': 1}), theLine) + call s:UncommentLines(firstLine, lastLine) + else + call s:CommentLinesToggle(forceNested, firstLine, lastLine) + endif + + elseif a:type == 'minimal' + try + call s:CommentLinesMinimal(firstLine, lastLine) + catch /NERDCommenter.Delimiters/ + call s:NerdEcho("Minimal comments can only be used for filetypes that have multipart delimiters.", 0) + catch /NERDCommenter.Settings/ + call s:NerdEcho("Place holders are required but disabled.", 0) + endtry + + elseif a:type == 'toEOL' + call s:SaveScreenState() + call s:CommentBlock(firstLine, firstLine, col("."), col("$")-1, 1) + call s:RestoreScreenState() + + elseif a:type == 'append' + call s:AppendCommentToLine() + + elseif a:type == 'insert' + call s:PlaceDelimitersAndInsBetween() + + elseif a:type == 'uncomment' + call s:UncommentLines(firstLine, lastLine) + + elseif a:type == 'yank' + if a:isVisual + normal! gvy + elseif countWasGiven + execute firstLine .','. lastLine .'yank' + else + normal! yy + endif + execute firstLine .','. lastLine .'call NERDComment('. a:isVisual .', "norm")' + endif + + let &ignorecase = oldIgnoreCase +endfunction + +" Function: s:PlaceDelimitersAndInsBetween() function {{{2 +" This is function is called to place comment delimiters down and place the +" cursor between them +function s:PlaceDelimitersAndInsBetween() + " get the left and right delimiters without any escape chars in them + let left = s:Left({'space': 1}) + let right = s:Right({'space': 1}) + + let theLine = getline(".") + let lineHasLeadTabs = s:HasLeadingTabs(theLine) || (theLine =~ '^ *$' && !&expandtab) + + "convert tabs to spaces and adjust the cursors column to take this into + "account + let untabbedCol = s:UntabbedCol(theLine, col(".")) + call setline(line("."), s:ConvertLeadingTabsToSpaces(theLine)) + call cursor(line("."), untabbedCol) + + " get the len of the right delim + let lenRight = strlen(right) + + let isDelimOnEOL = col(".") >= strlen(getline(".")) + + " if the cursor is in the first col then we gotta insert rather than + " append the comment delimiters here + let insOrApp = (col(".")==1 ? 'i' : 'a') + + " place the delimiters down. We do it differently depending on whether + " there is a left AND right delimiter + if lenRight > 0 + execute ":normal! " . insOrApp . left . right + execute ":normal! " . lenRight . "h" + else + execute ":normal! " . insOrApp . left + + " if we are tacking the delim on the EOL then we gotta add a space + " after it cos when we go out of insert mode the cursor will move back + " one and the user wont be in position to type the comment. + if isDelimOnEOL + execute 'normal! a ' + endif + endif + normal! l + + "if needed convert spaces back to tabs and adjust the cursors col + "accordingly + if lineHasLeadTabs + let tabbedCol = s:TabbedCol(getline("."), col(".")) + call setline(line("."), s:ConvertLeadingSpacesToTabs(getline("."))) + call cursor(line("."), tabbedCol) + endif + + startinsert +endfunction + +" Function: s:RemoveDelimiters(left, right, line) {{{2 +" this function is called to remove the first left comment delimiter and the +" last right delimiter of the given line. +" +" The args left and right must be strings. If there is no right delimiter (as +" is the case for e.g vim file comments) them the arg right should be "" +" +" Args: +" -left: the left comment delimiter +" -right: the right comment delimiter +" -line: the line to remove the delimiters from +function s:RemoveDelimiters(left, right, line) + + let l:left = a:left + let l:right = a:right + let lenLeft = strlen(left) + let lenRight = strlen(right) + + let delimsSpaced = (g:NERDSpaceDelims || g:NERDRemoveExtraSpaces) + + let line = a:line + + "look for the left delimiter, if we find it, remove it. + let leftIndx = s:FindDelimiterIndex(a:left, line) + if leftIndx != -1 + let line = strpart(line, 0, leftIndx) . strpart(line, leftIndx+lenLeft) + + "if the user has specified that there is a space after the left delim + "then check for the space and remove it if it is there + if delimsSpaced && strpart(line, leftIndx, s:lenSpaceStr) == s:spaceStr + let line = strpart(line, 0, leftIndx) . strpart(line, leftIndx+s:lenSpaceStr) + endif + endif + + "look for the right delimiter, if we find it, remove it + let rightIndx = s:FindDelimiterIndex(a:right, line) + if rightIndx != -1 + let line = strpart(line, 0, rightIndx) . strpart(line, rightIndx+lenRight) + + "if the user has specified that there is a space before the right delim + "then check for the space and remove it if it is there + if delimsSpaced && strpart(line, rightIndx-s:lenSpaceStr, s:lenSpaceStr) == s:spaceStr && s:Multipart() + let line = strpart(line, 0, rightIndx-s:lenSpaceStr) . strpart(line, rightIndx) + endif + endif + + return line +endfunction + +" Function: s:UncommentLines(topLine, bottomLine) {{{2 +" This function uncomments the given lines +" +" Args: +" topLine: the top line of the visual selection to uncomment +" bottomLine: the bottom line of the visual selection to uncomment +function s:UncommentLines(topLine, bottomLine) + "make local copies of a:firstline and a:lastline and, if need be, swap + "them around if the top line is below the bottom + let l:firstline = a:topLine + let l:lastline = a:bottomLine + if firstline > lastline + let firstline = lastline + let lastline = a:topLine + endif + + "go thru each line uncommenting each line removing sexy comments + let currentLine = firstline + while currentLine <= lastline + + "check the current line to see if it is part of a sexy comment + let sexyComBounds = s:FindBoundingLinesOfSexyCom(currentLine) + if !empty(sexyComBounds) + + "we need to store the num lines in the buf before the comment is + "removed so we know how many lines were removed when the sexy com + "was removed + let numLinesBeforeSexyComRemoved = s:NumLinesInBuf() + + call s:UncommentLinesSexy(sexyComBounds[0], sexyComBounds[1]) + + "move to the line after last line of the sexy comment + let numLinesAfterSexyComRemoved = s:NumLinesInBuf() + let numLinesRemoved = numLinesBeforeSexyComRemoved - numLinesAfterSexyComRemoved + let currentLine = sexyComBounds[1] - numLinesRemoved + 1 + let lastline = lastline - numLinesRemoved + + "no sexy com was detected so uncomment the line as normal + else + call s:UncommentLinesNormal(currentLine, currentLine) + let currentLine = currentLine + 1 + endif + endwhile + +endfunction + +" Function: s:UncommentLinesSexy(topline, bottomline) {{{2 +" This function removes all the comment characters associated with the sexy +" comment spanning the given lines +" Args: +" -topline/bottomline: the top/bottom lines of the sexy comment +function s:UncommentLinesSexy(topline, bottomline) + let left = s:GetSexyComLeft(0,1) + let right = s:GetSexyComRight(0,1) + + + "check if it is even possible for sexy comments to exist with the + "available delimiters + if left == -1 || right == -1 + throw 'NERDCommenter.Delimiters exception: cannot uncomment sexy comments with available delimiters.' + endif + + let leftUnEsc = s:GetSexyComLeft(0,0) + let rightUnEsc = s:GetSexyComRight(0,0) + + let sexyComMarker = s:GetSexyComMarker(0, 1) + let sexyComMarkerUnEsc = s:GetSexyComMarker(0, 0) + + "the markerOffset is how far right we need to move the sexyComMarker to + "line it up with the end of the left delim + let markerOffset = strlen(leftUnEsc)-strlen(sexyComMarkerUnEsc) + + " go thru the intermediate lines of the sexy comment and remove the + " sexy comment markers (eg the '*'s on the start of line in a c sexy + " comment) + let currentLine = a:topline+1 + while currentLine < a:bottomline + let theLine = getline(currentLine) + + " remove the sexy comment marker from the line. We also remove the + " space after it if there is one and if appropriate options are set + let sexyComMarkerIndx = stridx(theLine, sexyComMarkerUnEsc) + if strpart(theLine, sexyComMarkerIndx+strlen(sexyComMarkerUnEsc), s:lenSpaceStr) == s:spaceStr && g:NERDSpaceDelims + let theLine = strpart(theLine, 0, sexyComMarkerIndx - markerOffset) . strpart(theLine, sexyComMarkerIndx+strlen(sexyComMarkerUnEsc)+s:lenSpaceStr) + else + let theLine = strpart(theLine, 0, sexyComMarkerIndx - markerOffset) . strpart(theLine, sexyComMarkerIndx+strlen(sexyComMarkerUnEsc)) + endif + + let theLine = s:SwapOutterPlaceHoldersForMultiPartDelims(theLine) + + let theLine = s:ConvertLeadingWhiteSpace(theLine) + + " move onto the next line + call setline(currentLine, theLine) + let currentLine = currentLine + 1 + endwhile + + " gotta make a copy of a:bottomline cos we modify the position of the + " last line it if we remove the topline + let bottomline = a:bottomline + + " get the first line so we can remove the left delim from it + let theLine = getline(a:topline) + + " if the first line contains only the left delim then just delete it + if theLine =~ '^[ \t]*' . left . '[ \t]*$' && !g:NERDCompactSexyComs + call cursor(a:topline, 1) + normal! dd + let bottomline = bottomline - 1 + + " topline contains more than just the left delim + else + + " remove the delim. If there is a space after it + " then remove this too if appropriate + let delimIndx = stridx(theLine, leftUnEsc) + if strpart(theLine, delimIndx+strlen(leftUnEsc), s:lenSpaceStr) == s:spaceStr && g:NERDSpaceDelims + let theLine = strpart(theLine, 0, delimIndx) . strpart(theLine, delimIndx+strlen(leftUnEsc)+s:lenSpaceStr) + else + let theLine = strpart(theLine, 0, delimIndx) . strpart(theLine, delimIndx+strlen(leftUnEsc)) + endif + let theLine = s:SwapOutterPlaceHoldersForMultiPartDelims(theLine) + call setline(a:topline, theLine) + endif + + " get the last line so we can remove the right delim + let theLine = getline(bottomline) + + " if the bottomline contains only the right delim then just delete it + if theLine =~ '^[ \t]*' . right . '[ \t]*$' + call cursor(bottomline, 1) + normal! dd + + " the last line contains more than the right delim + else + " remove the right delim. If there is a space after it and + " if the appropriate options are set then remove this too. + let delimIndx = s:LastIndexOfDelim(rightUnEsc, theLine) + if strpart(theLine, delimIndx+strlen(leftUnEsc), s:lenSpaceStr) == s:spaceStr && g:NERDSpaceDelims + let theLine = strpart(theLine, 0, delimIndx) . strpart(theLine, delimIndx+strlen(rightUnEsc)+s:lenSpaceStr) + else + let theLine = strpart(theLine, 0, delimIndx) . strpart(theLine, delimIndx+strlen(rightUnEsc)) + endif + + " if the last line also starts with a sexy comment marker then we + " remove this as well + if theLine =~ '^[ \t]*' . sexyComMarker + + " remove the sexyComMarker. If there is a space after it then + " remove that too + let sexyComMarkerIndx = stridx(theLine, sexyComMarkerUnEsc) + if strpart(theLine, sexyComMarkerIndx+strlen(sexyComMarkerUnEsc), s:lenSpaceStr) == s:spaceStr && g:NERDSpaceDelims + let theLine = strpart(theLine, 0, sexyComMarkerIndx - markerOffset ) . strpart(theLine, sexyComMarkerIndx+strlen(sexyComMarkerUnEsc)+s:lenSpaceStr) + else + let theLine = strpart(theLine, 0, sexyComMarkerIndx - markerOffset ) . strpart(theLine, sexyComMarkerIndx+strlen(sexyComMarkerUnEsc)) + endif + endif + + let theLine = s:SwapOutterPlaceHoldersForMultiPartDelims(theLine) + call setline(bottomline, theLine) + endif +endfunction + +" Function: s:UncommentLineNormal(line) {{{2 +" uncomments the given line and returns the result +" Args: +" -line: the line to uncomment +function s:UncommentLineNormal(line) + let line = a:line + + "get the comment status on the line so we know how it is commented + let lineCommentStatus = s:IsCommentedOuttermost(s:Left(), s:Right(), s:Left({'alt': 1}), s:Right({'alt': 1}), line) + + "it is commented with s:Left() and s:Right() so remove these delims + if lineCommentStatus == 1 + let line = s:RemoveDelimiters(s:Left(), s:Right(), line) + + "it is commented with s:Left({'alt': 1}) and s:Right({'alt': 1}) so remove these delims + elseif lineCommentStatus == 2 && g:NERDRemoveAltComs + let line = s:RemoveDelimiters(s:Left({'alt': 1}), s:Right({'alt': 1}), line) + + "it is not properly commented with any delims so we check if it has + "any random left or right delims on it and remove the outtermost ones + else + "get the positions of all delim types on the line + let indxLeft = s:FindDelimiterIndex(s:Left(), line) + let indxLeftAlt = s:FindDelimiterIndex(s:Left({'alt': 1}), line) + let indxRight = s:FindDelimiterIndex(s:Right(), line) + let indxRightAlt = s:FindDelimiterIndex(s:Right({'alt': 1}), line) + + "remove the outter most left comment delim + if indxLeft != -1 && (indxLeft < indxLeftAlt || indxLeftAlt == -1) + let line = s:RemoveDelimiters(s:Left(), '', line) + elseif indxLeftAlt != -1 + let line = s:RemoveDelimiters(s:Left({'alt': 1}), '', line) + endif + + "remove the outter most right comment delim + if indxRight != -1 && (indxRight < indxRightAlt || indxRightAlt == -1) + let line = s:RemoveDelimiters('', s:Right(), line) + elseif indxRightAlt != -1 + let line = s:RemoveDelimiters('', s:Right({'alt': 1}), line) + endif + endif + + + let indxLeft = s:FindDelimiterIndex(s:Left(), line) + let indxLeftAlt = s:FindDelimiterIndex(s:Left({'alt': 1}), line) + let indxLeftPlace = s:FindDelimiterIndex(g:NERDLPlace, line) + + let indxRightPlace = s:FindDelimiterIndex(g:NERDRPlace, line) + let indxRightAlt = s:FindDelimiterIndex(s:Right({'alt': 1}), line) + let indxRightPlace = s:FindDelimiterIndex(g:NERDRPlace, line) + + let right = s:Right() + let left = s:Left() + if !s:Multipart() + let right = s:Right({'alt': 1}) + let left = s:Left({'alt': 1}) + endif + + + "if there are place-holders on the line then we check to see if they are + "the outtermost delimiters on the line. If so then we replace them with + "real delimiters + if indxLeftPlace != -1 + if (indxLeftPlace < indxLeft || indxLeft==-1) && (indxLeftPlace < indxLeftAlt || indxLeftAlt==-1) + let line = s:ReplaceDelims(g:NERDLPlace, g:NERDRPlace, left, right, line) + endif + elseif indxRightPlace != -1 + if (indxRightPlace < indxLeft || indxLeft==-1) && (indxLeftPlace < indxLeftAlt || indxLeftAlt==-1) + let line = s:ReplaceDelims(g:NERDLPlace, g:NERDRPlace, left, right, line) + endif + + endif + + let line = s:ConvertLeadingWhiteSpace(line) + + return line +endfunction + +" Function: s:UncommentLinesNormal(topline, bottomline) {{{2 +" This function is called to uncomment lines that arent a sexy comment +" Args: +" -topline/bottomline: the top/bottom line numbers of the comment +function s:UncommentLinesNormal(topline, bottomline) + let currentLine = a:topline + while currentLine <= a:bottomline + let line = getline(currentLine) + call setline(currentLine, s:UncommentLineNormal(line)) + let currentLine = currentLine + 1 + endwhile +endfunction + + +" Section: Other helper functions {{{1 +" ============================================================================ + +" Function: s:AddLeftDelim(delim, theLine) {{{2 +" Args: +function s:AddLeftDelim(delim, theLine) + return substitute(a:theLine, '^\([ \t]*\)', '\1' . a:delim, '') +endfunction + +" Function: s:AddLeftDelimAligned(delim, theLine) {{{2 +" Args: +function s:AddLeftDelimAligned(delim, theLine, alignIndx) + + "if the line is not long enough then bung some extra spaces on the front + "so we can align the delim properly + let theLine = a:theLine + if strlen(theLine) < a:alignIndx + let theLine = repeat(' ', a:alignIndx - strlen(theLine)) + endif + + return strpart(theLine, 0, a:alignIndx) . a:delim . strpart(theLine, a:alignIndx) +endfunction + +" Function: s:AddRightDelim(delim, theLine) {{{2 +" Args: +function s:AddRightDelim(delim, theLine) + if a:delim == '' + return a:theLine + else + return substitute(a:theLine, '$', a:delim, '') + endif +endfunction + +" Function: s:AddRightDelimAligned(delim, theLine, alignIndx) {{{2 +" Args: +function s:AddRightDelimAligned(delim, theLine, alignIndx) + if a:delim == "" + return a:theLine + else + + " when we align the right delim we are just adding spaces + " so we get a string containing the needed spaces (it + " could be empty) + let extraSpaces = '' + let extraSpaces = repeat(' ', a:alignIndx-strlen(a:theLine)) + + " add the right delim + return substitute(a:theLine, '$', extraSpaces . a:delim, '') + endif +endfunction + +" Function: s:AltMultipart() {{{2 +" returns 1 if the alternative delims are multipart +function s:AltMultipart() + return b:NERDCommenterDelims['rightAlt'] != '' +endfunction + +" Function: s:CanCommentLine(forceNested, line) {{{2 +"This function is used to determine whether the given line can be commented. +"It returns 1 if it can be and 0 otherwise +" +" Args: +" -forceNested: a flag indicating whether the caller wants comments to be nested +" if the current line is already commented +" -lineNum: the line num of the line to check for commentability +function s:CanCommentLine(forceNested, lineNum) + let theLine = getline(a:lineNum) + + " make sure we don't comment lines that are just spaces or tabs or empty. + if theLine =~ "^[ \t]*$" + return 0 + endif + + "if the line is part of a sexy comment then just flag it... + if s:IsInSexyComment(a:lineNum) + return 0 + endif + + let isCommented = s:IsCommentedNormOrSexy(a:lineNum) + + "if the line isnt commented return true + if !isCommented + return 1 + endif + + "if the line is commented but nesting is allowed then return true + if a:forceNested && (!s:Multipart() || g:NERDUsePlaceHolders) + return 1 + endif + + return 0 +endfunction + +" Function: s:CanPlaceCursor(line, col) {{{2 +" returns 1 if the cursor can be placed exactly in the given position +function s:CanPlaceCursor(line, col) + let c = col(".") + let l = line(".") + call cursor(a:line, a:col) + let success = (line(".") == a:line && col(".") == a:col) + call cursor(l,c) + return success +endfunction + +" Function: s:CanSexyCommentLines(topline, bottomline) {{{2 +" Return: 1 if the given lines can be commented sexually, 0 otherwise +function s:CanSexyCommentLines(topline, bottomline) + " see if the selected regions have any sexy comments + let currentLine = a:topline + while(currentLine <= a:bottomline) + if s:IsInSexyComment(currentLine) + return 0 + endif + let currentLine = currentLine + 1 + endwhile + return 1 +endfunction +" Function: s:CanToggleCommentLine(forceNested, line) {{{2 +"This function is used to determine whether the given line can be toggle commented. +"It returns 1 if it can be and 0 otherwise +" +" Args: +" -lineNum: the line num of the line to check for commentability +function s:CanToggleCommentLine(forceNested, lineNum) + let theLine = getline(a:lineNum) + if (s:IsCommentedFromStartOfLine(s:Left(), theLine) || s:IsCommentedFromStartOfLine(s:Left({'alt': 1}), theLine)) && !a:forceNested + return 0 + endif + + " make sure we don't comment lines that are just spaces or tabs or empty. + if theLine =~ "^[ \t]*$" + return 0 + endif + + "if the line is part of a sexy comment then just flag it... + if s:IsInSexyComment(a:lineNum) + return 0 + endif + + return 1 +endfunction + +" Function: s:ConvertLeadingSpacesToTabs(line) {{{2 +" This function takes a line and converts all leading tabs on that line into +" spaces +" +" Args: +" -line: the line whose leading tabs will be converted +function s:ConvertLeadingSpacesToTabs(line) + let toReturn = a:line + while toReturn =~ '^\t*' . s:TabSpace() . '\(.*\)$' + let toReturn = substitute(toReturn, '^\(\t*\)' . s:TabSpace() . '\(.*\)$' , '\1\t\2' , "") + endwhile + + return toReturn +endfunction + + +" Function: s:ConvertLeadingTabsToSpaces(line) {{{2 +" This function takes a line and converts all leading spaces on that line into +" tabs +" +" Args: +" -line: the line whose leading spaces will be converted +function s:ConvertLeadingTabsToSpaces(line) + let toReturn = a:line + while toReturn =~ '^\( *\)\t' + let toReturn = substitute(toReturn, '^\( *\)\t', '\1' . s:TabSpace() , "") + endwhile + + return toReturn +endfunction + +" Function: s:ConvertLeadingWhiteSpace(line) {{{2 +" Converts the leading white space to tabs/spaces depending on &ts +" +" Args: +" -line: the line to convert +function s:ConvertLeadingWhiteSpace(line) + let toReturn = a:line + while toReturn =~ '^ *\t' + let toReturn = substitute(toReturn, '^ *\zs\t\ze', s:TabSpace(), "g") + endwhile + + if !&expandtab + let toReturn = s:ConvertLeadingSpacesToTabs(toReturn) + endif + + return toReturn +endfunction + + +" Function: s:CountNonESCedOccurances(str, searchstr, escChar) {{{2 +" This function counts the number of substrings contained in another string. +" These substrings are only counted if they are not escaped with escChar +" Args: +" -str: the string to look for searchstr in +" -searchstr: the substring to search for in str +" -escChar: the escape character which, when preceding an instance of +" searchstr, will cause it not to be counted +function s:CountNonESCedOccurances(str, searchstr, escChar) + "get the index of the first occurrence of searchstr + let indx = stridx(a:str, a:searchstr) + + "if there is an instance of searchstr in str process it + if indx != -1 + "get the remainder of str after this instance of searchstr is removed + let lensearchstr = strlen(a:searchstr) + let strLeft = strpart(a:str, indx+lensearchstr) + + "if this instance of searchstr is not escaped, add one to the count + "and recurse. If it is escaped, just recurse + if !s:IsEscaped(a:str, indx, a:escChar) + return 1 + s:CountNonESCedOccurances(strLeft, a:searchstr, a:escChar) + else + return s:CountNonESCedOccurances(strLeft, a:searchstr, a:escChar) + endif + endif +endfunction +" Function: s:DoesBlockHaveDelim(delim, top, bottom) {{{2 +" Returns 1 if the given block of lines has a delimiter (a:delim) in it +" Args: +" -delim: the comment delimiter to check the block for +" -top: the top line number of the block +" -bottom: the bottom line number of the block +function s:DoesBlockHaveDelim(delim, top, bottom) + let currentLine = a:top + while currentLine < a:bottom + let theline = getline(currentLine) + if s:FindDelimiterIndex(a:delim, theline) != -1 + return 1 + endif + let currentLine = currentLine + 1 + endwhile + return 0 +endfunction + +" Function: s:DoesBlockHaveMultipartDelim(top, bottom) {{{2 +" Returns 1 if the given block has a >= 1 multipart delimiter in it +" Args: +" -top: the top line number of the block +" -bottom: the bottom line number of the block +function s:DoesBlockHaveMultipartDelim(top, bottom) + if s:HasMultipartDelims() + if s:Multipart() + return s:DoesBlockHaveDelim(s:Left(), a:top, a:bottom) || s:DoesBlockHaveDelim(s:Right(), a:top, a:bottom) + else + return s:DoesBlockHaveDelim(s:Left({'alt': 1}), a:top, a:bottom) || s:DoesBlockHaveDelim(s:Right({'alt': 1}), a:top, a:bottom) + endif + endif + return 0 +endfunction + + +" Function: s:Esc(str) {{{2 +" Escapes all the tricky chars in the given string +function s:Esc(str) + let charsToEsc = '*/\."&$+' + return escape(a:str, charsToEsc) +endfunction + +" Function: s:FindDelimiterIndex(delimiter, line) {{{2 +" This function is used to get the string index of the input comment delimiter +" on the input line. If no valid comment delimiter is found in the line then +" -1 is returned +" Args: +" -delimiter: the delimiter we are looking to find the index of +" -line: the line we are looking for delimiter on +function s:FindDelimiterIndex(delimiter, line) + + "make sure the delimiter isnt empty otherwise we go into an infinite loop. + if a:delimiter == "" + return -1 + endif + + + let l:delimiter = a:delimiter + let lenDel = strlen(l:delimiter) + + "get the index of the first occurrence of the delimiter + let delIndx = stridx(a:line, l:delimiter) + + "keep looping thru the line till we either find a real comment delimiter + "or run off the EOL + while delIndx != -1 + + "if we are not off the EOL get the str before the possible delimiter + "in question and check if it really is a delimiter. If it is, return + "its position + if delIndx != -1 + if s:IsDelimValid(l:delimiter, delIndx, a:line) + return delIndx + endif + endif + + "we have not yet found a real comment delimiter so move past the + "current one we are lookin at + let restOfLine = strpart(a:line, delIndx + lenDel) + let distToNextDelim = stridx(restOfLine , l:delimiter) + + "if distToNextDelim is -1 then there is no more potential delimiters + "on the line so set delIndx to -1. Otherwise, move along the line by + "distToNextDelim + if distToNextDelim == -1 + let delIndx = -1 + else + let delIndx = delIndx + lenDel + distToNextDelim + endif + endwhile + + "there is no comment delimiter on this line + return -1 +endfunction + +" Function: s:FindBoundingLinesOfSexyCom(lineNum) {{{2 +" This function takes in a line number and tests whether this line number is +" the top/bottom/middle line of a sexy comment. If it is then the top/bottom +" lines of the sexy comment are returned +" Args: +" -lineNum: the line number that is to be tested whether it is the +" top/bottom/middle line of a sexy com +" Returns: +" A string that has the top/bottom lines of the sexy comment encoded in it. +" The format is 'topline,bottomline'. If a:lineNum turns out not to be the +" top/bottom/middle of a sexy comment then -1 is returned +function s:FindBoundingLinesOfSexyCom(lineNum) + + "find which delimiters to look for as the start/end delims of the comment + let left = '' + let right = '' + if s:Multipart() + let left = s:Left({'esc': 1}) + let right = s:Right({'esc': 1}) + elseif s:AltMultipart() + let left = s:Left({'alt': 1, 'esc': 1}) + let right = s:Right({'alt': 1, 'esc': 1}) + else + return [] + endif + + let sexyComMarker = s:GetSexyComMarker(0, 1) + + "initialise the top/bottom line numbers of the sexy comment to -1 + let top = -1 + let bottom = -1 + + let currentLine = a:lineNum + while top == -1 || bottom == -1 + let theLine = getline(currentLine) + + "check if the current line is the top of the sexy comment + if currentLine <= a:lineNum && theLine =~ '^[ \t]*' . left && theLine !~ '.*' . right && currentLine < s:NumLinesInBuf() + let top = currentLine + let currentLine = a:lineNum + + "check if the current line is the bottom of the sexy comment + elseif theLine =~ '^[ \t]*' . right && theLine !~ '.*' . left && currentLine > 1 + let bottom = currentLine + + "the right delimiter is on the same line as the last sexyComMarker + elseif theLine =~ '^[ \t]*' . sexyComMarker . '.*' . right + let bottom = currentLine + + "we have not found the top or bottom line so we assume currentLine is an + "intermediate line and look to prove otherwise + else + + "if the line doesnt start with a sexyComMarker then it is not a sexy + "comment + if theLine !~ '^[ \t]*' . sexyComMarker + return [] + endif + + endif + + "if top is -1 then we havent found the top yet so keep looking up + if top == -1 + let currentLine = currentLine - 1 + "if we have found the top line then go down looking for the bottom + else + let currentLine = currentLine + 1 + endif + + endwhile + + return [top, bottom] +endfunction + + +" Function: s:GetSexyComMarker() {{{2 +" Returns the sexy comment marker for the current filetype. +" +" C style sexy comments are assumed if possible. If not then the sexy comment +" marker is the last char of the delimiter pair that has both left and right +" delims and has the longest left delim +" +" Args: +" -space: specifies whether the marker is to have a space string after it +" (the space string will only be added if NERDSpaceDelims is set) +" -esc: specifies whether the tricky chars in the marker are to be ESCed +function s:GetSexyComMarker(space, esc) + let sexyComMarker = b:NERDSexyComMarker + + "if there is no hardcoded marker then we find one + if sexyComMarker == '' + + "if the filetype has c style comments then use standard c sexy + "comments + if s:HasCStyleComments() + let sexyComMarker = '*' + else + "find a comment marker by getting the longest available left delim + "(that has a corresponding right delim) and taking the last char + let lenLeft = strlen(s:Left()) + let lenLeftAlt = strlen(s:Left({'alt': 1})) + let left = '' + let right = '' + if s:Multipart() && lenLeft >= lenLeftAlt + let left = s:Left() + elseif s:AltMultipart() + let left = s:Left({'alt': 1}) + else + return -1 + endif + + "get the last char of left + let sexyComMarker = strpart(left, strlen(left)-1) + endif + endif + + if a:space && g:NERDSpaceDelims + let sexyComMarker = sexyComMarker . s:spaceStr + endif + + if a:esc + let sexyComMarker = s:Esc(sexyComMarker) + endif + + return sexyComMarker +endfunction + +" Function: s:GetSexyComLeft(space, esc) {{{2 +" Returns the left delimiter for sexy comments for this filetype or -1 if +" there is none. C style sexy comments are used if possible +" Args: +" -space: specifies if the delim has a space string on the end +" (the space string will only be added if NERDSpaceDelims is set) +" -esc: specifies whether the tricky chars in the string are ESCed +function s:GetSexyComLeft(space, esc) + let lenLeft = strlen(s:Left()) + let lenLeftAlt = strlen(s:Left({'alt': 1})) + let left = '' + + "assume c style sexy comments if possible + if s:HasCStyleComments() + let left = '/*' + else + "grab the longest left delim that has a right + if s:Multipart() && lenLeft >= lenLeftAlt + let left = s:Left() + elseif s:AltMultipart() + let left = s:Left({'alt': 1}) + else + return -1 + endif + endif + + if a:space && g:NERDSpaceDelims + let left = left . s:spaceStr + endif + + if a:esc + let left = s:Esc(left) + endif + + return left +endfunction + +" Function: s:GetSexyComRight(space, esc) {{{2 +" Returns the right delimiter for sexy comments for this filetype or -1 if +" there is none. C style sexy comments are used if possible. +" Args: +" -space: specifies if the delim has a space string on the start +" (the space string will only be added if NERDSpaceDelims +" is specified for the current filetype) +" -esc: specifies whether the tricky chars in the string are ESCed +function s:GetSexyComRight(space, esc) + let lenLeft = strlen(s:Left()) + let lenLeftAlt = strlen(s:Left({'alt': 1})) + let right = '' + + "assume c style sexy comments if possible + if s:HasCStyleComments() + let right = '*/' + else + "grab the right delim that pairs with the longest left delim + if s:Multipart() && lenLeft >= lenLeftAlt + let right = s:Right() + elseif s:AltMultipart() + let right = s:Right({'alt': 1}) + else + return -1 + endif + endif + + if a:space && g:NERDSpaceDelims + let right = s:spaceStr . right + endif + + if a:esc + let right = s:Esc(right) + endif + + return right +endfunction + +" Function: s:HasMultipartDelims() {{{2 +" Returns 1 iff the current filetype has at least one set of multipart delims +function s:HasMultipartDelims() + return s:Multipart() || s:AltMultipart() +endfunction + +" Function: s:HasLeadingTabs(...) {{{2 +" Returns 1 if any of the given strings have leading tabs +function s:HasLeadingTabs(...) + for s in a:000 + if s =~ '^\t.*' + return 1 + end + endfor + return 0 +endfunction +" Function: s:HasCStyleComments() {{{2 +" Returns 1 iff the current filetype has c style comment delimiters +function s:HasCStyleComments() + return (s:Left() == '/*' && s:Right() == '*/') || (s:Left({'alt': 1}) == '/*' && s:Right({'alt': 1}) == '*/') +endfunction + +" Function: s:IsCommentedNormOrSexy(lineNum) {{{2 +"This function is used to determine whether the given line is commented with +"either set of delimiters or if it is part of a sexy comment +" +" Args: +" -lineNum: the line number of the line to check +function s:IsCommentedNormOrSexy(lineNum) + let theLine = getline(a:lineNum) + + "if the line is commented normally return 1 + if s:IsCommented(s:Left(), s:Right(), theLine) || s:IsCommented(s:Left({'alt': 1}), s:Right({'alt': 1}), theLine) + return 1 + endif + + "if the line is part of a sexy comment return 1 + if s:IsInSexyComment(a:lineNum) + return 1 + endif + return 0 +endfunction + +" Function: s:IsCommented(left, right, line) {{{2 +"This function is used to determine whether the given line is commented with +"the given delimiters +" +" Args: +" -line: the line that to check if commented +" -left/right: the left and right delimiters to check for +function s:IsCommented(left, right, line) + "if the line isnt commented return true + if s:FindDelimiterIndex(a:left, a:line) != -1 && (s:FindDelimiterIndex(a:right, a:line) != -1 || !s:Multipart()) + return 1 + endif + return 0 +endfunction + +" Function: s:IsCommentedFromStartOfLine(left, line) {{{2 +"This function is used to determine whether the given line is commented with +"the given delimiters at the start of the line i.e the left delimiter is the +"first thing on the line (apart from spaces\tabs) +" +" Args: +" -line: the line that to check if commented +" -left: the left delimiter to check for +function s:IsCommentedFromStartOfLine(left, line) + let theLine = s:ConvertLeadingTabsToSpaces(a:line) + let numSpaces = strlen(substitute(theLine, '^\( *\).*$', '\1', '')) + let delimIndx = s:FindDelimiterIndex(a:left, theLine) + return delimIndx == numSpaces +endfunction + +" Function: s:IsCommentedOuttermost(left, right, leftAlt, rightAlt, line) {{{2 +" Finds the type of the outtermost delims on the line +" +" Args: +" -line: the line that to check if the outtermost comments on it are +" left/right +" -left/right: the left and right delimiters to check for +" -leftAlt/rightAlt: the left and right alternative delimiters to check for +" +" Returns: +" 0 if the line is not commented with either set of delims +" 1 if the line is commented with the left/right delim set +" 2 if the line is commented with the leftAlt/rightAlt delim set +function s:IsCommentedOuttermost(left, right, leftAlt, rightAlt, line) + "get the first positions of the left delims and the last positions of the + "right delims + let indxLeft = s:FindDelimiterIndex(a:left, a:line) + let indxLeftAlt = s:FindDelimiterIndex(a:leftAlt, a:line) + let indxRight = s:LastIndexOfDelim(a:right, a:line) + let indxRightAlt = s:LastIndexOfDelim(a:rightAlt, a:line) + + "check if the line has a left delim before a leftAlt delim + if (indxLeft <= indxLeftAlt || indxLeftAlt == -1) && indxLeft != -1 + "check if the line has a right delim after any rightAlt delim + if (indxRight > indxRightAlt && indxRight > indxLeft) || !s:Multipart() + return 1 + endif + + "check if the line has a leftAlt delim before a left delim + elseif (indxLeftAlt <= indxLeft || indxLeft == -1) && indxLeftAlt != -1 + "check if the line has a rightAlt delim after any right delim + if (indxRightAlt > indxRight && indxRightAlt > indxLeftAlt) || !s:AltMultipart() + return 2 + endif + else + return 0 + endif + + return 0 + +endfunction + + +" Function: s:IsDelimValid(delimiter, delIndx, line) {{{2 +" This function is responsible for determining whether a given instance of a +" comment delimiter is a real delimiter or not. For example, in java the +" // string is a comment delimiter but in the line: +" System.out.println("//"); +" it does not count as a comment delimiter. This function is responsible for +" distinguishing between such cases. It does so by applying a set of +" heuristics that are not fool proof but should work most of the time. +" +" Args: +" -delimiter: the delimiter we are validating +" -delIndx: the position of delimiter in line +" -line: the line that delimiter occurs in +" +" Returns: +" 0 if the given delimiter is not a real delimiter (as far as we can tell) , +" 1 otherwise +function s:IsDelimValid(delimiter, delIndx, line) + "get the delimiter without the escchars + let l:delimiter = a:delimiter + + "get the strings before and after the delimiter + let preComStr = strpart(a:line, 0, a:delIndx) + let postComStr = strpart(a:line, a:delIndx+strlen(delimiter)) + + "to check if the delimiter is real, make sure it isnt preceded by + "an odd number of quotes and followed by the same (which would indicate + "that it is part of a string and therefore is not a comment) + if !s:IsNumEven(s:CountNonESCedOccurances(preComStr, '"', "\\")) && !s:IsNumEven(s:CountNonESCedOccurances(postComStr, '"', "\\")) + return 0 + endif + if !s:IsNumEven(s:CountNonESCedOccurances(preComStr, "'", "\\")) && !s:IsNumEven(s:CountNonESCedOccurances(postComStr, "'", "\\")) + return 0 + endif + if !s:IsNumEven(s:CountNonESCedOccurances(preComStr, "`", "\\")) && !s:IsNumEven(s:CountNonESCedOccurances(postComStr, "`", "\\")) + return 0 + endif + + + "if the comment delimiter is escaped, assume it isnt a real delimiter + if s:IsEscaped(a:line, a:delIndx, "\\") + return 0 + endif + + "vim comments are so fuckin stupid!! Why the hell do they have comment + "delimiters that are used elsewhere in the syntax?!?! We need to check + "some conditions especially for vim + if &filetype == "vim" + if !s:IsNumEven(s:CountNonESCedOccurances(preComStr, '"', "\\")) + return 0 + endif + + "if the delimiter is on the very first char of the line or is the + "first non-tab/space char on the line then it is a valid comment delimiter + if a:delIndx == 0 || a:line =~ "^[ \t]\\{" . a:delIndx . "\\}\".*$" + return 1 + endif + + let numLeftParen =s:CountNonESCedOccurances(preComStr, "(", "\\") + let numRightParen =s:CountNonESCedOccurances(preComStr, ")", "\\") + + "if the quote is inside brackets then assume it isnt a comment + if numLeftParen > numRightParen + return 0 + endif + + "if the line has an even num of unescaped "'s then we can assume that + "any given " is not a comment delimiter + if s:IsNumEven(s:CountNonESCedOccurances(a:line, "\"", "\\")) + return 0 + endif + endif + + return 1 + +endfunction + +" Function: s:IsNumEven(num) {{{2 +" A small function the returns 1 if the input number is even and 0 otherwise +" Args: +" -num: the number to check +function s:IsNumEven(num) + return (a:num % 2) == 0 +endfunction + +" Function: s:IsEscaped(str, indx, escChar) {{{2 +" This function takes a string, an index into that string and an esc char and +" returns 1 if the char at the index is escaped (i.e if it is preceded by an +" odd number of esc chars) +" Args: +" -str: the string to check +" -indx: the index into str that we want to check +" -escChar: the escape char the char at indx may be ESCed with +function s:IsEscaped(str, indx, escChar) + "initialise numEscChars to 0 and look at the char before indx + let numEscChars = 0 + let curIndx = a:indx-1 + + "keep going back thru str until we either reach the start of the str or + "run out of esc chars + while curIndx >= 0 && strpart(a:str, curIndx, 1) == a:escChar + + "we have found another esc char so add one to the count and move left + "one char + let numEscChars = numEscChars + 1 + let curIndx = curIndx - 1 + + endwhile + + "if there is an odd num of esc chars directly before the char at indx then + "the char at indx is escaped + return !s:IsNumEven(numEscChars) +endfunction + +" Function: s:IsInSexyComment(line) {{{2 +" returns 1 if the given line number is part of a sexy comment +function s:IsInSexyComment(line) + return !empty(s:FindBoundingLinesOfSexyCom(a:line)) +endfunction + +" Function: s:IsSexyComment(topline, bottomline) {{{2 +" This function takes in 2 line numbers and returns 1 if the lines between and +" including the given line numbers are a sexy comment. It returns 0 otherwise. +" Args: +" -topline: the line that the possible sexy comment starts on +" -bottomline: the line that the possible sexy comment stops on +function s:IsSexyComment(topline, bottomline) + + "get the delim set that would be used for a sexy comment + let left = '' + let right = '' + if s:Multipart() + let left = s:Left() + let right = s:Right() + elseif s:AltMultipart() + let left = s:Left({'alt': 1}) + let right = s:Right({'alt': 1}) + else + return 0 + endif + + "swap the top and bottom line numbers around if need be + let topline = a:topline + let bottomline = a:bottomline + if bottomline < topline + topline = bottomline + bottomline = a:topline + endif + + "if there is < 2 lines in the comment it cannot be sexy + if (bottomline - topline) <= 0 + return 0 + endif + + "if the top line doesnt begin with a left delim then the comment isnt sexy + if getline(a:topline) !~ '^[ \t]*' . left + return 0 + endif + + "if there is a right delim on the top line then this isnt a sexy comment + if s:FindDelimiterIndex(right, getline(a:topline)) != -1 + return 0 + endif + + "if there is a left delim on the bottom line then this isnt a sexy comment + if s:FindDelimiterIndex(left, getline(a:bottomline)) != -1 + return 0 + endif + + "if the bottom line doesnt begin with a right delim then the comment isnt + "sexy + if getline(a:bottomline) !~ '^.*' . right . '$' + return 0 + endif + + let sexyComMarker = s:GetSexyComMarker(0, 1) + + "check each of the intermediate lines to make sure they start with a + "sexyComMarker + let currentLine = a:topline+1 + while currentLine < a:bottomline + let theLine = getline(currentLine) + + if theLine !~ '^[ \t]*' . sexyComMarker + return 0 + endif + + "if there is a right delim in an intermediate line then the block isnt + "a sexy comment + if s:FindDelimiterIndex(right, theLine) != -1 + return 0 + endif + + let currentLine = currentLine + 1 + endwhile + + "we have not found anything to suggest that this isnt a sexy comment so + return 1 + +endfunction + +" Function: s:LastIndexOfDelim(delim, str) {{{2 +" This function takes a string and a delimiter and returns the last index of +" that delimiter in string +" Args: +" -delim: the delimiter to look for +" -str: the string to look for delim in +function s:LastIndexOfDelim(delim, str) + let delim = a:delim + let lenDelim = strlen(delim) + + "set index to the first occurrence of delim. If there is no occurrence then + "bail + let indx = s:FindDelimiterIndex(delim, a:str) + if indx == -1 + return -1 + endif + + "keep moving to the next instance of delim in str till there is none left + while 1 + + "search for the next delim after the previous one + let searchStr = strpart(a:str, indx+lenDelim) + let indx2 = s:FindDelimiterIndex(delim, searchStr) + + "if we find a delim update indx to record the position of it, if we + "dont find another delim then indx is the last one so break out of + "this loop + if indx2 != -1 + let indx = indx + indx2 + lenDelim + else + break + endif + endwhile + + return indx + +endfunction + +" Function: s:Left(...) {{{2 +" returns left delimiter data +function s:Left(...) + let params = a:0 ? a:1 : {} + + let delim = has_key(params, 'alt') ? b:NERDCommenterDelims['leftAlt'] : b:NERDCommenterDelims['left'] + + if delim == '' + return '' + endif + + if has_key(params, 'space') && g:NERDSpaceDelims + let delim = delim . s:spaceStr + endif + + if has_key(params, 'esc') + let delim = s:Esc(delim) + endif + + return delim +endfunction + +" Function: s:LeftMostIndx(countCommentedLines, countEmptyLines, topline, bottomline) {{{2 +" This function takes in 2 line numbers and returns the index of the left most +" char (that is not a space or a tab) on all of these lines. +" Args: +" -countCommentedLines: 1 if lines that are commented are to be checked as +" well. 0 otherwise +" -countEmptyLines: 1 if empty lines are to be counted in the search +" -topline: the top line to be checked +" -bottomline: the bottom line to be checked +function s:LeftMostIndx(countCommentedLines, countEmptyLines, topline, bottomline) + + " declare the left most index as an extreme value + let leftMostIndx = 1000 + + " go thru the block line by line updating leftMostIndx + let currentLine = a:topline + while currentLine <= a:bottomline + + " get the next line and if it is allowed to be commented, or is not + " commented, check it + let theLine = getline(currentLine) + if a:countEmptyLines || theLine !~ '^[ \t]*$' + if a:countCommentedLines || (!s:IsCommented(s:Left(), s:Right(), theLine) && !s:IsCommented(s:Left({'alt': 1}), s:Right({'alt': 1}), theLine)) + " convert spaces to tabs and get the number of leading spaces for + " this line and update leftMostIndx if need be + let theLine = s:ConvertLeadingTabsToSpaces(theLine) + let leadSpaceOfLine = strlen( substitute(theLine, '\(^[ \t]*\).*$','\1','') ) + if leadSpaceOfLine < leftMostIndx + let leftMostIndx = leadSpaceOfLine + endif + endif + endif + + " move on to the next line + let currentLine = currentLine + 1 + endwhile + + if leftMostIndx == 1000 + return 0 + else + return leftMostIndx + endif +endfunction + +" Function: s:Multipart() {{{2 +" returns 1 if the current delims are multipart +function s:Multipart() + return s:Right() != '' +endfunction + +" Function: s:NerdEcho(msg, typeOfMsg) {{{2 +" Args: +" -msg: the message to echo +" -typeOfMsg: 0 = warning message +" 1 = normal message +function s:NerdEcho(msg, typeOfMsg) + if a:typeOfMsg == 0 + echohl WarningMsg + echom 'NERDCommenter:' . a:msg + echohl None + elseif a:typeOfMsg == 1 + echom 'NERDCommenter:' . a:msg + endif +endfunction + +" Function: s:NumberOfLeadingTabs(s) {{{2 +" returns the number of leading tabs in the given string +function s:NumberOfLeadingTabs(s) + return strlen(substitute(a:s, '^\(\t*\).*$', '\1', "")) +endfunction + +" Function: s:NumLinesInBuf() {{{2 +" Returns the number of lines in the current buffer +function s:NumLinesInBuf() + return line('$') +endfunction + +" Function: s:ReplaceDelims(toReplace1, toReplace2, replacor1, replacor2, str) {{{2 +" This function takes in a string, 2 delimiters in that string and 2 strings +" to replace these delimiters with. +" +" Args: +" -toReplace1: the first delimiter to replace +" -toReplace2: the second delimiter to replace +" -replacor1: the string to replace toReplace1 with +" -replacor2: the string to replace toReplace2 with +" -str: the string that the delimiters to be replaced are in +function s:ReplaceDelims(toReplace1, toReplace2, replacor1, replacor2, str) + let line = s:ReplaceLeftMostDelim(a:toReplace1, a:replacor1, a:str) + let line = s:ReplaceRightMostDelim(a:toReplace2, a:replacor2, line) + return line +endfunction + +" Function: s:ReplaceLeftMostDelim(toReplace, replacor, str) {{{2 +" This function takes a string and a delimiter and replaces the left most +" occurrence of this delimiter in the string with a given string +" +" Args: +" -toReplace: the delimiter in str that is to be replaced +" -replacor: the string to replace toReplace with +" -str: the string that contains toReplace +function s:ReplaceLeftMostDelim(toReplace, replacor, str) + let toReplace = a:toReplace + let replacor = a:replacor + "get the left most occurrence of toReplace + let indxToReplace = s:FindDelimiterIndex(toReplace, a:str) + + "if there IS an occurrence of toReplace in str then replace it and return + "the resulting string + if indxToReplace != -1 + let line = strpart(a:str, 0, indxToReplace) . replacor . strpart(a:str, indxToReplace+strlen(toReplace)) + return line + endif + + return a:str +endfunction + +" Function: s:ReplaceRightMostDelim(toReplace, replacor, str) {{{2 +" This function takes a string and a delimiter and replaces the right most +" occurrence of this delimiter in the string with a given string +" +" Args: +" -toReplace: the delimiter in str that is to be replaced +" -replacor: the string to replace toReplace with +" -str: the string that contains toReplace +" +function s:ReplaceRightMostDelim(toReplace, replacor, str) + let toReplace = a:toReplace + let replacor = a:replacor + let lenToReplace = strlen(toReplace) + + "get the index of the last delim in str + let indxToReplace = s:LastIndexOfDelim(toReplace, a:str) + + "if there IS a delimiter in str, replace it and return the result + let line = a:str + if indxToReplace != -1 + let line = strpart(a:str, 0, indxToReplace) . replacor . strpart(a:str, indxToReplace+strlen(toReplace)) + endif + return line +endfunction + +"FUNCTION: s:RestoreScreenState() {{{2 +" +"Sets the screen state back to what it was when s:SaveScreenState was last +"called. +" +function s:RestoreScreenState() + if !exists("t:NERDComOldTopLine") || !exists("t:NERDComOldPos") + throw 'NERDCommenter exception: cannot restore screen' + endif + + call cursor(t:NERDComOldTopLine, 0) + normal! zt + call setpos(".", t:NERDComOldPos) +endfunction + +" Function: s:Right(...) {{{2 +" returns right delimiter data +function s:Right(...) + let params = a:0 ? a:1 : {} + + let delim = has_key(params, 'alt') ? b:NERDCommenterDelims['rightAlt'] : b:NERDCommenterDelims['right'] + + if delim == '' + return '' + endif + + if has_key(params, 'space') && g:NERDSpaceDelims + let delim = s:spaceStr . delim + endif + + if has_key(params, 'esc') + let delim = s:Esc(delim) + endif + + return delim +endfunction + +" Function: s:RightMostIndx(countCommentedLines, countEmptyLines, topline, bottomline) {{{2 +" This function takes in 2 line numbers and returns the index of the right most +" char on all of these lines. +" Args: +" -countCommentedLines: 1 if lines that are commented are to be checked as +" well. 0 otherwise +" -countEmptyLines: 1 if empty lines are to be counted in the search +" -topline: the top line to be checked +" -bottomline: the bottom line to be checked +function s:RightMostIndx(countCommentedLines, countEmptyLines, topline, bottomline) + let rightMostIndx = -1 + + " go thru the block line by line updating rightMostIndx + let currentLine = a:topline + while currentLine <= a:bottomline + + " get the next line and see if it is commentable, otherwise it doesnt + " count + let theLine = getline(currentLine) + if a:countEmptyLines || theLine !~ '^[ \t]*$' + + if a:countCommentedLines || (!s:IsCommented(s:Left(), s:Right(), theLine) && !s:IsCommented(s:Left({'alt': 1}), s:Right({'alt': 1}), theLine)) + + " update rightMostIndx if need be + let theLine = s:ConvertLeadingTabsToSpaces(theLine) + let lineLen = strlen(theLine) + if lineLen > rightMostIndx + let rightMostIndx = lineLen + endif + endif + endif + + " move on to the next line + let currentLine = currentLine + 1 + endwhile + + return rightMostIndx +endfunction + +"FUNCTION: s:SaveScreenState() {{{2 +"Saves the current cursor position in the current buffer and the window +"scroll position +function s:SaveScreenState() + let t:NERDComOldPos = getpos(".") + let t:NERDComOldTopLine = line("w0") +endfunction + +" Function: s:SwapOutterMultiPartDelimsForPlaceHolders(line) {{{2 +" This function takes a line and swaps the outter most multi-part delims for +" place holders +" Args: +" -line: the line to swap the delims in +" +function s:SwapOutterMultiPartDelimsForPlaceHolders(line) + " find out if the line is commented using normal delims and/or + " alternate ones + let isCommented = s:IsCommented(s:Left(), s:Right(), a:line) + let isCommentedAlt = s:IsCommented(s:Left({'alt': 1}), s:Right({'alt': 1}), a:line) + + let line2 = a:line + + "if the line is commented and there is a right delimiter, replace + "the delims with place-holders + if isCommented && s:Multipart() + let line2 = s:ReplaceDelims(s:Left(), s:Right(), g:NERDLPlace, g:NERDRPlace, a:line) + + "similarly if the line is commented with the alternative + "delimiters + elseif isCommentedAlt && s:AltMultipart() + let line2 = s:ReplaceDelims(s:Left({'alt': 1}), s:Right({'alt': 1}), g:NERDLPlace, g:NERDRPlace, a:line) + endif + + return line2 +endfunction + +" Function: s:SwapOutterPlaceHoldersForMultiPartDelims(line) {{{2 +" This function takes a line and swaps the outtermost place holders for +" multi-part delims +" Args: +" -line: the line to swap the delims in +" +function s:SwapOutterPlaceHoldersForMultiPartDelims(line) + let left = '' + let right = '' + if s:Multipart() + let left = s:Left() + let right = s:Right() + elseif s:AltMultipart() + let left = s:Left({'alt': 1}) + let right = s:Right({'alt': 1}) + endif + + let line = s:ReplaceDelims(g:NERDLPlace, g:NERDRPlace, left, right, a:line) + return line +endfunction +" Function: s:TabbedCol(line, col) {{{2 +" Gets the col number for given line and existing col number. The new col +" number is the col number when all leading spaces are converted to tabs +" Args: +" -line:the line to get the rel col for +" -col: the abs col +function s:TabbedCol(line, col) + let lineTruncated = strpart(a:line, 0, a:col) + let lineSpacesToTabs = substitute(lineTruncated, s:TabSpace(), '\t', 'g') + return strlen(lineSpacesToTabs) +endfunction +"FUNCTION: s:TabSpace() {{{2 +"returns a string of spaces equal in length to &tabstop +function s:TabSpace() + let tabSpace = "" + let spacesPerTab = &tabstop + while spacesPerTab > 0 + let tabSpace = tabSpace . " " + let spacesPerTab = spacesPerTab - 1 + endwhile + return tabSpace +endfunction + +" Function: s:UnEsc(str, escChar) {{{2 +" This function removes all the escape chars from a string +" Args: +" -str: the string to remove esc chars from +" -escChar: the escape char to be removed +function s:UnEsc(str, escChar) + return substitute(a:str, a:escChar, "", "g") +endfunction + +" Function: s:UntabbedCol(line, col) {{{2 +" Takes a line and a col and returns the absolute column of col taking into +" account that a tab is worth 3 or 4 (or whatever) spaces. +" Args: +" -line:the line to get the abs col for +" -col: the col that doesnt take into account tabs +function s:UntabbedCol(line, col) + let lineTruncated = strpart(a:line, 0, a:col) + let lineTabsToSpaces = substitute(lineTruncated, '\t', s:TabSpace(), 'g') + return strlen(lineTabsToSpaces) +endfunction +" Section: Comment mapping setup {{{1 +" =========================================================================== + +" switch to/from alternative delimiters +nnoremap NERDCommenterAltDelims :call SwitchToAlternativeDelimiters(1) + +" comment out lines +nnoremap NERDCommenterComment :call NERDComment(0, "norm") +vnoremap NERDCommenterComment :call NERDComment(1, "norm") + +" toggle comments +nnoremap NERDCommenterToggle :call NERDComment(0, "toggle") +vnoremap NERDCommenterToggle :call NERDComment(1, "toggle") + +" minimal comments +nnoremap NERDCommenterMinimal :call NERDComment(0, "minimal") +vnoremap NERDCommenterMinimal :call NERDComment(1, "minimal") + +" sexy comments +nnoremap NERDCommenterSexy :call NERDComment(0, "sexy") +vnoremap NERDCommenterSexy :call NERDComment(1, "sexy") + +" invert comments +nnoremap NERDCommenterInvert :call NERDComment(0, "invert") +vnoremap NERDCommenterInvert :call NERDComment(1, "invert") + +" yank then comment +nmap NERDCommenterYank :call NERDComment(0, "yank") +vmap NERDCommenterYank :call NERDComment(1, "yank") + +" left aligned comments +nnoremap NERDCommenterAlignLeft :call NERDComment(0, "alignLeft") +vnoremap NERDCommenterAlignLeft :call NERDComment(1, "alignLeft") + +" left and right aligned comments +nnoremap NERDCommenterAlignBoth :call NERDComment(0, "alignBoth") +vnoremap NERDCommenterAlignBoth :call NERDComment(1, "alignBoth") + +" nested comments +nnoremap NERDCommenterNest :call NERDComment(0, "nested") +vnoremap NERDCommenterNest :call NERDComment(1, "nested") + +" uncomment +nnoremap NERDCommenterUncomment :call NERDComment(0, "uncomment") +vnoremap NERDCommenterUncomment :call NERDComment(1, "uncomment") + +" comment till the end of the line +nnoremap NERDCommenterToEOL :call NERDComment(0, "toEOL") + +" append comments +nmap NERDCommenterAppend :call NERDComment(0, "append") + +" insert comments +inoremap NERDCommenterInInsert :call NERDComment(0, "insert") + + +function! s:CreateMaps(target, combo) + if !hasmapto(a:target, 'n') + exec 'nmap ' . a:combo . ' ' . a:target + endif + + if !hasmapto(a:target, 'v') + exec 'vmap ' . a:combo . ' ' . a:target + endif +endfunction + +if g:NERDCreateDefaultMappings + call s:CreateMaps('NERDCommenterComment', 'cc') + call s:CreateMaps('NERDCommenterToggle', 'c') + call s:CreateMaps('NERDCommenterMinimal', 'cm') + call s:CreateMaps('NERDCommenterSexy', 'cs') + call s:CreateMaps('NERDCommenterInvert', 'ci') + call s:CreateMaps('NERDCommenterYank', 'cy') + call s:CreateMaps('NERDCommenterAlignLeft', 'cl') + call s:CreateMaps('NERDCommenterAlignBoth', 'cb') + call s:CreateMaps('NERDCommenterNest', 'cn') + call s:CreateMaps('NERDCommenterUncomment', 'cu') + call s:CreateMaps('NERDCommenterToEOL', 'c$') + call s:CreateMaps('NERDCommenterAppend', 'cA') + + if !hasmapto('NERDCommenterAltDelims', 'n') + nmap ca NERDCommenterAltDelims + endif +endif + + + +" Section: Menu item setup {{{1 +" =========================================================================== +"check if the user wants the menu to be displayed +if g:NERDMenuMode != 0 + + let menuRoot = "" + if g:NERDMenuMode == 1 + let menuRoot = 'comment' + elseif g:NERDMenuMode == 2 + let menuRoot = '&comment' + elseif g:NERDMenuMode == 3 + let menuRoot = '&Plugin.&comment' + endif + + function! s:CreateMenuItems(target, desc, root) + exec 'nmenu ' . a:root . '.' . a:desc . ' ' . a:target + exec 'vmenu ' . a:root . '.' . a:desc . ' ' . a:target + endfunction + call s:CreateMenuItems("NERDCommenterComment", 'Comment', menuRoot) + call s:CreateMenuItems("NERDCommenterToggle", 'Toggle', menuRoot) + call s:CreateMenuItems('NERDCommenterMinimal', 'Minimal', menuRoot) + call s:CreateMenuItems('NERDCommenterNest', 'Nested', menuRoot) + exec 'nmenu '. menuRoot .'.To\ EOL NERDCommenterToEOL' + call s:CreateMenuItems('NERDCommenterInvert', 'Invert', menuRoot) + call s:CreateMenuItems('NERDCommenterSexy', 'Sexy', menuRoot) + call s:CreateMenuItems('NERDCommenterYank', 'Yank\ then\ comment', menuRoot) + exec 'nmenu '. menuRoot .'.Append NERDCommenterAppend' + exec 'menu '. menuRoot .'.-Sep- :' + call s:CreateMenuItems('NERDCommenterAlignLeft', 'Left\ aligned', menuRoot) + call s:CreateMenuItems('NERDCommenterAlignBoth', 'Left\ and\ right\ aligned', menuRoot) + exec 'menu '. menuRoot .'.-Sep2- :' + call s:CreateMenuItems('NERDCommenterUncomment', 'Uncomment', menuRoot) + exec 'nmenu '. menuRoot .'.Switch\ Delimiters NERDCommenterAltDelims' + exec 'imenu '. menuRoot .'.Insert\ Comment\ Here NERDCommenterInInsert' + exec 'menu '. menuRoot .'.-Sep3- :' + exec 'menu '. menuRoot .'.Help :help NERDCommenterContents' +endif +" vim: set foldmethod=marker : diff --git a/plugin/NERD_tree.vim b/plugin/NERD_tree.vim new file mode 100644 index 0000000..6411b1d --- /dev/null +++ b/plugin/NERD_tree.vim @@ -0,0 +1,4059 @@ +" ============================================================================ +" File: NERD_tree.vim +" Description: vim global plugin that provides a nice tree explorer +" Maintainer: Martin Grenfell +" Last Change: 1 December, 2009 +" License: This program is free software. It comes without any warranty, +" to the extent permitted by applicable law. You can redistribute +" it and/or modify it under the terms of the Do What The Fuck You +" Want To Public License, Version 2, as published by Sam Hocevar. +" See http://sam.zoy.org/wtfpl/COPYING for more details. +" +" ============================================================================ +let s:NERD_tree_version = '4.1.0' + +" SECTION: Script init stuff {{{1 +"============================================================ +if exists("loaded_nerd_tree") + finish +endif +if v:version < 700 + echoerr "NERDTree: this plugin requires vim >= 7. DOWNLOAD IT! You'll thank me later!" + finish +endif +let loaded_nerd_tree = 1 + +"for line continuation - i.e dont want C in &cpo +let s:old_cpo = &cpo +set cpo&vim + +"Function: s:initVariable() function {{{2 +"This function is used to initialise a given variable to a given value. The +"variable is only initialised if it does not exist prior +" +"Args: +"var: the name of the var to be initialised +"value: the value to initialise var to +" +"Returns: +"1 if the var is set, 0 otherwise +function! s:initVariable(var, value) + if !exists(a:var) + exec 'let ' . a:var . ' = ' . "'" . a:value . "'" + return 1 + endif + return 0 +endfunction + +"SECTION: Init variable calls and other random constants {{{2 +call s:initVariable("g:NERDChristmasTree", 1) +call s:initVariable("g:NERDTreeAutoCenter", 1) +call s:initVariable("g:NERDTreeAutoCenterThreshold", 3) +call s:initVariable("g:NERDTreeCaseSensitiveSort", 0) +call s:initVariable("g:NERDTreeChDirMode", 0) +if !exists("g:NERDTreeIgnore") + let g:NERDTreeIgnore = ['\~$'] +endif +call s:initVariable("g:NERDTreeBookmarksFile", expand('$HOME') . '/.NERDTreeBookmarks') +call s:initVariable("g:NERDTreeHighlightCursorline", 1) +call s:initVariable("g:NERDTreeHijackNetrw", 1) +call s:initVariable("g:NERDTreeMouseMode", 1) +call s:initVariable("g:NERDTreeNotificationThreshold", 100) +call s:initVariable("g:NERDTreeQuitOnOpen", 0) +call s:initVariable("g:NERDTreeShowBookmarks", 0) +call s:initVariable("g:NERDTreeShowFiles", 1) +call s:initVariable("g:NERDTreeShowHidden", 0) +call s:initVariable("g:NERDTreeShowLineNumbers", 0) +call s:initVariable("g:NERDTreeSortDirs", 1) + +if !exists("g:NERDTreeSortOrder") + let g:NERDTreeSortOrder = ['\/$', '*', '\.swp$', '\.bak$', '\~$'] +else + "if there isnt a * in the sort sequence then add one + if count(g:NERDTreeSortOrder, '*') < 1 + call add(g:NERDTreeSortOrder, '*') + endif +endif + +"we need to use this number many times for sorting... so we calculate it only +"once here +let s:NERDTreeSortStarIndex = index(g:NERDTreeSortOrder, '*') + +if !exists('g:NERDTreeStatusline') + + "the exists() crap here is a hack to stop vim spazzing out when + "loading a session that was created with an open nerd tree. It spazzes + "because it doesnt store b:NERDTreeRoot (its a b: var, and its a hash) + let g:NERDTreeStatusline = "%{exists('b:NERDTreeRoot')?b:NERDTreeRoot.path.str():''}" + +endif +call s:initVariable("g:NERDTreeWinPos", "left") +call s:initVariable("g:NERDTreeWinSize", 31) + +let s:running_windows = has("win16") || has("win32") || has("win64") + +"init the shell commands that will be used to copy nodes, and remove dir trees +" +"Note: the space after the command is important +if s:running_windows + call s:initVariable("g:NERDTreeRemoveDirCmd", 'rmdir /s /q ') +else + call s:initVariable("g:NERDTreeRemoveDirCmd", 'rm -rf ') + call s:initVariable("g:NERDTreeCopyCmd", 'cp -r ') +endif + + +"SECTION: Init variable calls for key mappings {{{2 +call s:initVariable("g:NERDTreeMapActivateNode", "o") +call s:initVariable("g:NERDTreeMapChangeRoot", "C") +call s:initVariable("g:NERDTreeMapChdir", "cd") +call s:initVariable("g:NERDTreeMapCloseChildren", "X") +call s:initVariable("g:NERDTreeMapCloseDir", "x") +call s:initVariable("g:NERDTreeMapDeleteBookmark", "D") +call s:initVariable("g:NERDTreeMapMenu", "m") +call s:initVariable("g:NERDTreeMapHelp", "?") +call s:initVariable("g:NERDTreeMapJumpFirstChild", "K") +call s:initVariable("g:NERDTreeMapJumpLastChild", "J") +call s:initVariable("g:NERDTreeMapJumpNextSibling", "") +call s:initVariable("g:NERDTreeMapJumpParent", "p") +call s:initVariable("g:NERDTreeMapJumpPrevSibling", "") +call s:initVariable("g:NERDTreeMapJumpRoot", "P") +call s:initVariable("g:NERDTreeMapOpenExpl", "e") +call s:initVariable("g:NERDTreeMapOpenInTab", "t") +call s:initVariable("g:NERDTreeMapOpenInTabSilent", "T") +call s:initVariable("g:NERDTreeMapOpenRecursively", "O") +call s:initVariable("g:NERDTreeMapOpenSplit", "i") +call s:initVariable("g:NERDTreeMapOpenVSplit", "s") +call s:initVariable("g:NERDTreeMapPreview", "g" . NERDTreeMapActivateNode) +call s:initVariable("g:NERDTreeMapPreviewSplit", "g" . NERDTreeMapOpenSplit) +call s:initVariable("g:NERDTreeMapPreviewVSplit", "g" . NERDTreeMapOpenVSplit) +call s:initVariable("g:NERDTreeMapQuit", "q") +call s:initVariable("g:NERDTreeMapRefresh", "r") +call s:initVariable("g:NERDTreeMapRefreshRoot", "R") +call s:initVariable("g:NERDTreeMapToggleBookmarks", "B") +call s:initVariable("g:NERDTreeMapToggleFiles", "F") +call s:initVariable("g:NERDTreeMapToggleFilters", "f") +call s:initVariable("g:NERDTreeMapToggleHidden", "I") +call s:initVariable("g:NERDTreeMapToggleZoom", "A") +call s:initVariable("g:NERDTreeMapUpdir", "u") +call s:initVariable("g:NERDTreeMapUpdirKeepOpen", "U") + +"SECTION: Script level variable declaration{{{2 +if s:running_windows + let s:escape_chars = " `\|\"#%&,?()\*^<>" +else + let s:escape_chars = " \\`\|\"#%&,?()\*^<>" +endif +let s:NERDTreeBufName = 'NERD_tree_' + +let s:tree_wid = 2 +let s:tree_markup_reg = '^[ `|]*[\-+~]' +let s:tree_up_dir_line = '.. (up a dir)' + +"the number to add to the nerd tree buffer name to make the buf name unique +let s:next_buffer_number = 1 + +" SECTION: Commands {{{1 +"============================================================ +"init the command that users start the nerd tree with +command! -n=? -complete=dir -bar NERDTree :call s:initNerdTree('') +command! -n=? -complete=dir -bar NERDTreeToggle :call s:toggle('') +command! -n=0 -bar NERDTreeClose :call s:closeTreeIfOpen() +command! -n=1 -complete=customlist,s:completeBookmarks -bar NERDTreeFromBookmark call s:initNerdTree('') +command! -n=0 -bar NERDTreeMirror call s:initNerdTreeMirror() +command! -n=0 -bar NERDTreeFind call s:findAndRevealPath() +" SECTION: Auto commands {{{1 +"============================================================ +augroup NERDTree + "Save the cursor position whenever we close the nerd tree + exec "autocmd BufWinLeave ". s:NERDTreeBufName ."* call saveScreenState()" + "cache bookmarks when vim loads + autocmd VimEnter * call s:Bookmark.CacheBookmarks(0) + + "load all nerdtree plugins after vim starts + autocmd VimEnter * runtime! nerdtree_plugin/**/*.vim +augroup END + +if g:NERDTreeHijackNetrw + augroup NERDTreeHijackNetrw + autocmd VimEnter * silent! autocmd! FileExplorer + au BufEnter,VimEnter * call s:checkForBrowse(expand("")) + augroup END +endif + +"SECTION: Classes {{{1 +"============================================================ +"CLASS: Bookmark {{{2 +"============================================================ +let s:Bookmark = {} +" FUNCTION: Bookmark.activate() {{{3 +function! s:Bookmark.activate() + if self.path.isDirectory + call self.toRoot() + else + if self.validate() + let n = s:TreeFileNode.New(self.path) + call n.open() + endif + endif +endfunction +" FUNCTION: Bookmark.AddBookmark(name, path) {{{3 +" Class method to add a new bookmark to the list, if a previous bookmark exists +" with the same name, just update the path for that bookmark +function! s:Bookmark.AddBookmark(name, path) + for i in s:Bookmark.Bookmarks() + if i.name ==# a:name + let i.path = a:path + return + endif + endfor + call add(s:Bookmark.Bookmarks(), s:Bookmark.New(a:name, a:path)) + call s:Bookmark.Sort() +endfunction +" Function: Bookmark.Bookmarks() {{{3 +" Class method to get all bookmarks. Lazily initializes the bookmarks global +" variable +function! s:Bookmark.Bookmarks() + if !exists("g:NERDTreeBookmarks") + let g:NERDTreeBookmarks = [] + endif + return g:NERDTreeBookmarks +endfunction +" Function: Bookmark.BookmarkExistsFor(name) {{{3 +" class method that returns 1 if a bookmark with the given name is found, 0 +" otherwise +function! s:Bookmark.BookmarkExistsFor(name) + try + call s:Bookmark.BookmarkFor(a:name) + return 1 + catch /^NERDTree.BookmarkNotFoundError/ + return 0 + endtry +endfunction +" Function: Bookmark.BookmarkFor(name) {{{3 +" Class method to get the bookmark that has the given name. {} is return if no +" bookmark is found +function! s:Bookmark.BookmarkFor(name) + for i in s:Bookmark.Bookmarks() + if i.name ==# a:name + return i + endif + endfor + throw "NERDTree.BookmarkNotFoundError: no bookmark found for name: \"". a:name .'"' +endfunction +" Function: Bookmark.BookmarkNames() {{{3 +" Class method to return an array of all bookmark names +function! s:Bookmark.BookmarkNames() + let names = [] + for i in s:Bookmark.Bookmarks() + call add(names, i.name) + endfor + return names +endfunction +" FUNCTION: Bookmark.CacheBookmarks(silent) {{{3 +" Class method to read all bookmarks from the bookmarks file intialize +" bookmark objects for each one. +" +" Args: +" silent - dont echo an error msg if invalid bookmarks are found +function! s:Bookmark.CacheBookmarks(silent) + if filereadable(g:NERDTreeBookmarksFile) + let g:NERDTreeBookmarks = [] + let g:NERDTreeInvalidBookmarks = [] + let bookmarkStrings = readfile(g:NERDTreeBookmarksFile) + let invalidBookmarksFound = 0 + for i in bookmarkStrings + + "ignore blank lines + if i != '' + + let name = substitute(i, '^\(.\{-}\) .*$', '\1', '') + let path = substitute(i, '^.\{-} \(.*\)$', '\1', '') + + try + let bookmark = s:Bookmark.New(name, s:Path.New(path)) + call add(g:NERDTreeBookmarks, bookmark) + catch /^NERDTree.InvalidArgumentsError/ + call add(g:NERDTreeInvalidBookmarks, i) + let invalidBookmarksFound += 1 + endtry + endif + endfor + if invalidBookmarksFound + call s:Bookmark.Write() + if !a:silent + call s:echo(invalidBookmarksFound . " invalid bookmarks were read. See :help NERDTreeInvalidBookmarks for info.") + endif + endif + call s:Bookmark.Sort() + endif +endfunction +" FUNCTION: Bookmark.compareTo(otherbookmark) {{{3 +" Compare these two bookmarks for sorting purposes +function! s:Bookmark.compareTo(otherbookmark) + return a:otherbookmark.name < self.name +endfunction +" FUNCTION: Bookmark.ClearAll() {{{3 +" Class method to delete all bookmarks. +function! s:Bookmark.ClearAll() + for i in s:Bookmark.Bookmarks() + call i.delete() + endfor + call s:Bookmark.Write() +endfunction +" FUNCTION: Bookmark.delete() {{{3 +" Delete this bookmark. If the node for this bookmark is under the current +" root, then recache bookmarks for its Path object +function! s:Bookmark.delete() + let node = {} + try + let node = self.getNode(1) + catch /^NERDTree.BookmarkedNodeNotFoundError/ + endtry + call remove(s:Bookmark.Bookmarks(), index(s:Bookmark.Bookmarks(), self)) + if !empty(node) + call node.path.cacheDisplayString() + endif + call s:Bookmark.Write() +endfunction +" FUNCTION: Bookmark.getNode(searchFromAbsoluteRoot) {{{3 +" Gets the treenode for this bookmark +" +" Args: +" searchFromAbsoluteRoot: specifies whether we should search from the current +" tree root, or the highest cached node +function! s:Bookmark.getNode(searchFromAbsoluteRoot) + let searchRoot = a:searchFromAbsoluteRoot ? s:TreeDirNode.AbsoluteTreeRoot() : b:NERDTreeRoot + let targetNode = searchRoot.findNode(self.path) + if empty(targetNode) + throw "NERDTree.BookmarkedNodeNotFoundError: no node was found for bookmark: " . self.name + endif + return targetNode +endfunction +" FUNCTION: Bookmark.GetNodeForName(name, searchFromAbsoluteRoot) {{{3 +" Class method that finds the bookmark with the given name and returns the +" treenode for it. +function! s:Bookmark.GetNodeForName(name, searchFromAbsoluteRoot) + let bookmark = s:Bookmark.BookmarkFor(a:name) + return bookmark.getNode(a:searchFromAbsoluteRoot) +endfunction +" FUNCTION: Bookmark.GetSelected() {{{3 +" returns the Bookmark the cursor is over, or {} +function! s:Bookmark.GetSelected() + let line = getline(".") + let name = substitute(line, '^>\(.\{-}\) .\+$', '\1', '') + if name != line + try + return s:Bookmark.BookmarkFor(name) + catch /^NERDTree.BookmarkNotFoundError/ + return {} + endtry + endif + return {} +endfunction + +" Function: Bookmark.InvalidBookmarks() {{{3 +" Class method to get all invalid bookmark strings read from the bookmarks +" file +function! s:Bookmark.InvalidBookmarks() + if !exists("g:NERDTreeInvalidBookmarks") + let g:NERDTreeInvalidBookmarks = [] + endif + return g:NERDTreeInvalidBookmarks +endfunction +" FUNCTION: Bookmark.mustExist() {{{3 +function! s:Bookmark.mustExist() + if !self.path.exists() + call s:Bookmark.CacheBookmarks(1) + throw "NERDTree.BookmarkPointsToInvalidLocationError: the bookmark \"". + \ self.name ."\" points to a non existing location: \"". self.path.str() + endif +endfunction +" FUNCTION: Bookmark.New(name, path) {{{3 +" Create a new bookmark object with the given name and path object +function! s:Bookmark.New(name, path) + if a:name =~ ' ' + throw "NERDTree.IllegalBookmarkNameError: illegal name:" . a:name + endif + + let newBookmark = copy(self) + let newBookmark.name = a:name + let newBookmark.path = a:path + return newBookmark +endfunction +" FUNCTION: Bookmark.openInNewTab(options) {{{3 +" Create a new bookmark object with the given name and path object +function! s:Bookmark.openInNewTab(options) + let currentTab = tabpagenr() + if self.path.isDirectory + tabnew + call s:initNerdTree(self.name) + else + exec "tabedit " . bookmark.path.str({'format': 'Edit'}) + endif + + if has_key(a:options, 'stayInCurrentTab') + exec "tabnext " . currentTab + endif +endfunction +" Function: Bookmark.setPath(path) {{{3 +" makes this bookmark point to the given path +function! s:Bookmark.setPath(path) + let self.path = a:path +endfunction +" Function: Bookmark.Sort() {{{3 +" Class method that sorts all bookmarks +function! s:Bookmark.Sort() + let CompareFunc = function("s:compareBookmarks") + call sort(s:Bookmark.Bookmarks(), CompareFunc) +endfunction +" Function: Bookmark.str() {{{3 +" Get the string that should be rendered in the view for this bookmark +function! s:Bookmark.str() + let pathStrMaxLen = winwidth(s:getTreeWinNum()) - 4 - len(self.name) + if &nu + let pathStrMaxLen = pathStrMaxLen - &numberwidth + endif + + let pathStr = self.path.str({'format': 'UI'}) + if len(pathStr) > pathStrMaxLen + let pathStr = '<' . strpart(pathStr, len(pathStr) - pathStrMaxLen) + endif + return '>' . self.name . ' ' . pathStr +endfunction +" FUNCTION: Bookmark.toRoot() {{{3 +" Make the node for this bookmark the new tree root +function! s:Bookmark.toRoot() + if self.validate() + try + let targetNode = self.getNode(1) + catch /^NERDTree.BookmarkedNodeNotFoundError/ + let targetNode = s:TreeFileNode.New(s:Bookmark.BookmarkFor(self.name).path) + endtry + call targetNode.makeRoot() + call s:renderView() + call targetNode.putCursorHere(0, 0) + endif +endfunction +" FUNCTION: Bookmark.ToRoot(name) {{{3 +" Make the node for this bookmark the new tree root +function! s:Bookmark.ToRoot(name) + let bookmark = s:Bookmark.BookmarkFor(a:name) + call bookmark.toRoot() +endfunction + + +"FUNCTION: Bookmark.validate() {{{3 +function! s:Bookmark.validate() + if self.path.exists() + return 1 + else + call s:Bookmark.CacheBookmarks(1) + call s:renderView() + call s:echo(self.name . "now points to an invalid location. See :help NERDTreeInvalidBookmarks for info.") + return 0 + endif +endfunction + +" Function: Bookmark.Write() {{{3 +" Class method to write all bookmarks to the bookmarks file +function! s:Bookmark.Write() + let bookmarkStrings = [] + for i in s:Bookmark.Bookmarks() + call add(bookmarkStrings, i.name . ' ' . i.path.str()) + endfor + + "add a blank line before the invalid ones + call add(bookmarkStrings, "") + + for j in s:Bookmark.InvalidBookmarks() + call add(bookmarkStrings, j) + endfor + call writefile(bookmarkStrings, g:NERDTreeBookmarksFile) +endfunction +"CLASS: KeyMap {{{2 +"============================================================ +let s:KeyMap = {} +"FUNCTION: KeyMap.All() {{{3 +function! s:KeyMap.All() + if !exists("s:keyMaps") + let s:keyMaps = [] + endif + return s:keyMaps +endfunction + +"FUNCTION: KeyMap.BindAll() {{{3 +function! s:KeyMap.BindAll() + for i in s:KeyMap.All() + call i.bind() + endfor +endfunction + +"FUNCTION: KeyMap.bind() {{{3 +function! s:KeyMap.bind() + exec "nnoremap ". self.key ." :call ". self.callback ."()" +endfunction + +"FUNCTION: KeyMap.Create(options) {{{3 +function! s:KeyMap.Create(options) + let newKeyMap = copy(self) + let newKeyMap.key = a:options['key'] + let newKeyMap.quickhelpText = a:options['quickhelpText'] + let newKeyMap.callback = a:options['callback'] + call add(s:KeyMap.All(), newKeyMap) +endfunction +"CLASS: MenuController {{{2 +"============================================================ +let s:MenuController = {} +"FUNCTION: MenuController.New(menuItems) {{{3 +"create a new menu controller that operates on the given menu items +function! s:MenuController.New(menuItems) + let newMenuController = copy(self) + if a:menuItems[0].isSeparator() + let newMenuController.menuItems = a:menuItems[1:-1] + else + let newMenuController.menuItems = a:menuItems + endif + return newMenuController +endfunction + +"FUNCTION: MenuController.showMenu() {{{3 +"start the main loop of the menu and get the user to choose/execute a menu +"item +function! s:MenuController.showMenu() + call self._saveOptions() + + try + let self.selection = 0 + + let done = 0 + while !done + redraw! + call self._echoPrompt() + let key = nr2char(getchar()) + let done = self._handleKeypress(key) + endwhile + finally + call self._restoreOptions() + endtry + + if self.selection != -1 + let m = self._current() + call m.execute() + endif +endfunction + +"FUNCTION: MenuController._echoPrompt() {{{3 +function! s:MenuController._echoPrompt() + echo "NERDTree Menu. Use j/k/enter and the shortcuts indicated" + echo "==========================================================" + + for i in range(0, len(self.menuItems)-1) + if self.selection == i + echo "> " . self.menuItems[i].text + else + echo " " . self.menuItems[i].text + endif + endfor +endfunction + +"FUNCTION: MenuController._current(key) {{{3 +"get the MenuItem that is curently selected +function! s:MenuController._current() + return self.menuItems[self.selection] +endfunction + +"FUNCTION: MenuController._handleKeypress(key) {{{3 +"change the selection (if appropriate) and return 1 if the user has made +"their choice, 0 otherwise +function! s:MenuController._handleKeypress(key) + if a:key == 'j' + call self._cursorDown() + elseif a:key == 'k' + call self._cursorUp() + elseif a:key == nr2char(27) "escape + let self.selection = -1 + return 1 + elseif a:key == "\r" || a:key == "\n" "enter and ctrl-j + return 1 + else + let index = self._nextIndexFor(a:key) + if index != -1 + let self.selection = index + if len(self._allIndexesFor(a:key)) == 1 + return 1 + endif + endif + endif + + return 0 +endfunction + +"FUNCTION: MenuController._allIndexesFor(shortcut) {{{3 +"get indexes to all menu items with the given shortcut +function! s:MenuController._allIndexesFor(shortcut) + let toReturn = [] + + for i in range(0, len(self.menuItems)-1) + if self.menuItems[i].shortcut == a:shortcut + call add(toReturn, i) + endif + endfor + + return toReturn +endfunction + +"FUNCTION: MenuController._nextIndexFor(shortcut) {{{3 +"get the index to the next menu item with the given shortcut, starts from the +"current cursor location and wraps around to the top again if need be +function! s:MenuController._nextIndexFor(shortcut) + for i in range(self.selection+1, len(self.menuItems)-1) + if self.menuItems[i].shortcut == a:shortcut + return i + endif + endfor + + for i in range(0, self.selection) + if self.menuItems[i].shortcut == a:shortcut + return i + endif + endfor + + return -1 +endfunction + +"FUNCTION: MenuController._setCmdheight() {{{3 +"sets &cmdheight to whatever is needed to display the menu +function! s:MenuController._setCmdheight() + let &cmdheight = len(self.menuItems) + 3 +endfunction + +"FUNCTION: MenuController._saveOptions() {{{3 +"set any vim options that are required to make the menu work (saving their old +"values) +function! s:MenuController._saveOptions() + let self._oldLazyredraw = &lazyredraw + let self._oldCmdheight = &cmdheight + set nolazyredraw + call self._setCmdheight() +endfunction + +"FUNCTION: MenuController._restoreOptions() {{{3 +"restore the options we saved in _saveOptions() +function! s:MenuController._restoreOptions() + let &cmdheight = self._oldCmdheight + let &lazyredraw = self._oldLazyredraw +endfunction + +"FUNCTION: MenuController._cursorDown() {{{3 +"move the cursor to the next menu item, skipping separators +function! s:MenuController._cursorDown() + let done = 0 + while !done + if self.selection < len(self.menuItems)-1 + let self.selection += 1 + else + let self.selection = 0 + endif + + if !self._current().isSeparator() + let done = 1 + endif + endwhile +endfunction + +"FUNCTION: MenuController._cursorUp() {{{3 +"move the cursor to the previous menu item, skipping separators +function! s:MenuController._cursorUp() + let done = 0 + while !done + if self.selection > 0 + let self.selection -= 1 + else + let self.selection = len(self.menuItems)-1 + endif + + if !self._current().isSeparator() + let done = 1 + endif + endwhile +endfunction + +"CLASS: MenuItem {{{2 +"============================================================ +let s:MenuItem = {} +"FUNCTION: MenuItem.All() {{{3 +"get all top level menu items +function! s:MenuItem.All() + if !exists("s:menuItems") + let s:menuItems = [] + endif + return s:menuItems +endfunction + +"FUNCTION: MenuItem.AllEnabled() {{{3 +"get all top level menu items that are currently enabled +function! s:MenuItem.AllEnabled() + let toReturn = [] + for i in s:MenuItem.All() + if i.enabled() + call add(toReturn, i) + endif + endfor + return toReturn +endfunction + +"FUNCTION: MenuItem.Create(options) {{{3 +"make a new menu item and add it to the global list +function! s:MenuItem.Create(options) + let newMenuItem = copy(self) + + let newMenuItem.text = a:options['text'] + let newMenuItem.shortcut = a:options['shortcut'] + let newMenuItem.children = [] + + let newMenuItem.isActiveCallback = -1 + if has_key(a:options, 'isActiveCallback') + let newMenuItem.isActiveCallback = a:options['isActiveCallback'] + endif + + let newMenuItem.callback = -1 + if has_key(a:options, 'callback') + let newMenuItem.callback = a:options['callback'] + endif + + if has_key(a:options, 'parent') + call add(a:options['parent'].children, newMenuItem) + else + call add(s:MenuItem.All(), newMenuItem) + endif + + return newMenuItem +endfunction + +"FUNCTION: MenuItem.CreateSeparator(options) {{{3 +"make a new separator menu item and add it to the global list +function! s:MenuItem.CreateSeparator(options) + let standard_options = { 'text': '--------------------', + \ 'shortcut': -1, + \ 'callback': -1 } + let options = extend(a:options, standard_options, "force") + + return s:MenuItem.Create(options) +endfunction + +"FUNCTION: MenuItem.CreateSubmenu(options) {{{3 +"make a new submenu and add it to global list +function! s:MenuItem.CreateSubmenu(options) + let standard_options = { 'callback': -1 } + let options = extend(a:options, standard_options, "force") + + return s:MenuItem.Create(options) +endfunction + +"FUNCTION: MenuItem.enabled() {{{3 +"return 1 if this menu item should be displayed +" +"delegates off to the isActiveCallback, and defaults to 1 if no callback was +"specified +function! s:MenuItem.enabled() + if self.isActiveCallback != -1 + return {self.isActiveCallback}() + endif + return 1 +endfunction + +"FUNCTION: MenuItem.execute() {{{3 +"perform the action behind this menu item, if this menuitem has children then +"display a new menu for them, otherwise deletegate off to the menuitem's +"callback +function! s:MenuItem.execute() + if len(self.children) + let mc = s:MenuController.New(self.children) + call mc.showMenu() + else + if self.callback != -1 + call {self.callback}() + endif + endif +endfunction + +"FUNCTION: MenuItem.isSeparator() {{{3 +"return 1 if this menuitem is a separator +function! s:MenuItem.isSeparator() + return self.callback == -1 && self.children == [] +endfunction + +"FUNCTION: MenuItem.isSubmenu() {{{3 +"return 1 if this menuitem is a submenu +function! s:MenuItem.isSubmenu() + return self.callback == -1 && !empty(self.children) +endfunction + +"CLASS: TreeFileNode {{{2 +"This class is the parent of the TreeDirNode class and constitures the +"'Component' part of the composite design pattern between the treenode +"classes. +"============================================================ +let s:TreeFileNode = {} +"FUNCTION: TreeFileNode.activate(forceKeepWinOpen) {{{3 +function! s:TreeFileNode.activate(forceKeepWinOpen) + call self.open() + if !a:forceKeepWinOpen + call s:closeTreeIfQuitOnOpen() + end +endfunction +"FUNCTION: TreeFileNode.bookmark(name) {{{3 +"bookmark this node with a:name +function! s:TreeFileNode.bookmark(name) + try + let oldMarkedNode = s:Bookmark.GetNodeForName(a:name, 1) + call oldMarkedNode.path.cacheDisplayString() + catch /^NERDTree.BookmarkNotFoundError/ + endtry + + call s:Bookmark.AddBookmark(a:name, self.path) + call self.path.cacheDisplayString() + call s:Bookmark.Write() +endfunction +"FUNCTION: TreeFileNode.cacheParent() {{{3 +"initializes self.parent if it isnt already +function! s:TreeFileNode.cacheParent() + if empty(self.parent) + let parentPath = self.path.getParent() + if parentPath.equals(self.path) + throw "NERDTree.CannotCacheParentError: already at root" + endif + let self.parent = s:TreeFileNode.New(parentPath) + endif +endfunction +"FUNCTION: TreeFileNode.compareNodes {{{3 +"This is supposed to be a class level method but i cant figure out how to +"get func refs to work from a dict.. +" +"A class level method that compares two nodes +" +"Args: +"n1, n2: the 2 nodes to compare +function! s:compareNodes(n1, n2) + return a:n1.path.compareTo(a:n2.path) +endfunction + +"FUNCTION: TreeFileNode.clearBoomarks() {{{3 +function! s:TreeFileNode.clearBoomarks() + for i in s:Bookmark.Bookmarks() + if i.path.equals(self.path) + call i.delete() + end + endfor + call self.path.cacheDisplayString() +endfunction +"FUNCTION: TreeFileNode.copy(dest) {{{3 +function! s:TreeFileNode.copy(dest) + call self.path.copy(a:dest) + let newPath = s:Path.New(a:dest) + let parent = b:NERDTreeRoot.findNode(newPath.getParent()) + if !empty(parent) + call parent.refresh() + endif + return parent.findNode(newPath) +endfunction + +"FUNCTION: TreeFileNode.delete {{{3 +"Removes this node from the tree and calls the Delete method for its path obj +function! s:TreeFileNode.delete() + call self.path.delete() + call self.parent.removeChild(self) +endfunction + +"FUNCTION: TreeFileNode.displayString() {{{3 +" +"Returns a string that specifies how the node should be represented as a +"string +" +"Return: +"a string that can be used in the view to represent this node +function! s:TreeFileNode.displayString() + return self.path.displayString() +endfunction + +"FUNCTION: TreeFileNode.equals(treenode) {{{3 +" +"Compares this treenode to the input treenode and returns 1 if they are the +"same node. +" +"Use this method instead of == because sometimes when the treenodes contain +"many children, vim seg faults when doing == +" +"Args: +"treenode: the other treenode to compare to +function! s:TreeFileNode.equals(treenode) + return self.path.str() ==# a:treenode.path.str() +endfunction + +"FUNCTION: TreeFileNode.findNode(path) {{{3 +"Returns self if this node.path.Equals the given path. +"Returns {} if not equal. +" +"Args: +"path: the path object to compare against +function! s:TreeFileNode.findNode(path) + if a:path.equals(self.path) + return self + endif + return {} +endfunction +"FUNCTION: TreeFileNode.findOpenDirSiblingWithVisibleChildren(direction) {{{3 +" +"Finds the next sibling for this node in the indicated direction. This sibling +"must be a directory and may/may not have children as specified. +" +"Args: +"direction: 0 if you want to find the previous sibling, 1 for the next sibling +" +"Return: +"a treenode object or {} if no appropriate sibling could be found +function! s:TreeFileNode.findOpenDirSiblingWithVisibleChildren(direction) + "if we have no parent then we can have no siblings + if self.parent != {} + let nextSibling = self.findSibling(a:direction) + + while nextSibling != {} + if nextSibling.path.isDirectory && nextSibling.hasVisibleChildren() && nextSibling.isOpen + return nextSibling + endif + let nextSibling = nextSibling.findSibling(a:direction) + endwhile + endif + + return {} +endfunction +"FUNCTION: TreeFileNode.findSibling(direction) {{{3 +" +"Finds the next sibling for this node in the indicated direction +" +"Args: +"direction: 0 if you want to find the previous sibling, 1 for the next sibling +" +"Return: +"a treenode object or {} if no sibling could be found +function! s:TreeFileNode.findSibling(direction) + "if we have no parent then we can have no siblings + if self.parent != {} + + "get the index of this node in its parents children + let siblingIndx = self.parent.getChildIndex(self.path) + + if siblingIndx != -1 + "move a long to the next potential sibling node + let siblingIndx = a:direction ==# 1 ? siblingIndx+1 : siblingIndx-1 + + "keep moving along to the next sibling till we find one that is valid + let numSiblings = self.parent.getChildCount() + while siblingIndx >= 0 && siblingIndx < numSiblings + + "if the next node is not an ignored node (i.e. wont show up in the + "view) then return it + if self.parent.children[siblingIndx].path.ignore() ==# 0 + return self.parent.children[siblingIndx] + endif + + "go to next node + let siblingIndx = a:direction ==# 1 ? siblingIndx+1 : siblingIndx-1 + endwhile + endif + endif + + return {} +endfunction + +"FUNCTION: TreeFileNode.getLineNum(){{{3 +"returns the line number this node is rendered on, or -1 if it isnt rendered +function! s:TreeFileNode.getLineNum() + "if the node is the root then return the root line no. + if self.isRoot() + return s:TreeFileNode.GetRootLineNum() + endif + + let totalLines = line("$") + + "the path components we have matched so far + let pathcomponents = [substitute(b:NERDTreeRoot.path.str({'format': 'UI'}), '/ *$', '', '')] + "the index of the component we are searching for + let curPathComponent = 1 + + let fullpath = self.path.str({'format': 'UI'}) + + + let lnum = s:TreeFileNode.GetRootLineNum() + while lnum > 0 + let lnum = lnum + 1 + "have we reached the bottom of the tree? + if lnum ==# totalLines+1 + return -1 + endif + + let curLine = getline(lnum) + + let indent = s:indentLevelFor(curLine) + if indent ==# curPathComponent + let curLine = s:stripMarkupFromLine(curLine, 1) + + let curPath = join(pathcomponents, '/') . '/' . curLine + if stridx(fullpath, curPath, 0) ==# 0 + if fullpath ==# curPath || strpart(fullpath, len(curPath)-1,1) ==# '/' + let curLine = substitute(curLine, '/ *$', '', '') + call add(pathcomponents, curLine) + let curPathComponent = curPathComponent + 1 + + if fullpath ==# curPath + return lnum + endif + endif + endif + endif + endwhile + return -1 +endfunction + +"FUNCTION: TreeFileNode.GetRootForTab(){{{3 +"get the root node for this tab +function! s:TreeFileNode.GetRootForTab() + if s:treeExistsForTab() + return getbufvar(t:NERDTreeBufName, 'NERDTreeRoot') + end + return {} +endfunction +"FUNCTION: TreeFileNode.GetRootLineNum(){{{3 +"gets the line number of the root node +function! s:TreeFileNode.GetRootLineNum() + let rootLine = 1 + while getline(rootLine) !~ '^\(/\|<\)' + let rootLine = rootLine + 1 + endwhile + return rootLine +endfunction + +"FUNCTION: TreeFileNode.GetSelected() {{{3 +"gets the treenode that the cursor is currently over +function! s:TreeFileNode.GetSelected() + try + let path = s:getPath(line(".")) + if path ==# {} + return {} + endif + return b:NERDTreeRoot.findNode(path) + catch /NERDTree/ + return {} + endtry +endfunction +"FUNCTION: TreeFileNode.isVisible() {{{3 +"returns 1 if this node should be visible according to the tree filters and +"hidden file filters (and their on/off status) +function! s:TreeFileNode.isVisible() + return !self.path.ignore() +endfunction +"FUNCTION: TreeFileNode.isRoot() {{{3 +"returns 1 if this node is b:NERDTreeRoot +function! s:TreeFileNode.isRoot() + if !s:treeExistsForBuf() + throw "NERDTree.NoTreeError: No tree exists for the current buffer" + endif + + return self.equals(b:NERDTreeRoot) +endfunction + +"FUNCTION: TreeFileNode.makeRoot() {{{3 +"Make this node the root of the tree +function! s:TreeFileNode.makeRoot() + if self.path.isDirectory + let b:NERDTreeRoot = self + else + call self.cacheParent() + let b:NERDTreeRoot = self.parent + endif + + call b:NERDTreeRoot.open() + + "change dir to the dir of the new root if instructed to + if g:NERDTreeChDirMode ==# 2 + exec "cd " . b:NERDTreeRoot.path.str({'format': 'Edit'}) + endif +endfunction +"FUNCTION: TreeFileNode.New(path) {{{3 +"Returns a new TreeNode object with the given path and parent +" +"Args: +"path: a path object representing the full filesystem path to the file/dir that the node represents +function! s:TreeFileNode.New(path) + if a:path.isDirectory + return s:TreeDirNode.New(a:path) + else + let newTreeNode = copy(self) + let newTreeNode.path = a:path + let newTreeNode.parent = {} + return newTreeNode + endif +endfunction + +"FUNCTION: TreeFileNode.open() {{{3 +"Open the file represented by the given node in the current window, splitting +"the window if needed +" +"ARGS: +"treenode: file node to open +function! s:TreeFileNode.open() + if b:NERDTreeType ==# "secondary" + exec 'edit ' . self.path.str({'format': 'Edit'}) + return + endif + + "if the file is already open in this tab then just stick the cursor in it + let winnr = bufwinnr('^' . self.path.str() . '$') + if winnr != -1 + call s:exec(winnr . "wincmd w") + + else + if !s:isWindowUsable(winnr("#")) && s:firstUsableWindow() ==# -1 + call self.openSplit() + else + try + if !s:isWindowUsable(winnr("#")) + call s:exec(s:firstUsableWindow() . "wincmd w") + else + call s:exec('wincmd p') + endif + exec ("edit " . self.path.str({'format': 'Edit'})) + catch /^Vim\%((\a\+)\)\=:E37/ + call s:putCursorInTreeWin() + throw "NERDTree.FileAlreadyOpenAndModifiedError: ". self.path.str() ." is already open and modified." + catch /^Vim\%((\a\+)\)\=:/ + echo v:exception + endtry + endif + endif +endfunction +"FUNCTION: TreeFileNode.openSplit() {{{3 +"Open this node in a new window +function! s:TreeFileNode.openSplit() + + if b:NERDTreeType ==# "secondary" + exec "split " . self.path.str({'format': 'Edit'}) + return + endif + + " Save the user's settings for splitbelow and splitright + let savesplitbelow=&splitbelow + let savesplitright=&splitright + + " 'there' will be set to a command to move from the split window + " back to the explorer window + " + " 'back' will be set to a command to move from the explorer window + " back to the newly split window + " + " 'right' and 'below' will be set to the settings needed for + " splitbelow and splitright IF the explorer is the only window. + " + let there= g:NERDTreeWinPos ==# "left" ? "wincmd h" : "wincmd l" + let back = g:NERDTreeWinPos ==# "left" ? "wincmd l" : "wincmd h" + let right= g:NERDTreeWinPos ==# "left" + let below=0 + + " Attempt to go to adjacent window + call s:exec(back) + + let onlyOneWin = (winnr("$") ==# 1) + + " If no adjacent window, set splitright and splitbelow appropriately + if onlyOneWin + let &splitright=right + let &splitbelow=below + else + " found adjacent window - invert split direction + let &splitright=!right + let &splitbelow=!below + endif + + let splitMode = onlyOneWin ? "vertical" : "" + + " Open the new window + try + exec(splitMode." sp " . self.path.str({'format': 'Edit'})) + catch /^Vim\%((\a\+)\)\=:E37/ + call s:putCursorInTreeWin() + throw "NERDTree.FileAlreadyOpenAndModifiedError: ". self.path.str() ." is already open and modified." + catch /^Vim\%((\a\+)\)\=:/ + "do nothing + endtry + + "resize the tree window if no other window was open before + if onlyOneWin + let size = exists("b:NERDTreeOldWindowSize") ? b:NERDTreeOldWindowSize : g:NERDTreeWinSize + call s:exec(there) + exec("silent ". splitMode ." resize ". size) + call s:exec('wincmd p') + endif + + " Restore splitmode settings + let &splitbelow=savesplitbelow + let &splitright=savesplitright +endfunction +"FUNCTION: TreeFileNode.openVSplit() {{{3 +"Open this node in a new vertical window +function! s:TreeFileNode.openVSplit() + if b:NERDTreeType ==# "secondary" + exec "vnew " . self.path.str({'format': 'Edit'}) + return + endif + + let winwidth = winwidth(".") + if winnr("$")==#1 + let winwidth = g:NERDTreeWinSize + endif + + call s:exec("wincmd p") + exec "vnew " . self.path.str({'format': 'Edit'}) + + "resize the nerd tree back to the original size + call s:putCursorInTreeWin() + exec("silent vertical resize ". winwidth) + call s:exec('wincmd p') +endfunction +"FUNCTION: TreeFileNode.openInNewTab(options) {{{3 +function! s:TreeFileNode.openInNewTab(options) + let currentTab = tabpagenr() + + if !has_key(a:options, 'keepTreeOpen') + call s:closeTreeIfQuitOnOpen() + endif + + exec "tabedit " . self.path.str({'format': 'Edit'}) + + if has_key(a:options, 'stayInCurrentTab') && a:options['stayInCurrentTab'] + exec "tabnext " . currentTab + endif + +endfunction +"FUNCTION: TreeFileNode.putCursorHere(isJump, recurseUpward){{{3 +"Places the cursor on the line number this node is rendered on +" +"Args: +"isJump: 1 if this cursor movement should be counted as a jump by vim +"recurseUpward: try to put the cursor on the parent if the this node isnt +"visible +function! s:TreeFileNode.putCursorHere(isJump, recurseUpward) + let ln = self.getLineNum() + if ln != -1 + if a:isJump + mark ' + endif + call cursor(ln, col(".")) + else + if a:recurseUpward + let node = self + while node != {} && node.getLineNum() ==# -1 + let node = node.parent + call node.open() + endwhile + call s:renderView() + call node.putCursorHere(a:isJump, 0) + endif + endif +endfunction + +"FUNCTION: TreeFileNode.refresh() {{{3 +function! s:TreeFileNode.refresh() + call self.path.refresh() +endfunction +"FUNCTION: TreeFileNode.rename() {{{3 +"Calls the rename method for this nodes path obj +function! s:TreeFileNode.rename(newName) + let newName = substitute(a:newName, '\(\\\|\/\)$', '', '') + call self.path.rename(newName) + call self.parent.removeChild(self) + + let parentPath = self.path.getParent() + let newParent = b:NERDTreeRoot.findNode(parentPath) + + if newParent != {} + call newParent.createChild(self.path, 1) + call newParent.refresh() + endif +endfunction +"FUNCTION: TreeFileNode.renderToString {{{3 +"returns a string representation for this tree to be rendered in the view +function! s:TreeFileNode.renderToString() + return self._renderToString(0, 0, [], self.getChildCount() ==# 1) +endfunction + + +"Args: +"depth: the current depth in the tree for this call +"drawText: 1 if we should actually draw the line for this node (if 0 then the +"child nodes are rendered only) +"vertMap: a binary array that indicates whether a vertical bar should be draw +"for each depth in the tree +"isLastChild:true if this curNode is the last child of its parent +function! s:TreeFileNode._renderToString(depth, drawText, vertMap, isLastChild) + let output = "" + if a:drawText ==# 1 + + let treeParts = '' + + "get all the leading spaces and vertical tree parts for this line + if a:depth > 1 + for j in a:vertMap[0:-2] + if j ==# 1 + let treeParts = treeParts . '| ' + else + let treeParts = treeParts . ' ' + endif + endfor + endif + + "get the last vertical tree part for this line which will be different + "if this node is the last child of its parent + if a:isLastChild + let treeParts = treeParts . '`' + else + let treeParts = treeParts . '|' + endif + + + "smack the appropriate dir/file symbol on the line before the file/dir + "name itself + if self.path.isDirectory + if self.isOpen + let treeParts = treeParts . '~' + else + let treeParts = treeParts . '+' + endif + else + let treeParts = treeParts . '-' + endif + let line = treeParts . self.displayString() + + let output = output . line . "\n" + endif + + "if the node is an open dir, draw its children + if self.path.isDirectory ==# 1 && self.isOpen ==# 1 + + let childNodesToDraw = self.getVisibleChildren() + if len(childNodesToDraw) > 0 + + "draw all the nodes children except the last + let lastIndx = len(childNodesToDraw)-1 + if lastIndx > 0 + for i in childNodesToDraw[0:lastIndx-1] + let output = output . i._renderToString(a:depth + 1, 1, add(copy(a:vertMap), 1), 0) + endfor + endif + + "draw the last child, indicating that it IS the last + let output = output . childNodesToDraw[lastIndx]._renderToString(a:depth + 1, 1, add(copy(a:vertMap), 0), 1) + endif + endif + + return output +endfunction +"CLASS: TreeDirNode {{{2 +"This class is a child of the TreeFileNode class and constitutes the +"'Composite' part of the composite design pattern between the treenode +"classes. +"============================================================ +let s:TreeDirNode = copy(s:TreeFileNode) +"FUNCTION: TreeDirNode.AbsoluteTreeRoot(){{{3 +"class method that returns the highest cached ancestor of the current root +function! s:TreeDirNode.AbsoluteTreeRoot() + let currentNode = b:NERDTreeRoot + while currentNode.parent != {} + let currentNode = currentNode.parent + endwhile + return currentNode +endfunction +"FUNCTION: TreeDirNode.activate(forceKeepWinOpen) {{{3 +unlet s:TreeDirNode.activate +function! s:TreeDirNode.activate(forceKeepWinOpen) + call self.toggleOpen() + call s:renderView() + call self.putCursorHere(0, 0) +endfunction +"FUNCTION: TreeDirNode.addChild(treenode, inOrder) {{{3 +"Adds the given treenode to the list of children for this node +" +"Args: +"-treenode: the node to add +"-inOrder: 1 if the new node should be inserted in sorted order +function! s:TreeDirNode.addChild(treenode, inOrder) + call add(self.children, a:treenode) + let a:treenode.parent = self + + if a:inOrder + call self.sortChildren() + endif +endfunction + +"FUNCTION: TreeDirNode.close() {{{3 +"Closes this directory +function! s:TreeDirNode.close() + let self.isOpen = 0 +endfunction + +"FUNCTION: TreeDirNode.closeChildren() {{{3 +"Closes all the child dir nodes of this node +function! s:TreeDirNode.closeChildren() + for i in self.children + if i.path.isDirectory + call i.close() + call i.closeChildren() + endif + endfor +endfunction + +"FUNCTION: TreeDirNode.createChild(path, inOrder) {{{3 +"Instantiates a new child node for this node with the given path. The new +"nodes parent is set to this node. +" +"Args: +"path: a Path object that this node will represent/contain +"inOrder: 1 if the new node should be inserted in sorted order +" +"Returns: +"the newly created node +function! s:TreeDirNode.createChild(path, inOrder) + let newTreeNode = s:TreeFileNode.New(a:path) + call self.addChild(newTreeNode, a:inOrder) + return newTreeNode +endfunction + +"FUNCTION: TreeDirNode.findNode(path) {{{3 +"Will find one of the children (recursively) that has the given path +" +"Args: +"path: a path object +unlet s:TreeDirNode.findNode +function! s:TreeDirNode.findNode(path) + if a:path.equals(self.path) + return self + endif + if stridx(a:path.str(), self.path.str(), 0) ==# -1 + return {} + endif + + if self.path.isDirectory + for i in self.children + let retVal = i.findNode(a:path) + if retVal != {} + return retVal + endif + endfor + endif + return {} +endfunction +"FUNCTION: TreeDirNode.getChildCount() {{{3 +"Returns the number of children this node has +function! s:TreeDirNode.getChildCount() + return len(self.children) +endfunction + +"FUNCTION: TreeDirNode.getChild(path) {{{3 +"Returns child node of this node that has the given path or {} if no such node +"exists. +" +"This function doesnt not recurse into child dir nodes +" +"Args: +"path: a path object +function! s:TreeDirNode.getChild(path) + if stridx(a:path.str(), self.path.str(), 0) ==# -1 + return {} + endif + + let index = self.getChildIndex(a:path) + if index ==# -1 + return {} + else + return self.children[index] + endif + +endfunction + +"FUNCTION: TreeDirNode.getChildByIndex(indx, visible) {{{3 +"returns the child at the given index +"Args: +"indx: the index to get the child from +"visible: 1 if only the visible children array should be used, 0 if all the +"children should be searched. +function! s:TreeDirNode.getChildByIndex(indx, visible) + let array_to_search = a:visible? self.getVisibleChildren() : self.children + if a:indx > len(array_to_search) + throw "NERDTree.InvalidArgumentsError: Index is out of bounds." + endif + return array_to_search[a:indx] +endfunction + +"FUNCTION: TreeDirNode.getChildIndex(path) {{{3 +"Returns the index of the child node of this node that has the given path or +"-1 if no such node exists. +" +"This function doesnt not recurse into child dir nodes +" +"Args: +"path: a path object +function! s:TreeDirNode.getChildIndex(path) + if stridx(a:path.str(), self.path.str(), 0) ==# -1 + return -1 + endif + + "do a binary search for the child + let a = 0 + let z = self.getChildCount() + while a < z + let mid = (a+z)/2 + let diff = a:path.compareTo(self.children[mid].path) + + if diff ==# -1 + let z = mid + elseif diff ==# 1 + let a = mid+1 + else + return mid + endif + endwhile + return -1 +endfunction + +"FUNCTION: TreeDirNode.GetSelected() {{{3 +"Returns the current node if it is a dir node, or else returns the current +"nodes parent +unlet s:TreeDirNode.GetSelected +function! s:TreeDirNode.GetSelected() + let currentDir = s:TreeFileNode.GetSelected() + if currentDir != {} && !currentDir.isRoot() + if currentDir.path.isDirectory ==# 0 + let currentDir = currentDir.parent + endif + endif + return currentDir +endfunction +"FUNCTION: TreeDirNode.getVisibleChildCount() {{{3 +"Returns the number of visible children this node has +function! s:TreeDirNode.getVisibleChildCount() + return len(self.getVisibleChildren()) +endfunction + +"FUNCTION: TreeDirNode.getVisibleChildren() {{{3 +"Returns a list of children to display for this node, in the correct order +" +"Return: +"an array of treenodes +function! s:TreeDirNode.getVisibleChildren() + let toReturn = [] + for i in self.children + if i.path.ignore() ==# 0 + call add(toReturn, i) + endif + endfor + return toReturn +endfunction + +"FUNCTION: TreeDirNode.hasVisibleChildren() {{{3 +"returns 1 if this node has any childre, 0 otherwise.. +function! s:TreeDirNode.hasVisibleChildren() + return self.getVisibleChildCount() != 0 +endfunction + +"FUNCTION: TreeDirNode._initChildren() {{{3 +"Removes all childen from this node and re-reads them +" +"Args: +"silent: 1 if the function should not echo any "please wait" messages for +"large directories +" +"Return: the number of child nodes read +function! s:TreeDirNode._initChildren(silent) + "remove all the current child nodes + let self.children = [] + + "get an array of all the files in the nodes dir + let dir = self.path + let globDir = dir.str({'format': 'Glob'}) + let filesStr = globpath(globDir, '*') . "\n" . globpath(globDir, '.*') + let files = split(filesStr, "\n") + + if !a:silent && len(files) > g:NERDTreeNotificationThreshold + call s:echo("Please wait, caching a large dir ...") + endif + + let invalidFilesFound = 0 + for i in files + + "filter out the .. and . directories + "Note: we must match .. AND ../ cos sometimes the globpath returns + "../ for path with strange chars (eg $) + if i !~ '\/\.\.\/\?$' && i !~ '\/\.\/\?$' + + "put the next file in a new node and attach it + try + let path = s:Path.New(i) + call self.createChild(path, 0) + catch /^NERDTree.\(InvalidArguments\|InvalidFiletype\)Error/ + let invalidFilesFound += 1 + endtry + endif + endfor + + call self.sortChildren() + + if !a:silent && len(files) > g:NERDTreeNotificationThreshold + call s:echo("Please wait, caching a large dir ... DONE (". self.getChildCount() ." nodes cached).") + endif + + if invalidFilesFound + call s:echoWarning(invalidFilesFound . " file(s) could not be loaded into the NERD tree") + endif + return self.getChildCount() +endfunction +"FUNCTION: TreeDirNode.New(path) {{{3 +"Returns a new TreeNode object with the given path and parent +" +"Args: +"path: a path object representing the full filesystem path to the file/dir that the node represents +unlet s:TreeDirNode.New +function! s:TreeDirNode.New(path) + if a:path.isDirectory != 1 + throw "NERDTree.InvalidArgumentsError: A TreeDirNode object must be instantiated with a directory Path object." + endif + + let newTreeNode = copy(self) + let newTreeNode.path = a:path + + let newTreeNode.isOpen = 0 + let newTreeNode.children = [] + + let newTreeNode.parent = {} + + return newTreeNode +endfunction +"FUNCTION: TreeDirNode.open() {{{3 +"Reads in all this nodes children +" +"Return: the number of child nodes read +unlet s:TreeDirNode.open +function! s:TreeDirNode.open() + let self.isOpen = 1 + if self.children ==# [] + return self._initChildren(0) + else + return 0 + endif +endfunction + +" FUNCTION: TreeDirNode.openExplorer() {{{3 +" opens an explorer window for this node in the previous window (could be a +" nerd tree or a netrw) +function! s:TreeDirNode.openExplorer() + let oldwin = winnr() + call s:exec('wincmd p') + if oldwin ==# winnr() || (&modified && s:bufInWindows(winbufnr(winnr())) < 2) + call s:exec('wincmd p') + call self.openSplit() + else + exec ("silent edit " . self.path.str({'format': 'Edit'})) + endif +endfunction +"FUNCTION: TreeDirNode.openInNewTab(options) {{{3 +unlet s:TreeDirNode.openInNewTab +function! s:TreeDirNode.openInNewTab(options) + let currentTab = tabpagenr() + + if !has_key(a:options, 'keepTreeOpen') || !a:options['keepTreeOpen'] + call s:closeTreeIfQuitOnOpen() + endif + + tabnew + call s:initNerdTree(self.path.str()) + + if has_key(a:options, 'stayInCurrentTab') && a:options['stayInCurrentTab'] + exec "tabnext " . currentTab + endif +endfunction +"FUNCTION: TreeDirNode.openRecursively() {{{3 +"Opens this treenode and all of its children whose paths arent 'ignored' +"because of the file filters. +" +"This method is actually a wrapper for the OpenRecursively2 method which does +"the work. +function! s:TreeDirNode.openRecursively() + call self._openRecursively2(1) +endfunction + +"FUNCTION: TreeDirNode._openRecursively2() {{{3 +"Opens this all children of this treenode recursively if either: +" *they arent filtered by file filters +" *a:forceOpen is 1 +" +"Args: +"forceOpen: 1 if this node should be opened regardless of file filters +function! s:TreeDirNode._openRecursively2(forceOpen) + if self.path.ignore() ==# 0 || a:forceOpen + let self.isOpen = 1 + if self.children ==# [] + call self._initChildren(1) + endif + + for i in self.children + if i.path.isDirectory ==# 1 + call i._openRecursively2(0) + endif + endfor + endif +endfunction + +"FUNCTION: TreeDirNode.refresh() {{{3 +unlet s:TreeDirNode.refresh +function! s:TreeDirNode.refresh() + call self.path.refresh() + + "if this node was ever opened, refresh its children + if self.isOpen || !empty(self.children) + "go thru all the files/dirs under this node + let newChildNodes = [] + let invalidFilesFound = 0 + let dir = self.path + let globDir = dir.str({'format': 'Glob'}) + let filesStr = globpath(globDir, '*') . "\n" . globpath(globDir, '.*') + let files = split(filesStr, "\n") + for i in files + "filter out the .. and . directories + "Note: we must match .. AND ../ cos sometimes the globpath returns + "../ for path with strange chars (eg $) + if i !~ '\/\.\.\/\?$' && i !~ '\/\.\/\?$' + + try + "create a new path and see if it exists in this nodes children + let path = s:Path.New(i) + let newNode = self.getChild(path) + if newNode != {} + call newNode.refresh() + call add(newChildNodes, newNode) + + "the node doesnt exist so create it + else + let newNode = s:TreeFileNode.New(path) + let newNode.parent = self + call add(newChildNodes, newNode) + endif + + + catch /^NERDTree.InvalidArgumentsError/ + let invalidFilesFound = 1 + endtry + endif + endfor + + "swap this nodes children out for the children we just read/refreshed + let self.children = newChildNodes + call self.sortChildren() + + if invalidFilesFound + call s:echoWarning("some files could not be loaded into the NERD tree") + endif + endif +endfunction + +"FUNCTION: TreeDirNode.reveal(path) {{{3 +"reveal the given path, i.e. cache and open all treenodes needed to display it +"in the UI +function! s:TreeDirNode.reveal(path) + if !a:path.isUnder(self.path) + throw "NERDTree.InvalidArgumentsError: " . a:path.str() . " should be under " . self.path.str() + endif + + call self.open() + + if self.path.equals(a:path.getParent()) + let n = self.findNode(a:path) + call s:renderView() + call n.putCursorHere(1,0) + return + endif + + let p = a:path + while !p.getParent().equals(self.path) + let p = p.getParent() + endwhile + + let n = self.findNode(p) + call n.reveal(a:path) +endfunction +"FUNCTION: TreeDirNode.removeChild(treenode) {{{3 +" +"Removes the given treenode from this nodes set of children +" +"Args: +"treenode: the node to remove +" +"Throws a NERDTree.ChildNotFoundError if the given treenode is not found +function! s:TreeDirNode.removeChild(treenode) + for i in range(0, self.getChildCount()-1) + if self.children[i].equals(a:treenode) + call remove(self.children, i) + return + endif + endfor + + throw "NERDTree.ChildNotFoundError: child node was not found" +endfunction + +"FUNCTION: TreeDirNode.sortChildren() {{{3 +" +"Sorts the children of this node according to alphabetical order and the +"directory priority. +" +function! s:TreeDirNode.sortChildren() + let CompareFunc = function("s:compareNodes") + call sort(self.children, CompareFunc) +endfunction + +"FUNCTION: TreeDirNode.toggleOpen() {{{3 +"Opens this directory if it is closed and vice versa +function! s:TreeDirNode.toggleOpen() + if self.isOpen ==# 1 + call self.close() + else + call self.open() + endif +endfunction + +"FUNCTION: TreeDirNode.transplantChild(newNode) {{{3 +"Replaces the child of this with the given node (where the child node's full +"path matches a:newNode's fullpath). The search for the matching node is +"non-recursive +" +"Arg: +"newNode: the node to graft into the tree +function! s:TreeDirNode.transplantChild(newNode) + for i in range(0, self.getChildCount()-1) + if self.children[i].equals(a:newNode) + let self.children[i] = a:newNode + let a:newNode.parent = self + break + endif + endfor +endfunction +"============================================================ +"CLASS: Path {{{2 +"============================================================ +let s:Path = {} +"FUNCTION: Path.AbsolutePathFor(str) {{{3 +function! s:Path.AbsolutePathFor(str) + let prependCWD = 0 + if s:running_windows + let prependCWD = a:str !~ '^.:\(\\\|\/\)' + else + let prependCWD = a:str !~ '^/' + endif + + let toReturn = a:str + if prependCWD + let toReturn = getcwd() . s:Path.Slash() . a:str + endif + + return toReturn +endfunction +"FUNCTION: Path.bookmarkNames() {{{3 +function! s:Path.bookmarkNames() + if !exists("self._bookmarkNames") + call self.cacheDisplayString() + endif + return self._bookmarkNames +endfunction +"FUNCTION: Path.cacheDisplayString() {{{3 +function! s:Path.cacheDisplayString() + let self.cachedDisplayString = self.getLastPathComponent(1) + + if self.isExecutable + let self.cachedDisplayString = self.cachedDisplayString . '*' + endif + + let self._bookmarkNames = [] + for i in s:Bookmark.Bookmarks() + if i.path.equals(self) + call add(self._bookmarkNames, i.name) + endif + endfor + if !empty(self._bookmarkNames) + let self.cachedDisplayString .= ' {' . join(self._bookmarkNames) . '}' + endif + + if self.isSymLink + let self.cachedDisplayString .= ' -> ' . self.symLinkDest + endif + + if self.isReadOnly + let self.cachedDisplayString .= ' [RO]' + endif +endfunction +"FUNCTION: Path.changeToDir() {{{3 +function! s:Path.changeToDir() + let dir = self.str({'format': 'Cd'}) + if self.isDirectory ==# 0 + let dir = self.getParent().str({'format': 'Cd'}) + endif + + try + execute "cd " . dir + call s:echo("CWD is now: " . getcwd()) + catch + throw "NERDTree.PathChangeError: cannot change CWD to " . dir + endtry +endfunction + +"FUNCTION: Path.compareTo() {{{3 +" +"Compares this Path to the given path and returns 0 if they are equal, -1 if +"this Path is "less than" the given path, or 1 if it is "greater". +" +"Args: +"path: the path object to compare this to +" +"Return: +"1, -1 or 0 +function! s:Path.compareTo(path) + let thisPath = self.getLastPathComponent(1) + let thatPath = a:path.getLastPathComponent(1) + + "if the paths are the same then clearly we return 0 + if thisPath ==# thatPath + return 0 + endif + + let thisSS = self.getSortOrderIndex() + let thatSS = a:path.getSortOrderIndex() + + "compare the sort sequences, if they are different then the return + "value is easy + if thisSS < thatSS + return -1 + elseif thisSS > thatSS + return 1 + else + "if the sort sequences are the same then compare the paths + "alphabetically + let pathCompare = g:NERDTreeCaseSensitiveSort ? thisPath <# thatPath : thisPath limit + let toReturn = "<" . strpart(toReturn, len(toReturn) - limit + 1) + endif + endif + + return toReturn +endfunction + +"FUNCTION: Path._strForUI() {{{3 +function! s:Path._strForUI() + let toReturn = '/' . join(self.pathSegments, '/') + if self.isDirectory && toReturn != '/' + let toReturn = toReturn . '/' + endif + return toReturn +endfunction + +"FUNCTION: Path._strForCd() {{{3 +" +" returns a string that can be used with :cd +function! s:Path._strForCd() + return escape(self.str(), s:escape_chars) +endfunction +"FUNCTION: Path._strForEdit() {{{3 +" +"Return: the string for this path that is suitable to be used with the :edit +"command +function! s:Path._strForEdit() + let p = self.str({'format': 'UI'}) + let cwd = getcwd() + + if s:running_windows + let p = tolower(self.str()) + let cwd = tolower(getcwd()) + endif + + let p = escape(p, s:escape_chars) + + let cwd = cwd . s:Path.Slash() + + "return a relative path if we can + if stridx(p, cwd) ==# 0 + let p = strpart(p, strlen(cwd)) + endif + + if p ==# '' + let p = '.' + endif + + return p + +endfunction +"FUNCTION: Path._strForGlob() {{{3 +function! s:Path._strForGlob() + let lead = s:Path.Slash() + + "if we are running windows then slap a drive letter on the front + if s:running_windows + let lead = self.drive . '\' + endif + + let toReturn = lead . join(self.pathSegments, s:Path.Slash()) + + if !s:running_windows + let toReturn = escape(toReturn, s:escape_chars) + endif + return toReturn +endfunction +"FUNCTION: Path._str() {{{3 +" +"Gets the string path for this path object that is appropriate for the OS. +"EG, in windows c:\foo\bar +" in *nix /foo/bar +function! s:Path._str() + let lead = s:Path.Slash() + + "if we are running windows then slap a drive letter on the front + if s:running_windows + let lead = self.drive . '\' + endif + + return lead . join(self.pathSegments, s:Path.Slash()) +endfunction + +"FUNCTION: Path.strTrunk() {{{3 +"Gets the path without the last segment on the end. +function! s:Path.strTrunk() + return self.drive . '/' . join(self.pathSegments[0:-2], '/') +endfunction + +"FUNCTION: Path.WinToUnixPath(pathstr){{{3 +"Takes in a windows path and returns the unix equiv +" +"A class level method +" +"Args: +"pathstr: the windows path to convert +function! s:Path.WinToUnixPath(pathstr) + if !s:running_windows + return a:pathstr + endif + + let toReturn = a:pathstr + + "remove the x:\ of the front + let toReturn = substitute(toReturn, '^.*:\(\\\|/\)\?', '/', "") + + "convert all \ chars to / + let toReturn = substitute(toReturn, '\', '/', "g") + + return toReturn +endfunction + +" SECTION: General Functions {{{1 +"============================================================ +"FUNCTION: s:bufInWindows(bnum){{{2 +"[[STOLEN FROM VTREEEXPLORER.VIM]] +"Determine the number of windows open to this buffer number. +"Care of Yegappan Lakshman. Thanks! +" +"Args: +"bnum: the subject buffers buffer number +function! s:bufInWindows(bnum) + let cnt = 0 + let winnum = 1 + while 1 + let bufnum = winbufnr(winnum) + if bufnum < 0 + break + endif + if bufnum ==# a:bnum + let cnt = cnt + 1 + endif + let winnum = winnum + 1 + endwhile + + return cnt +endfunction " >>> +"FUNCTION: s:checkForBrowse(dir) {{{2 +"inits a secondary nerd tree in the current buffer if appropriate +function! s:checkForBrowse(dir) + if a:dir != '' && isdirectory(a:dir) + call s:initNerdTreeInPlace(a:dir) + endif +endfunction +"FUNCTION: s:compareBookmarks(first, second) {{{2 +"Compares two bookmarks +function! s:compareBookmarks(first, second) + return a:first.compareTo(a:second) +endfunction + +" FUNCTION: s:completeBookmarks(A,L,P) {{{2 +" completion function for the bookmark commands +function! s:completeBookmarks(A,L,P) + return filter(s:Bookmark.BookmarkNames(), 'v:val =~ "^' . a:A . '"') +endfunction +" FUNCTION: s:exec(cmd) {{{2 +" same as :exec cmd but eventignore=all is set for the duration +function! s:exec(cmd) + let old_ei = &ei + set ei=all + exec a:cmd + let &ei = old_ei +endfunction +" FUNCTION: s:findAndRevealPath() {{{2 +function! s:findAndRevealPath() + try + let p = s:Path.New(expand("%")) + catch /^NERDTree.InvalidArgumentsError/ + call s:echo("no file for the current buffer") + return + endtry + + if !s:treeExistsForTab() + call s:initNerdTree(p.getParent().str()) + else + if !p.isUnder(s:TreeFileNode.GetRootForTab().path) + call s:initNerdTree(p.getParent().str()) + else + if !s:isTreeOpen() + call s:toggle("") + endif + endif + endif + call s:putCursorInTreeWin() + call b:NERDTreeRoot.reveal(p) +endfunction +"FUNCTION: s:initNerdTree(name) {{{2 +"Initialise the nerd tree for this tab. The tree will start in either the +"given directory, or the directory associated with the given bookmark +" +"Args: +"name: the name of a bookmark or a directory +function! s:initNerdTree(name) + let path = {} + if s:Bookmark.BookmarkExistsFor(a:name) + let path = s:Bookmark.BookmarkFor(a:name).path + else + let dir = a:name ==# '' ? getcwd() : a:name + + "hack to get an absolute path if a relative path is given + if dir =~ '^\.' + let dir = getcwd() . s:Path.Slash() . dir + endif + let dir = resolve(dir) + + try + let path = s:Path.New(dir) + catch /^NERDTree.InvalidArgumentsError/ + call s:echo("No bookmark or directory found for: " . a:name) + return + endtry + endif + if !path.isDirectory + let path = path.getParent() + endif + + "if instructed to, then change the vim CWD to the dir the NERDTree is + "inited in + if g:NERDTreeChDirMode != 0 + call path.changeToDir() + endif + + if s:treeExistsForTab() + if s:isTreeOpen() + call s:closeTree() + endif + unlet t:NERDTreeBufName + endif + + let newRoot = s:TreeDirNode.New(path) + call newRoot.open() + + call s:createTreeWin() + let b:treeShowHelp = 0 + let b:NERDTreeIgnoreEnabled = 1 + let b:NERDTreeShowFiles = g:NERDTreeShowFiles + let b:NERDTreeShowHidden = g:NERDTreeShowHidden + let b:NERDTreeShowBookmarks = g:NERDTreeShowBookmarks + let b:NERDTreeRoot = newRoot + + let b:NERDTreeType = "primary" + + call s:renderView() + call b:NERDTreeRoot.putCursorHere(0, 0) +endfunction + +"FUNCTION: s:initNerdTreeInPlace(dir) {{{2 +function! s:initNerdTreeInPlace(dir) + try + let path = s:Path.New(a:dir) + catch /^NERDTree.InvalidArgumentsError/ + call s:echo("Invalid directory name:" . a:name) + return + endtry + + "we want the directory buffer to disappear when we do the :edit below + setlocal bufhidden=wipe + + let previousBuf = expand("#") + + "we need a unique name for each secondary tree buffer to ensure they are + "all independent + exec "silent edit " . s:nextBufferName() + + let b:NERDTreePreviousBuf = bufnr(previousBuf) + + let b:NERDTreeRoot = s:TreeDirNode.New(path) + call b:NERDTreeRoot.open() + + "throwaway buffer options + setlocal noswapfile + setlocal buftype=nofile + setlocal bufhidden=hide + setlocal nowrap + setlocal foldcolumn=0 + setlocal nobuflisted + setlocal nospell + if g:NERDTreeShowLineNumbers + setlocal nu + else + setlocal nonu + endif + + iabc + + if g:NERDTreeHighlightCursorline + setlocal cursorline + endif + + call s:setupStatusline() + + let b:treeShowHelp = 0 + let b:NERDTreeIgnoreEnabled = 1 + let b:NERDTreeShowFiles = g:NERDTreeShowFiles + let b:NERDTreeShowHidden = g:NERDTreeShowHidden + let b:NERDTreeShowBookmarks = g:NERDTreeShowBookmarks + + let b:NERDTreeType = "secondary" + + call s:bindMappings() + setfiletype nerdtree + " syntax highlighting + if has("syntax") && exists("g:syntax_on") + call s:setupSyntaxHighlighting() + endif + + call s:renderView() +endfunction +" FUNCTION: s:initNerdTreeMirror() {{{2 +function! s:initNerdTreeMirror() + + "get the names off all the nerd tree buffers + let treeBufNames = [] + for i in range(1, tabpagenr("$")) + let nextName = s:tabpagevar(i, 'NERDTreeBufName') + if nextName != -1 && (!exists("t:NERDTreeBufName") || nextName != t:NERDTreeBufName) + call add(treeBufNames, nextName) + endif + endfor + let treeBufNames = s:unique(treeBufNames) + + "map the option names (that the user will be prompted with) to the nerd + "tree buffer names + let options = {} + let i = 0 + while i < len(treeBufNames) + let bufName = treeBufNames[i] + let treeRoot = getbufvar(bufName, "NERDTreeRoot") + let options[i+1 . '. ' . treeRoot.path.str() . ' (buf name: ' . bufName . ')'] = bufName + let i = i + 1 + endwhile + + "work out which tree to mirror, if there is more than 1 then ask the user + let bufferName = '' + if len(keys(options)) > 1 + let choices = ["Choose a tree to mirror"] + let choices = extend(choices, sort(keys(options))) + let choice = inputlist(choices) + if choice < 1 || choice > len(options) || choice ==# '' + return + endif + + let bufferName = options[sort(keys(options))[choice-1]] + elseif len(keys(options)) ==# 1 + let bufferName = values(options)[0] + else + call s:echo("No trees to mirror") + return + endif + + if s:treeExistsForTab() && s:isTreeOpen() + call s:closeTree() + endif + + let t:NERDTreeBufName = bufferName + call s:createTreeWin() + exec 'buffer ' . bufferName + if !&hidden + call s:renderView() + endif +endfunction +" FUNCTION: s:nextBufferName() {{{2 +" returns the buffer name for the next nerd tree +function! s:nextBufferName() + let name = s:NERDTreeBufName . s:next_buffer_number + let s:next_buffer_number += 1 + return name +endfunction +" FUNCTION: s:tabpagevar(tabnr, var) {{{2 +function! s:tabpagevar(tabnr, var) + let currentTab = tabpagenr() + let old_ei = &ei + set ei=all + + exec "tabnext " . a:tabnr + let v = -1 + if exists('t:' . a:var) + exec 'let v = t:' . a:var + endif + exec "tabnext " . currentTab + + let &ei = old_ei + + return v +endfunction +" Function: s:treeExistsForBuffer() {{{2 +" Returns 1 if a nerd tree root exists in the current buffer +function! s:treeExistsForBuf() + return exists("b:NERDTreeRoot") +endfunction +" Function: s:treeExistsForTab() {{{2 +" Returns 1 if a nerd tree root exists in the current tab +function! s:treeExistsForTab() + return exists("t:NERDTreeBufName") +endfunction +" Function: s:unique(list) {{{2 +" returns a:list without duplicates +function! s:unique(list) + let uniqlist = [] + for elem in a:list + if index(uniqlist, elem) ==# -1 + let uniqlist += [elem] + endif + endfor + return uniqlist +endfunction +" SECTION: Public API {{{1 +"============================================================ +let g:NERDTreePath = s:Path +let g:NERDTreeDirNode = s:TreeDirNode +let g:NERDTreeFileNode = s:TreeFileNode +let g:NERDTreeBookmark = s:Bookmark + +function! NERDTreeAddMenuItem(options) + call s:MenuItem.Create(a:options) +endfunction + +function! NERDTreeAddMenuSeparator(...) + let opts = a:0 ? a:1 : {} + call s:MenuItem.CreateSeparator(opts) +endfunction + +function! NERDTreeAddSubmenu(options) + return s:MenuItem.Create(a:options) +endfunction + +function! NERDTreeAddKeyMap(options) + call s:KeyMap.Create(a:options) +endfunction + +function! NERDTreeRender() + call s:renderView() +endfunction + +" SECTION: View Functions {{{1 +"============================================================ +"FUNCTION: s:centerView() {{{2 +"centers the nerd tree window around the cursor (provided the nerd tree +"options permit) +function! s:centerView() + if g:NERDTreeAutoCenter + let current_line = winline() + let lines_to_top = current_line + let lines_to_bottom = winheight(s:getTreeWinNum()) - current_line + if lines_to_top < g:NERDTreeAutoCenterThreshold || lines_to_bottom < g:NERDTreeAutoCenterThreshold + normal! zz + endif + endif +endfunction +"FUNCTION: s:closeTree() {{{2 +"Closes the primary NERD tree window for this tab +function! s:closeTree() + if !s:isTreeOpen() + throw "NERDTree.NoTreeFoundError: no NERDTree is open" + endif + + if winnr("$") != 1 + call s:exec(s:getTreeWinNum() . " wincmd w") + close + call s:exec("wincmd p") + else + close + endif +endfunction + +"FUNCTION: s:closeTreeIfOpen() {{{2 +"Closes the NERD tree window if it is open +function! s:closeTreeIfOpen() + if s:isTreeOpen() + call s:closeTree() + endif +endfunction +"FUNCTION: s:closeTreeIfQuitOnOpen() {{{2 +"Closes the NERD tree window if the close on open option is set +function! s:closeTreeIfQuitOnOpen() + if g:NERDTreeQuitOnOpen && s:isTreeOpen() + call s:closeTree() + endif +endfunction +"FUNCTION: s:createTreeWin() {{{2 +"Inits the NERD tree window. ie. opens it, sizes it, sets all the local +"options etc +function! s:createTreeWin() + "create the nerd tree window + let splitLocation = g:NERDTreeWinPos ==# "left" ? "topleft " : "botright " + let splitSize = g:NERDTreeWinSize + + if !exists('t:NERDTreeBufName') + let t:NERDTreeBufName = s:nextBufferName() + silent! exec splitLocation . 'vertical ' . splitSize . ' new' + silent! exec "edit " . t:NERDTreeBufName + else + silent! exec splitLocation . 'vertical ' . splitSize . ' split' + silent! exec "buffer " . t:NERDTreeBufName + endif + + setlocal winfixwidth + + "throwaway buffer options + setlocal noswapfile + setlocal buftype=nofile + setlocal nowrap + setlocal foldcolumn=0 + setlocal nobuflisted + setlocal nospell + if g:NERDTreeShowLineNumbers + setlocal nu + else + setlocal nonu + endif + + iabc + + if g:NERDTreeHighlightCursorline + setlocal cursorline + endif + + call s:setupStatusline() + + call s:bindMappings() + setfiletype nerdtree + " syntax highlighting + if has("syntax") && exists("g:syntax_on") + call s:setupSyntaxHighlighting() + endif +endfunction + +"FUNCTION: s:dumpHelp {{{2 +"prints out the quick help +function! s:dumpHelp() + let old_h = @h + if b:treeShowHelp ==# 1 + let @h= "\" NERD tree (" . s:NERD_tree_version . ") quickhelp~\n" + let @h=@h."\" ============================\n" + let @h=@h."\" File node mappings~\n" + let @h=@h."\" ". (g:NERDTreeMouseMode ==# 3 ? "single" : "double") ."-click,\n" + let @h=@h."\" ,\n" + if b:NERDTreeType ==# "primary" + let @h=@h."\" ". g:NERDTreeMapActivateNode .": open in prev window\n" + else + let @h=@h."\" ". g:NERDTreeMapActivateNode .": open in current window\n" + endif + if b:NERDTreeType ==# "primary" + let @h=@h."\" ". g:NERDTreeMapPreview .": preview\n" + endif + let @h=@h."\" ". g:NERDTreeMapOpenInTab.": open in new tab\n" + let @h=@h."\" ". g:NERDTreeMapOpenInTabSilent .": open in new tab silently\n" + let @h=@h."\" middle-click,\n" + let @h=@h."\" ". g:NERDTreeMapOpenSplit .": open split\n" + let @h=@h."\" ". g:NERDTreeMapPreviewSplit .": preview split\n" + let @h=@h."\" ". g:NERDTreeMapOpenVSplit .": open vsplit\n" + let @h=@h."\" ". g:NERDTreeMapPreviewVSplit .": preview vsplit\n" + + let @h=@h."\"\n\" ----------------------------\n" + let @h=@h."\" Directory node mappings~\n" + let @h=@h."\" ". (g:NERDTreeMouseMode ==# 1 ? "double" : "single") ."-click,\n" + let @h=@h."\" ". g:NERDTreeMapActivateNode .": open & close node\n" + let @h=@h."\" ". g:NERDTreeMapOpenRecursively .": recursively open node\n" + let @h=@h."\" ". g:NERDTreeMapCloseDir .": close parent of node\n" + let @h=@h."\" ". g:NERDTreeMapCloseChildren .": close all child nodes of\n" + let @h=@h."\" current node recursively\n" + let @h=@h."\" middle-click,\n" + let @h=@h."\" ". g:NERDTreeMapOpenExpl.": explore selected dir\n" + + let @h=@h."\"\n\" ----------------------------\n" + let @h=@h."\" Bookmark table mappings~\n" + let @h=@h."\" double-click,\n" + let @h=@h."\" ". g:NERDTreeMapActivateNode .": open bookmark\n" + let @h=@h."\" ". g:NERDTreeMapOpenInTab.": open in new tab\n" + let @h=@h."\" ". g:NERDTreeMapOpenInTabSilent .": open in new tab silently\n" + let @h=@h."\" ". g:NERDTreeMapDeleteBookmark .": delete bookmark\n" + + let @h=@h."\"\n\" ----------------------------\n" + let @h=@h."\" Tree navigation mappings~\n" + let @h=@h."\" ". g:NERDTreeMapJumpRoot .": go to root\n" + let @h=@h."\" ". g:NERDTreeMapJumpParent .": go to parent\n" + let @h=@h."\" ". g:NERDTreeMapJumpFirstChild .": go to first child\n" + let @h=@h."\" ". g:NERDTreeMapJumpLastChild .": go to last child\n" + let @h=@h."\" ". g:NERDTreeMapJumpNextSibling .": go to next sibling\n" + let @h=@h."\" ". g:NERDTreeMapJumpPrevSibling .": go to prev sibling\n" + + let @h=@h."\"\n\" ----------------------------\n" + let @h=@h."\" Filesystem mappings~\n" + let @h=@h."\" ". g:NERDTreeMapChangeRoot .": change tree root to the\n" + let @h=@h."\" selected dir\n" + let @h=@h."\" ". g:NERDTreeMapUpdir .": move tree root up a dir\n" + let @h=@h."\" ". g:NERDTreeMapUpdirKeepOpen .": move tree root up a dir\n" + let @h=@h."\" but leave old root open\n" + let @h=@h."\" ". g:NERDTreeMapRefresh .": refresh cursor dir\n" + let @h=@h."\" ". g:NERDTreeMapRefreshRoot .": refresh current root\n" + let @h=@h."\" ". g:NERDTreeMapMenu .": Show menu\n" + let @h=@h."\" ". g:NERDTreeMapChdir .":change the CWD to the\n" + let @h=@h."\" selected dir\n" + + let @h=@h."\"\n\" ----------------------------\n" + let @h=@h."\" Tree filtering mappings~\n" + let @h=@h."\" ". g:NERDTreeMapToggleHidden .": hidden files (" . (b:NERDTreeShowHidden ? "on" : "off") . ")\n" + let @h=@h."\" ". g:NERDTreeMapToggleFilters .": file filters (" . (b:NERDTreeIgnoreEnabled ? "on" : "off") . ")\n" + let @h=@h."\" ". g:NERDTreeMapToggleFiles .": files (" . (b:NERDTreeShowFiles ? "on" : "off") . ")\n" + let @h=@h."\" ". g:NERDTreeMapToggleBookmarks .": bookmarks (" . (b:NERDTreeShowBookmarks ? "on" : "off") . ")\n" + + "add quickhelp entries for each custom key map + if len(s:KeyMap.All()) + let @h=@h."\"\n\" ----------------------------\n" + let @h=@h."\" Custom mappings~\n" + for i in s:KeyMap.All() + let @h=@h."\" ". i.key .": ". i.quickhelpText ."\n" + endfor + endif + + let @h=@h."\"\n\" ----------------------------\n" + let @h=@h."\" Other mappings~\n" + let @h=@h."\" ". g:NERDTreeMapQuit .": Close the NERDTree window\n" + let @h=@h."\" ". g:NERDTreeMapToggleZoom .": Zoom (maximize-minimize)\n" + let @h=@h."\" the NERDTree window\n" + let @h=@h."\" ". g:NERDTreeMapHelp .": toggle help\n" + let @h=@h."\"\n\" ----------------------------\n" + let @h=@h."\" Bookmark commands~\n" + let @h=@h."\" :Bookmark \n" + let @h=@h."\" :BookmarkToRoot \n" + let @h=@h."\" :RevealBookmark \n" + let @h=@h."\" :OpenBookmark \n" + let @h=@h."\" :ClearBookmarks []\n" + let @h=@h."\" :ClearAllBookmarks\n" + else + let @h="\" Press ". g:NERDTreeMapHelp ." for help\n" + endif + + silent! put h + + let @h = old_h +endfunction +"FUNCTION: s:echo {{{2 +"A wrapper for :echo. Appends 'NERDTree:' on the front of all messages +" +"Args: +"msg: the message to echo +function! s:echo(msg) + redraw + echomsg "NERDTree: " . a:msg +endfunction +"FUNCTION: s:echoWarning {{{2 +"Wrapper for s:echo, sets the message type to warningmsg for this message +"Args: +"msg: the message to echo +function! s:echoWarning(msg) + echohl warningmsg + call s:echo(a:msg) + echohl normal +endfunction +"FUNCTION: s:echoError {{{2 +"Wrapper for s:echo, sets the message type to errormsg for this message +"Args: +"msg: the message to echo +function! s:echoError(msg) + echohl errormsg + call s:echo(a:msg) + echohl normal +endfunction +"FUNCTION: s:firstUsableWindow(){{{2 +"find the window number of the first normal window +function! s:firstUsableWindow() + let i = 1 + while i <= winnr("$") + let bnum = winbufnr(i) + if bnum != -1 && getbufvar(bnum, '&buftype') ==# '' + \ && !getwinvar(i, '&previewwindow') + \ && (!getbufvar(bnum, '&modified') || &hidden) + return i + endif + + let i += 1 + endwhile + return -1 +endfunction +"FUNCTION: s:getPath(ln) {{{2 +"Gets the full path to the node that is rendered on the given line number +" +"Args: +"ln: the line number to get the path for +" +"Return: +"A path if a node was selected, {} if nothing is selected. +"If the 'up a dir' line was selected then the path to the parent of the +"current root is returned +function! s:getPath(ln) + let line = getline(a:ln) + + let rootLine = s:TreeFileNode.GetRootLineNum() + + "check to see if we have the root node + if a:ln == rootLine + return b:NERDTreeRoot.path + endif + + " in case called from outside the tree + if line !~ '^ *[|`]' || line =~ '^$' + return {} + endif + + if line ==# s:tree_up_dir_line + return b:NERDTreeRoot.path.getParent() + endif + + let indent = s:indentLevelFor(line) + + "remove the tree parts and the leading space + let curFile = s:stripMarkupFromLine(line, 0) + + let wasdir = 0 + if curFile =~ '/$' + let wasdir = 1 + let curFile = substitute(curFile, '/\?$', '/', "") + endif + + let dir = "" + let lnum = a:ln + while lnum > 0 + let lnum = lnum - 1 + let curLine = getline(lnum) + let curLineStripped = s:stripMarkupFromLine(curLine, 1) + + "have we reached the top of the tree? + if lnum == rootLine + let dir = b:NERDTreeRoot.path.str({'format': 'UI'}) . dir + break + endif + if curLineStripped =~ '/$' + let lpindent = s:indentLevelFor(curLine) + if lpindent < indent + let indent = indent - 1 + + let dir = substitute (curLineStripped,'^\\', "", "") . dir + continue + endif + endif + endwhile + let curFile = b:NERDTreeRoot.path.drive . dir . curFile + let toReturn = s:Path.New(curFile) + return toReturn +endfunction + +"FUNCTION: s:getTreeWinNum() {{{2 +"gets the nerd tree window number for this tab +function! s:getTreeWinNum() + if exists("t:NERDTreeBufName") + return bufwinnr(t:NERDTreeBufName) + else + return -1 + endif +endfunction +"FUNCTION: s:indentLevelFor(line) {{{2 +function! s:indentLevelFor(line) + return match(a:line, '[^ \-+~`|]') / s:tree_wid +endfunction +"FUNCTION: s:isTreeOpen() {{{2 +function! s:isTreeOpen() + return s:getTreeWinNum() != -1 +endfunction +"FUNCTION: s:isWindowUsable(winnumber) {{{2 +"Returns 0 if opening a file from the tree in the given window requires it to +"be split, 1 otherwise +" +"Args: +"winnumber: the number of the window in question +function! s:isWindowUsable(winnumber) + "gotta split if theres only one window (i.e. the NERD tree) + if winnr("$") ==# 1 + return 0 + endif + + let oldwinnr = winnr() + call s:exec(a:winnumber . "wincmd p") + let specialWindow = getbufvar("%", '&buftype') != '' || getwinvar('%', '&previewwindow') + let modified = &modified + call s:exec(oldwinnr . "wincmd p") + + "if its a special window e.g. quickfix or another explorer plugin then we + "have to split + if specialWindow + return 0 + endif + + if &hidden + return 1 + endif + + return !modified || s:bufInWindows(winbufnr(a:winnumber)) >= 2 +endfunction + +" FUNCTION: s:jumpToChild(direction) {{{2 +" Args: +" direction: 0 if going to first child, 1 if going to last +function! s:jumpToChild(direction) + let currentNode = s:TreeFileNode.GetSelected() + if currentNode ==# {} || currentNode.isRoot() + call s:echo("cannot jump to " . (a:direction ? "last" : "first") . " child") + return + end + let dirNode = currentNode.parent + let childNodes = dirNode.getVisibleChildren() + + let targetNode = childNodes[0] + if a:direction + let targetNode = childNodes[len(childNodes) - 1] + endif + + if targetNode.equals(currentNode) + let siblingDir = currentNode.parent.findOpenDirSiblingWithVisibleChildren(a:direction) + if siblingDir != {} + let indx = a:direction ? siblingDir.getVisibleChildCount()-1 : 0 + let targetNode = siblingDir.getChildByIndex(indx, 1) + endif + endif + + call targetNode.putCursorHere(1, 0) + + call s:centerView() +endfunction + + +"FUNCTION: s:promptToDelBuffer(bufnum, msg){{{2 +"prints out the given msg and, if the user responds by pushing 'y' then the +"buffer with the given bufnum is deleted +" +"Args: +"bufnum: the buffer that may be deleted +"msg: a message that will be echoed to the user asking them if they wish to +" del the buffer +function! s:promptToDelBuffer(bufnum, msg) + echo a:msg + if nr2char(getchar()) ==# 'y' + exec "silent bdelete! " . a:bufnum + endif +endfunction + +"FUNCTION: s:putCursorOnBookmarkTable(){{{2 +"Places the cursor at the top of the bookmarks table +function! s:putCursorOnBookmarkTable() + if !b:NERDTreeShowBookmarks + throw "NERDTree.IllegalOperationError: cant find bookmark table, bookmarks arent active" + endif + + let rootNodeLine = s:TreeFileNode.GetRootLineNum() + + let line = 1 + while getline(line) !~ '^>-\+Bookmarks-\+$' + let line = line + 1 + if line >= rootNodeLine + throw "NERDTree.BookmarkTableNotFoundError: didnt find the bookmarks table" + endif + endwhile + call cursor(line, 0) +endfunction + +"FUNCTION: s:putCursorInTreeWin(){{{2 +"Places the cursor in the nerd tree window +function! s:putCursorInTreeWin() + if !s:isTreeOpen() + throw "NERDTree.InvalidOperationError: cant put cursor in NERD tree window, no window exists" + endif + + call s:exec(s:getTreeWinNum() . "wincmd w") +endfunction + +"FUNCTION: s:renderBookmarks {{{2 +function! s:renderBookmarks() + + call setline(line(".")+1, ">----------Bookmarks----------") + call cursor(line(".")+1, col(".")) + + for i in s:Bookmark.Bookmarks() + call setline(line(".")+1, i.str()) + call cursor(line(".")+1, col(".")) + endfor + + call setline(line(".")+1, '') + call cursor(line(".")+1, col(".")) +endfunction +"FUNCTION: s:renderView {{{2 +"The entry function for rendering the tree +function! s:renderView() + setlocal modifiable + + "remember the top line of the buffer and the current line so we can + "restore the view exactly how it was + let curLine = line(".") + let curCol = col(".") + let topLine = line("w0") + + "delete all lines in the buffer (being careful not to clobber a register) + silent 1,$delete _ + + call s:dumpHelp() + + "delete the blank line before the help and add one after it + call setline(line(".")+1, "") + call cursor(line(".")+1, col(".")) + + if b:NERDTreeShowBookmarks + call s:renderBookmarks() + endif + + "add the 'up a dir' line + call setline(line(".")+1, s:tree_up_dir_line) + call cursor(line(".")+1, col(".")) + + "draw the header line + let header = b:NERDTreeRoot.path.str({'format': 'UI', 'truncateTo': winwidth(0)}) + call setline(line(".")+1, header) + call cursor(line(".")+1, col(".")) + + "draw the tree + let old_o = @o + let @o = b:NERDTreeRoot.renderToString() + silent put o + let @o = old_o + + "delete the blank line at the top of the buffer + silent 1,1delete _ + + "restore the view + let old_scrolloff=&scrolloff + let &scrolloff=0 + call cursor(topLine, 1) + normal! zt + call cursor(curLine, curCol) + let &scrolloff = old_scrolloff + + setlocal nomodifiable +endfunction + +"FUNCTION: s:renderViewSavingPosition {{{2 +"Renders the tree and ensures the cursor stays on the current node or the +"current nodes parent if it is no longer available upon re-rendering +function! s:renderViewSavingPosition() + let currentNode = s:TreeFileNode.GetSelected() + + "go up the tree till we find a node that will be visible or till we run + "out of nodes + while currentNode != {} && !currentNode.isVisible() && !currentNode.isRoot() + let currentNode = currentNode.parent + endwhile + + call s:renderView() + + if currentNode != {} + call currentNode.putCursorHere(0, 0) + endif +endfunction +"FUNCTION: s:restoreScreenState() {{{2 +" +"Sets the screen state back to what it was when s:saveScreenState was last +"called. +" +"Assumes the cursor is in the NERDTree window +function! s:restoreScreenState() + if !exists("b:NERDTreeOldTopLine") || !exists("b:NERDTreeOldPos") || !exists("b:NERDTreeOldWindowSize") + return + endif + exec("silent vertical resize ".b:NERDTreeOldWindowSize) + + let old_scrolloff=&scrolloff + let &scrolloff=0 + call cursor(b:NERDTreeOldTopLine, 0) + normal! zt + call setpos(".", b:NERDTreeOldPos) + let &scrolloff=old_scrolloff +endfunction + +"FUNCTION: s:saveScreenState() {{{2 +"Saves the current cursor position in the current buffer and the window +"scroll position +function! s:saveScreenState() + let win = winnr() + try + call s:putCursorInTreeWin() + let b:NERDTreeOldPos = getpos(".") + let b:NERDTreeOldTopLine = line("w0") + let b:NERDTreeOldWindowSize = winwidth("") + call s:exec(win . "wincmd w") + catch /^NERDTree.InvalidOperationError/ + endtry +endfunction + +"FUNCTION: s:setupStatusline() {{{2 +function! s:setupStatusline() + if g:NERDTreeStatusline != -1 + let &l:statusline = g:NERDTreeStatusline + endif +endfunction +"FUNCTION: s:setupSyntaxHighlighting() {{{2 +function! s:setupSyntaxHighlighting() + "treeFlags are syntax items that should be invisible, but give clues as to + "how things should be highlighted + syn match treeFlag #\~# + syn match treeFlag #\[RO\]# + + "highlighting for the .. (up dir) line at the top of the tree + execute "syn match treeUp #". s:tree_up_dir_line ."#" + + "highlighting for the ~/+ symbols for the directory nodes + syn match treeClosable #\~\<# + syn match treeClosable #\~\.# + syn match treeOpenable #+\<# + syn match treeOpenable #+\.#he=e-1 + + "highlighting for the tree structural parts + syn match treePart #|# + syn match treePart #`# + syn match treePartFile #[|`]-#hs=s+1 contains=treePart + + "quickhelp syntax elements + syn match treeHelpKey #" \{1,2\}[^ ]*:#hs=s+2,he=e-1 + syn match treeHelpKey #" \{1,2\}[^ ]*,#hs=s+2,he=e-1 + syn match treeHelpTitle #" .*\~#hs=s+2,he=e-1 contains=treeFlag + syn match treeToggleOn #".*(on)#hs=e-2,he=e-1 contains=treeHelpKey + syn match treeToggleOff #".*(off)#hs=e-3,he=e-1 contains=treeHelpKey + syn match treeHelpCommand #" :.\{-}\>#hs=s+3 + syn match treeHelp #^".*# contains=treeHelpKey,treeHelpTitle,treeFlag,treeToggleOff,treeToggleOn,treeHelpCommand + + "highlighting for readonly files + syn match treeRO #.*\[RO\]#hs=s+2 contains=treeFlag,treeBookmark,treePart,treePartFile + + "highlighting for sym links + syn match treeLink #[^-| `].* -> # contains=treeBookmark,treeOpenable,treeClosable,treeDirSlash + + "highlighing for directory nodes and file nodes + syn match treeDirSlash #/# + syn match treeDir #[^-| `].*/# contains=treeLink,treeDirSlash,treeOpenable,treeClosable + syn match treeExecFile #[|`]-.*\*\($\| \)# contains=treeLink,treePart,treeRO,treePartFile,treeBookmark + syn match treeFile #|-.*# contains=treeLink,treePart,treeRO,treePartFile,treeBookmark,treeExecFile + syn match treeFile #`-.*# contains=treeLink,treePart,treeRO,treePartFile,treeBookmark,treeExecFile + syn match treeCWD #^/.*$# + + "highlighting for bookmarks + syn match treeBookmark # {.*}#hs=s+1 + + "highlighting for the bookmarks table + syn match treeBookmarksLeader #^># + syn match treeBookmarksHeader #^>-\+Bookmarks-\+$# contains=treeBookmarksLeader + syn match treeBookmarkName #^>.\{-} #he=e-1 contains=treeBookmarksLeader + syn match treeBookmark #^>.*$# contains=treeBookmarksLeader,treeBookmarkName,treeBookmarksHeader + + if g:NERDChristmasTree + hi def link treePart Special + hi def link treePartFile Type + hi def link treeFile Normal + hi def link treeExecFile Title + hi def link treeDirSlash Identifier + hi def link treeClosable Type + else + hi def link treePart Normal + hi def link treePartFile Normal + hi def link treeFile Normal + hi def link treeClosable Title + endif + + hi def link treeBookmarksHeader statement + hi def link treeBookmarksLeader ignore + hi def link treeBookmarkName Identifier + hi def link treeBookmark normal + + hi def link treeHelp String + hi def link treeHelpKey Identifier + hi def link treeHelpCommand Identifier + hi def link treeHelpTitle Macro + hi def link treeToggleOn Question + hi def link treeToggleOff WarningMsg + + hi def link treeDir Directory + hi def link treeUp Directory + hi def link treeCWD Statement + hi def link treeLink Macro + hi def link treeOpenable Title + hi def link treeFlag ignore + hi def link treeRO WarningMsg + hi def link treeBookmark Statement + + hi def link NERDTreeCurrentNode Search +endfunction + +"FUNCTION: s:stripMarkupFromLine(line, removeLeadingSpaces){{{2 +"returns the given line with all the tree parts stripped off +" +"Args: +"line: the subject line +"removeLeadingSpaces: 1 if leading spaces are to be removed (leading spaces = +"any spaces before the actual text of the node) +function! s:stripMarkupFromLine(line, removeLeadingSpaces) + let line = a:line + "remove the tree parts and the leading space + let line = substitute (line, s:tree_markup_reg,"","") + + "strip off any read only flag + let line = substitute (line, ' \[RO\]', "","") + + "strip off any bookmark flags + let line = substitute (line, ' {[^}]*}', "","") + + "strip off any executable flags + let line = substitute (line, '*\ze\($\| \)', "","") + + let wasdir = 0 + if line =~ '/$' + let wasdir = 1 + endif + let line = substitute (line,' -> .*',"","") " remove link to + if wasdir ==# 1 + let line = substitute (line, '/\?$', '/', "") + endif + + if a:removeLeadingSpaces + let line = substitute (line, '^ *', '', '') + endif + + return line +endfunction + +"FUNCTION: s:toggle(dir) {{{2 +"Toggles the NERD tree. I.e the NERD tree is open, it is closed, if it is +"closed it is restored or initialized (if it doesnt exist) +" +"Args: +"dir: the full path for the root node (is only used if the NERD tree is being +"initialized. +function! s:toggle(dir) + if s:treeExistsForTab() + if !s:isTreeOpen() + call s:createTreeWin() + if !&hidden + call s:renderView() + endif + call s:restoreScreenState() + else + call s:closeTree() + endif + else + call s:initNerdTree(a:dir) + endif +endfunction +"SECTION: Interface bindings {{{1 +"============================================================ +"FUNCTION: s:activateNode(forceKeepWindowOpen) {{{2 +"If the current node is a file, open it in the previous window (or a new one +"if the previous is modified). If it is a directory then it is opened. +" +"args: +"forceKeepWindowOpen - dont close the window even if NERDTreeQuitOnOpen is set +function! s:activateNode(forceKeepWindowOpen) + if getline(".") ==# s:tree_up_dir_line + return s:upDir(0) + endif + + let treenode = s:TreeFileNode.GetSelected() + if treenode != {} + call treenode.activate(a:forceKeepWindowOpen) + else + let bookmark = s:Bookmark.GetSelected() + if !empty(bookmark) + call bookmark.activate() + endif + endif +endfunction + +"FUNCTION: s:bindMappings() {{{2 +function! s:bindMappings() + " set up mappings and commands for this buffer + nnoremap :call handleMiddleMouse() + nnoremap :call checkForActivate() + nnoremap <2-leftmouse> :call activateNode(0) + + exec "nnoremap ". g:NERDTreeMapActivateNode . " :call activateNode(0)" + exec "nnoremap ". g:NERDTreeMapOpenSplit ." :call openEntrySplit(0,0)" + exec "nnoremap :call activateNode(0)" + + exec "nnoremap ". g:NERDTreeMapPreview ." :call previewNode(0)" + exec "nnoremap ". g:NERDTreeMapPreviewSplit ." :call previewNode(1)" + + exec "nnoremap ". g:NERDTreeMapOpenVSplit ." :call openEntrySplit(1,0)" + exec "nnoremap ". g:NERDTreeMapPreviewVSplit ." :call previewNode(2)" + + exec "nnoremap ". g:NERDTreeMapOpenRecursively ." :call openNodeRecursively()" + + exec "nnoremap ". g:NERDTreeMapUpdirKeepOpen ." :call upDir(1)" + exec "nnoremap ". g:NERDTreeMapUpdir ." :call upDir(0)" + exec "nnoremap ". g:NERDTreeMapChangeRoot ." :call chRoot()" + + exec "nnoremap ". g:NERDTreeMapChdir ." :call chCwd()" + + exec "nnoremap ". g:NERDTreeMapQuit ." :call closeTreeWindow()" + + exec "nnoremap ". g:NERDTreeMapRefreshRoot ." :call refreshRoot()" + exec "nnoremap ". g:NERDTreeMapRefresh ." :call refreshCurrent()" + + exec "nnoremap ". g:NERDTreeMapHelp ." :call displayHelp()" + exec "nnoremap ". g:NERDTreeMapToggleZoom ." :call toggleZoom()" + exec "nnoremap ". g:NERDTreeMapToggleHidden ." :call toggleShowHidden()" + exec "nnoremap ". g:NERDTreeMapToggleFilters ." :call toggleIgnoreFilter()" + exec "nnoremap ". g:NERDTreeMapToggleFiles ." :call toggleShowFiles()" + exec "nnoremap ". g:NERDTreeMapToggleBookmarks ." :call toggleShowBookmarks()" + + exec "nnoremap ". g:NERDTreeMapCloseDir ." :call closeCurrentDir()" + exec "nnoremap ". g:NERDTreeMapCloseChildren ." :call closeChildren()" + + exec "nnoremap ". g:NERDTreeMapMenu ." :call showMenu()" + + exec "nnoremap ". g:NERDTreeMapJumpParent ." :call jumpToParent()" + exec "nnoremap ". g:NERDTreeMapJumpNextSibling ." :call jumpToSibling(1)" + exec "nnoremap ". g:NERDTreeMapJumpPrevSibling ." :call jumpToSibling(0)" + exec "nnoremap ". g:NERDTreeMapJumpFirstChild ." :call jumpToFirstChild()" + exec "nnoremap ". g:NERDTreeMapJumpLastChild ." :call jumpToLastChild()" + exec "nnoremap ". g:NERDTreeMapJumpRoot ." :call jumpToRoot()" + + exec "nnoremap ". g:NERDTreeMapOpenInTab ." :call openInNewTab(0)" + exec "nnoremap ". g:NERDTreeMapOpenInTabSilent ." :call openInNewTab(1)" + + exec "nnoremap ". g:NERDTreeMapOpenExpl ." :call openExplorer()" + + exec "nnoremap ". g:NERDTreeMapDeleteBookmark ." :call deleteBookmark()" + + "bind all the user custom maps + call s:KeyMap.BindAll() + + command! -buffer -nargs=1 Bookmark :call bookmarkNode('') + command! -buffer -complete=customlist,s:completeBookmarks -nargs=1 RevealBookmark :call revealBookmark('') + command! -buffer -complete=customlist,s:completeBookmarks -nargs=1 OpenBookmark :call openBookmark('') + command! -buffer -complete=customlist,s:completeBookmarks -nargs=* ClearBookmarks call clearBookmarks('') + command! -buffer -complete=customlist,s:completeBookmarks -nargs=+ BookmarkToRoot call s:Bookmark.ToRoot('') + command! -buffer -nargs=0 ClearAllBookmarks call s:Bookmark.ClearAll() call renderView() + command! -buffer -nargs=0 ReadBookmarks call s:Bookmark.CacheBookmarks(0) call renderView() + command! -buffer -nargs=0 WriteBookmarks call s:Bookmark.Write() +endfunction + +" FUNCTION: s:bookmarkNode(name) {{{2 +" Associate the current node with the given name +function! s:bookmarkNode(name) + let currentNode = s:TreeFileNode.GetSelected() + if currentNode != {} + try + call currentNode.bookmark(a:name) + call s:renderView() + catch /^NERDTree.IllegalBookmarkNameError/ + call s:echo("bookmark names must not contain spaces") + endtry + else + call s:echo("select a node first") + endif +endfunction +"FUNCTION: s:checkForActivate() {{{2 +"Checks if the click should open the current node, if so then activate() is +"called (directories are automatically opened if the symbol beside them is +"clicked) +function! s:checkForActivate() + let currentNode = s:TreeFileNode.GetSelected() + if currentNode != {} + let startToCur = strpart(getline(line(".")), 0, col(".")) + let char = strpart(startToCur, strlen(startToCur)-1, 1) + + "if they clicked a dir, check if they clicked on the + or ~ sign + "beside it + if currentNode.path.isDirectory + if startToCur =~ s:tree_markup_reg . '$' && char =~ '[+~]' + call s:activateNode(0) + return + endif + endif + + if (g:NERDTreeMouseMode ==# 2 && currentNode.path.isDirectory) || g:NERDTreeMouseMode ==# 3 + if char !~ s:tree_markup_reg && startToCur !~ '\/$' + call s:activateNode(0) + return + endif + endif + endif +endfunction + +" FUNCTION: s:chCwd() {{{2 +function! s:chCwd() + let treenode = s:TreeFileNode.GetSelected() + if treenode ==# {} + call s:echo("Select a node first") + return + endif + + try + call treenode.path.changeToDir() + catch /^NERDTree.PathChangeError/ + call s:echoWarning("could not change cwd") + endtry +endfunction + +" FUNCTION: s:chRoot() {{{2 +" changes the current root to the selected one +function! s:chRoot() + let treenode = s:TreeFileNode.GetSelected() + if treenode ==# {} + call s:echo("Select a node first") + return + endif + + call treenode.makeRoot() + call s:renderView() + call b:NERDTreeRoot.putCursorHere(0, 0) +endfunction + +" FUNCTION: s:clearBookmarks(bookmarks) {{{2 +function! s:clearBookmarks(bookmarks) + if a:bookmarks ==# '' + let currentNode = s:TreeFileNode.GetSelected() + if currentNode != {} + call currentNode.clearBoomarks() + endif + else + for name in split(a:bookmarks, ' ') + let bookmark = s:Bookmark.BookmarkFor(name) + call bookmark.delete() + endfor + endif + call s:renderView() +endfunction +" FUNCTION: s:closeChildren() {{{2 +" closes all childnodes of the current node +function! s:closeChildren() + let currentNode = s:TreeDirNode.GetSelected() + if currentNode ==# {} + call s:echo("Select a node first") + return + endif + + call currentNode.closeChildren() + call s:renderView() + call currentNode.putCursorHere(0, 0) +endfunction +" FUNCTION: s:closeCurrentDir() {{{2 +" closes the parent dir of the current node +function! s:closeCurrentDir() + let treenode = s:TreeFileNode.GetSelected() + if treenode ==# {} + call s:echo("Select a node first") + return + endif + + let parent = treenode.parent + if parent ==# {} || parent.isRoot() + call s:echo("cannot close tree root") + else + call treenode.parent.close() + call s:renderView() + call treenode.parent.putCursorHere(0, 0) + endif +endfunction +" FUNCTION: s:closeTreeWindow() {{{2 +" close the tree window +function! s:closeTreeWindow() + if b:NERDTreeType ==# "secondary" && b:NERDTreePreviousBuf != -1 + exec "buffer " . b:NERDTreePreviousBuf + else + if winnr("$") > 1 + call s:closeTree() + else + call s:echo("Cannot close last window") + endif + endif +endfunction +" FUNCTION: s:deleteBookmark() {{{2 +" if the cursor is on a bookmark, prompt to delete +function! s:deleteBookmark() + let bookmark = s:Bookmark.GetSelected() + if bookmark ==# {} + call s:echo("Put the cursor on a bookmark") + return + endif + + echo "Are you sure you wish to delete the bookmark:\n\"" . bookmark.name . "\" (yN):" + + if nr2char(getchar()) ==# 'y' + try + call bookmark.delete() + call s:renderView() + redraw + catch /^NERDTree/ + call s:echoWarning("Could not remove bookmark") + endtry + else + call s:echo("delete aborted" ) + endif + +endfunction + +" FUNCTION: s:displayHelp() {{{2 +" toggles the help display +function! s:displayHelp() + let b:treeShowHelp = b:treeShowHelp ? 0 : 1 + call s:renderView() + call s:centerView() +endfunction + +" FUNCTION: s:handleMiddleMouse() {{{2 +function! s:handleMiddleMouse() + let curNode = s:TreeFileNode.GetSelected() + if curNode ==# {} + call s:echo("Put the cursor on a node first" ) + return + endif + + if curNode.path.isDirectory + call s:openExplorer() + else + call s:openEntrySplit(0,0) + endif +endfunction + + +" FUNCTION: s:jumpToFirstChild() {{{2 +" wrapper for the jump to child method +function! s:jumpToFirstChild() + call s:jumpToChild(0) +endfunction + +" FUNCTION: s:jumpToLastChild() {{{2 +" wrapper for the jump to child method +function! s:jumpToLastChild() + call s:jumpToChild(1) +endfunction + +" FUNCTION: s:jumpToParent() {{{2 +" moves the cursor to the parent of the current node +function! s:jumpToParent() + let currentNode = s:TreeFileNode.GetSelected() + if !empty(currentNode) + if !empty(currentNode.parent) + call currentNode.parent.putCursorHere(1, 0) + call s:centerView() + else + call s:echo("cannot jump to parent") + endif + else + call s:echo("put the cursor on a node first") + endif +endfunction + +" FUNCTION: s:jumpToRoot() {{{2 +" moves the cursor to the root node +function! s:jumpToRoot() + call b:NERDTreeRoot.putCursorHere(1, 0) + call s:centerView() +endfunction + +" FUNCTION: s:jumpToSibling() {{{2 +" moves the cursor to the sibling of the current node in the given direction +" +" Args: +" forward: 1 if the cursor should move to the next sibling, 0 if it should +" move back to the previous sibling +function! s:jumpToSibling(forward) + let currentNode = s:TreeFileNode.GetSelected() + if !empty(currentNode) + let sibling = currentNode.findSibling(a:forward) + + if !empty(sibling) + call sibling.putCursorHere(1, 0) + call s:centerView() + endif + else + call s:echo("put the cursor on a node first") + endif +endfunction + +" FUNCTION: s:openBookmark(name) {{{2 +" put the cursor on the given bookmark and, if its a file, open it +function! s:openBookmark(name) + try + let targetNode = s:Bookmark.GetNodeForName(a:name, 0) + call targetNode.putCursorHere(0, 1) + redraw! + catch /^NERDTree.BookmarkedNodeNotFoundError/ + call s:echo("note - target node is not cached") + let bookmark = s:Bookmark.BookmarkFor(a:name) + let targetNode = s:TreeFileNode.New(bookmark.path) + endtry + if targetNode.path.isDirectory + call targetNode.openExplorer() + else + call targetNode.open() + endif +endfunction +" FUNCTION: s:openEntrySplit(vertical, forceKeepWindowOpen) {{{2 +"Opens the currently selected file from the explorer in a +"new window +" +"args: +"forceKeepWindowOpen - dont close the window even if NERDTreeQuitOnOpen is set +function! s:openEntrySplit(vertical, forceKeepWindowOpen) + let treenode = s:TreeFileNode.GetSelected() + if treenode != {} + if a:vertical + call treenode.openVSplit() + else + call treenode.openSplit() + endif + if !a:forceKeepWindowOpen + call s:closeTreeIfQuitOnOpen() + endif + else + call s:echo("select a node first") + endif +endfunction + +" FUNCTION: s:openExplorer() {{{2 +function! s:openExplorer() + let treenode = s:TreeDirNode.GetSelected() + if treenode != {} + call treenode.openExplorer() + else + call s:echo("select a node first") + endif +endfunction + +" FUNCTION: s:openInNewTab(stayCurrentTab) {{{2 +" Opens the selected node or bookmark in a new tab +" Args: +" stayCurrentTab: if 1 then vim will stay in the current tab, if 0 then vim +" will go to the tab where the new file is opened +function! s:openInNewTab(stayCurrentTab) + let target = s:TreeFileNode.GetSelected() + if target == {} + let target = s:Bookmark.GetSelected() + endif + + if target != {} + call target.openInNewTab({'stayInCurrentTab': a:stayCurrentTab}) + endif +endfunction + +" FUNCTION: s:openNodeRecursively() {{{2 +function! s:openNodeRecursively() + let treenode = s:TreeFileNode.GetSelected() + if treenode ==# {} || treenode.path.isDirectory ==# 0 + call s:echo("Select a directory node first" ) + else + call s:echo("Recursively opening node. Please wait...") + call treenode.openRecursively() + call s:renderView() + redraw + call s:echo("Recursively opening node. Please wait... DONE") + endif + +endfunction + +"FUNCTION: s:previewNode() {{{2 +"Args: +" openNewWin: if 0, use the previous window, if 1 open in new split, if 2 +" open in a vsplit +function! s:previewNode(openNewWin) + let currentBuf = bufnr("") + if a:openNewWin > 0 + call s:openEntrySplit(a:openNewWin ==# 2,1) + else + call s:activateNode(1) + end + call s:exec(bufwinnr(currentBuf) . "wincmd w") +endfunction + +" FUNCTION: s:revealBookmark(name) {{{2 +" put the cursor on the node associate with the given name +function! s:revealBookmark(name) + try + let targetNode = s:Bookmark.GetNodeForName(a:name, 0) + call targetNode.putCursorHere(0, 1) + catch /^NERDTree.BookmarkNotFoundError/ + call s:echo("Bookmark isnt cached under the current root") + endtry +endfunction +" FUNCTION: s:refreshRoot() {{{2 +" Reloads the current root. All nodes below this will be lost and the root dir +" will be reloaded. +function! s:refreshRoot() + call s:echo("Refreshing the root node. This could take a while...") + call b:NERDTreeRoot.refresh() + call s:renderView() + redraw + call s:echo("Refreshing the root node. This could take a while... DONE") +endfunction + +" FUNCTION: s:refreshCurrent() {{{2 +" refreshes the root for the current node +function! s:refreshCurrent() + let treenode = s:TreeDirNode.GetSelected() + if treenode ==# {} + call s:echo("Refresh failed. Select a node first") + return + endif + + call s:echo("Refreshing node. This could take a while...") + call treenode.refresh() + call s:renderView() + redraw + call s:echo("Refreshing node. This could take a while... DONE") +endfunction +" FUNCTION: s:showMenu() {{{2 +function! s:showMenu() + let curNode = s:TreeFileNode.GetSelected() + if curNode ==# {} + call s:echo("Put the cursor on a node first" ) + return + endif + + let mc = s:MenuController.New(s:MenuItem.AllEnabled()) + call mc.showMenu() +endfunction + +" FUNCTION: s:toggleIgnoreFilter() {{{2 +" toggles the use of the NERDTreeIgnore option +function! s:toggleIgnoreFilter() + let b:NERDTreeIgnoreEnabled = !b:NERDTreeIgnoreEnabled + call s:renderViewSavingPosition() + call s:centerView() +endfunction + +" FUNCTION: s:toggleShowBookmarks() {{{2 +" toggles the display of bookmarks +function! s:toggleShowBookmarks() + let b:NERDTreeShowBookmarks = !b:NERDTreeShowBookmarks + if b:NERDTreeShowBookmarks + call s:renderView() + call s:putCursorOnBookmarkTable() + else + call s:renderViewSavingPosition() + endif + call s:centerView() +endfunction +" FUNCTION: s:toggleShowFiles() {{{2 +" toggles the display of hidden files +function! s:toggleShowFiles() + let b:NERDTreeShowFiles = !b:NERDTreeShowFiles + call s:renderViewSavingPosition() + call s:centerView() +endfunction + +" FUNCTION: s:toggleShowHidden() {{{2 +" toggles the display of hidden files +function! s:toggleShowHidden() + let b:NERDTreeShowHidden = !b:NERDTreeShowHidden + call s:renderViewSavingPosition() + call s:centerView() +endfunction + +" FUNCTION: s:toggleZoom() {{2 +" zoom (maximize/minimize) the NERDTree window +function! s:toggleZoom() + if exists("b:NERDTreeZoomed") && b:NERDTreeZoomed + let size = exists("b:NERDTreeOldWindowSize") ? b:NERDTreeOldWindowSize : g:NERDTreeWinSize + exec "silent vertical resize ". size + let b:NERDTreeZoomed = 0 + else + exec "vertical resize" + let b:NERDTreeZoomed = 1 + endif +endfunction + +"FUNCTION: s:upDir(keepState) {{{2 +"moves the tree up a level +" +"Args: +"keepState: 1 if the current root should be left open when the tree is +"re-rendered +function! s:upDir(keepState) + let cwd = b:NERDTreeRoot.path.str({'format': 'UI'}) + if cwd ==# "/" || cwd =~ '^[^/]..$' + call s:echo("already at top dir") + else + if !a:keepState + call b:NERDTreeRoot.close() + endif + + let oldRoot = b:NERDTreeRoot + + if empty(b:NERDTreeRoot.parent) + let path = b:NERDTreeRoot.path.getParent() + let newRoot = s:TreeDirNode.New(path) + call newRoot.open() + call newRoot.transplantChild(b:NERDTreeRoot) + let b:NERDTreeRoot = newRoot + else + let b:NERDTreeRoot = b:NERDTreeRoot.parent + endif + + if g:NERDTreeChDirMode ==# 2 + call b:NERDTreeRoot.path.changeToDir() + endif + + call s:renderView() + call oldRoot.putCursorHere(0, 0) + endif +endfunction + + +"reset &cpo back to users setting +let &cpo = s:old_cpo + +" vim: set sw=4 sts=4 et fdm=marker: diff --git a/plugin/acp.vim b/plugin/acp.vim new file mode 100644 index 0000000..0c01a31 --- /dev/null +++ b/plugin/acp.vim @@ -0,0 +1,170 @@ +"============================================================================= +" Copyright (c) 2007-2009 Takeshi NISHIDA +" +" GetLatestVimScripts: 1879 1 :AutoInstall: AutoComplPop +"============================================================================= +" LOAD GUARD {{{1 + +if exists('g:loaded_acp') + finish +elseif v:version < 702 + echoerr 'AutoComplPop does not support this version of vim (' . v:version . ').' + finish +endif +let g:loaded_acp = 1 + +" }}}1 +"============================================================================= +" FUNCTION: {{{1 + +" +function s:defineOption(name, default) + if !exists(a:name) + let {a:name} = a:default + endif +endfunction + +" +function s:makeDefaultBehavior() + let behavs = { + \ '*' : [], + \ 'ruby' : [], + \ 'python' : [], + \ 'perl' : [], + \ 'xml' : [], + \ 'html' : [], + \ 'xhtml' : [], + \ 'css' : [], + \ } + "--------------------------------------------------------------------------- + if !empty(g:acp_behaviorUserDefinedFunction) && + \ !empty(g:acp_behaviorUserDefinedMeets) + for key in keys(behavs) + call add(behavs[key], { + \ 'command' : "\\", + \ 'completefunc' : g:acp_behaviorUserDefinedFunction, + \ 'meets' : g:acp_behaviorUserDefinedMeets, + \ 'repeat' : 0, + \ }) + endfor + endif + "--------------------------------------------------------------------------- + for key in keys(behavs) + call add(behavs[key], { + \ 'command' : "\\", + \ 'completefunc' : 'acp#completeSnipmate', + \ 'meets' : 'acp#meetsForSnipmate', + \ 'onPopupClose' : 'acp#onPopupCloseSnipmate', + \ 'repeat' : 0, + \ }) + endfor + "--------------------------------------------------------------------------- + for key in keys(behavs) + call add(behavs[key], { + \ 'command' : g:acp_behaviorKeywordCommand, + \ 'meets' : 'acp#meetsForKeyword', + \ 'repeat' : 0, + \ }) + endfor + "--------------------------------------------------------------------------- + for key in keys(behavs) + call add(behavs[key], { + \ 'command' : "\\", + \ 'meets' : 'acp#meetsForFile', + \ 'repeat' : 1, + \ }) + endfor + "--------------------------------------------------------------------------- + call add(behavs.ruby, { + \ 'command' : "\\", + \ 'meets' : 'acp#meetsForRubyOmni', + \ 'repeat' : 0, + \ }) + "--------------------------------------------------------------------------- + call add(behavs.python, { + \ 'command' : "\\", + \ 'meets' : 'acp#meetsForPythonOmni', + \ 'repeat' : 0, + \ }) + "--------------------------------------------------------------------------- + call add(behavs.perl, { + \ 'command' : "\\", + \ 'meets' : 'acp#meetsForPerlOmni', + \ 'repeat' : 0, + \ }) + "--------------------------------------------------------------------------- + call add(behavs.xml, { + \ 'command' : "\\", + \ 'meets' : 'acp#meetsForXmlOmni', + \ 'repeat' : 1, + \ }) + "--------------------------------------------------------------------------- + call add(behavs.html, { + \ 'command' : "\\", + \ 'meets' : 'acp#meetsForHtmlOmni', + \ 'repeat' : 1, + \ }) + "--------------------------------------------------------------------------- + call add(behavs.xhtml, { + \ 'command' : "\\", + \ 'meets' : 'acp#meetsForHtmlOmni', + \ 'repeat' : 1, + \ }) + "--------------------------------------------------------------------------- + call add(behavs.css, { + \ 'command' : "\\", + \ 'meets' : 'acp#meetsForCssOmni', + \ 'repeat' : 0, + \ }) + "--------------------------------------------------------------------------- + return behavs +endfunction + +" }}}1 +"============================================================================= +" INITIALIZATION {{{1 + +"----------------------------------------------------------------------------- +call s:defineOption('g:acp_enableAtStartup', 1) +call s:defineOption('g:acp_mappingDriven', 0) +call s:defineOption('g:acp_ignorecaseOption', 1) +call s:defineOption('g:acp_completeOption', '.,w,b,k') +call s:defineOption('g:acp_completeoptPreview', 0) +call s:defineOption('g:acp_behaviorUserDefinedFunction', '') +call s:defineOption('g:acp_behaviorUserDefinedMeets', '') +call s:defineOption('g:acp_behaviorSnipmateLength', -1) +call s:defineOption('g:acp_behaviorKeywordCommand', "\") +call s:defineOption('g:acp_behaviorKeywordLength', 2) +call s:defineOption('g:acp_behaviorKeywordIgnores', []) +call s:defineOption('g:acp_behaviorFileLength', 0) +call s:defineOption('g:acp_behaviorRubyOmniMethodLength', 0) +call s:defineOption('g:acp_behaviorRubyOmniSymbolLength', 1) +call s:defineOption('g:acp_behaviorPythonOmniLength', 0) +call s:defineOption('g:acp_behaviorPerlOmniLength', -1) +call s:defineOption('g:acp_behaviorXmlOmniLength', 0) +call s:defineOption('g:acp_behaviorHtmlOmniLength', 0) +call s:defineOption('g:acp_behaviorCssOmniPropertyLength', 1) +call s:defineOption('g:acp_behaviorCssOmniValueLength', 0) +call s:defineOption('g:acp_behavior', {}) +"----------------------------------------------------------------------------- +call extend(g:acp_behavior, s:makeDefaultBehavior(), 'keep') +"----------------------------------------------------------------------------- +command! -bar -narg=0 AcpEnable call acp#enable() +command! -bar -narg=0 AcpDisable call acp#disable() +command! -bar -narg=0 AcpLock call acp#lock() +command! -bar -narg=0 AcpUnlock call acp#unlock() +"----------------------------------------------------------------------------- +" legacy commands +command! -bar -narg=0 AutoComplPopEnable AcpEnable +command! -bar -narg=0 AutoComplPopDisable AcpDisable +command! -bar -narg=0 AutoComplPopLock AcpLock +command! -bar -narg=0 AutoComplPopUnlock AcpUnlock +"----------------------------------------------------------------------------- +if g:acp_enableAtStartup + AcpEnable +endif +"----------------------------------------------------------------------------- + +" }}}1 +"============================================================================= +" vim: set fdm=marker: diff --git a/plugin/bufrestore.vim b/plugin/bufrestore.vim new file mode 100644 index 0000000..fc28263 --- /dev/null +++ b/plugin/bufrestore.vim @@ -0,0 +1,38 @@ +"============================================================================== +" Plugin: Buffer Restore +" +" Description: This plugin keeps a stack of recently closed buffers and allows +" the user to pop a file off of the stack at any time and restore that file +" for editing. +"============================================================================== + +"============================================================================== +" Global Variables +"============================================================================== +let s:Recent_Files = [] + +"============================================================================== +" Functions +"============================================================================== +function! StoreBufferFilename(path) + let buffer_name = a:path + if !empty(buffer_name) + let s:Recent_Files = [buffer_name] + s:Recent_Files + endif +endfunction + +function! BufferRestore() + if len(s:Recent_Files) > 0 + let edit_command = ":edit " . s:Recent_Files[0] + let s:Recent_Files = s:Recent_Files[1:] + exec edit_command + endif +endfunction + +"============================================================================== +" Auto Commands +"============================================================================== +augroup bufrestore +autocmd! +autocmd bufrestore BufDelete * :call StoreBufferFilename(expand("")) + diff --git a/plugin/cctree.vim b/plugin/cctree.vim new file mode 100644 index 0000000..b6d7d70 --- /dev/null +++ b/plugin/cctree.vim @@ -0,0 +1,3272 @@ +" C Call-Tree Explorer (CCTree) +" +" +" Script Info and Documentation +"============================================================================= +" Copyright: Copyright (C) August 2008 - 2011, Hari Rangarajan +" Permission is hereby granted to use and distribute this code, +" with or without modifications, provided that this copyright +" notice is copied with it. Like anything else that's free, +" cctree.vim is provided *as is* and comes with no +" warranty of any kind, either expressed or implied. In no +" event will the copyright holder be liable for any damamges +" resulting from the use of this software. +" +" Name Of File: CCTree.vim +" Description: C Call-Tree Explorer Vim Plugin +" Maintainer: Hari Rangarajan +" URL: http://vim.sourceforge.net/scripts/script.php?script_id=2368 +" Last Change: April 22, 2011 +" Version: 1.40 +" +"============================================================================= +" +" {{{ Description: +" Plugin generates call-trees for any function or macro in real-time inside +" Vim. +" }}} +" {{{ Requirements: 1) Vim 7.xx , 2) Cscope +" +" Tested on Unix and the following Win32 versions: +" + Cscope, mlcscope (WIN32) +" http://www.geocities.com/shankara_c/cscope.html +" http://www.bell-labs.com/project/wwexptools/packages.html +" +" +" }}} +" {{{ Installation: +" Copy this file to ~/.vim/plugins/ +" or to /vimfiles/plugins/ (on Win32 platforms) +" +" It might also be possible to load it as a filetype plugin +" ~/.vim/ftplugin/c/ +" +" Need to set :filetype plugin on +" +" }}} +" {{{ Usage: +" Build cscope database, for example: +" > cscope -b -i cscope.files +" [Tip: add -c option to build uncompressed databases for faster +" load speeds] +" +" Load database with command ":CCTreeLoadDB" +" (Please note that it might take a while depending on the +" database size) +" +" Append database with command ":CCTreeAppendDB" +" Allows multiple cscope files to be loaded and cross-referenced +" Illustration: +" :CCTreeAppendDB ./cscope.out +" :CCTreeAppendDB ./dir1/cscope.out +" :CCTreeAppendDB ./dir2/cscope.out +" +" A database name, i.e., my_cscope.out, can be specified with +" the command. If not provided, a prompt will ask for the +" filename; default is cscope.out. +" +" To show loaded databases, use command ":CCTreeShowLoadedDBs" +" +" To unload all databases, use command ":CCTreeUnLoadDB" +" Note: There is no provision to unload databases individually +" +" To save the current set of databases loaded in to memory onto disk +" in native CCTree XRef format, use command ":CCTreeSaveXRefDB" +" +" To load a saved native CCTree XRef format file, use +" command ":CCTreeLoadXRefDB" +" +" To load a saved native CCTree XRef format file, use +" command ":CCTreeLoadXRefDBFromDisk" +" +" Notes: No merging database support for CCTree native DB's [at present]. +" +" +" To have multiple CCTree preview windows, use ":CCTreeWindowSaveCopy" +" Note: Once saved, only the depth of the preview window can be changed +" +" Default Mappings: +" Get reverse call tree for symbol < +" Get forward call tree for symbol > +" Increase depth of tree and update = +" Decrease depth of tree and update - +" +" Open symbol in other window +" Preview symbol in other window +" +" Save copy of preview window y +" Highlight current call-tree flow +" Compress(Fold) call tree view zs +" (This is useful for viewing long +" call trees which span across +" multiple pages) +" +" Custom user-mappings: +" Users can custom-map the short-cut keys by +" overriding the following variables in their +" Vim start-up configuration +" +" g:CCTreeKeyTraceForwardTree = '>' +" g:CCTreeKeyTraceReverseTree = '<' +" g:CCTreeKeyHilightTree = '' " Static highlighting +" g:CCTreeKeySaveWindow = 'y' +" g:CCTreeKeyToggleWindow = 'w' +" g:CCTreeKeyCompressTree = 'zs' " Compress call-tree +" g:CCTreeKeyDepthPlus = '=' +" g:CCTreeKeyDepthMinus = '-' +" +" Command List: +" CCTreeLoadDB +" CCTreeAppendDB +" CCTreeLoadXRefDB +" CCTreeSaveXRefDB +" CCTreeLoadDBFromDisk +" +" CCTreeUnLoadDB +" CCTreeShowLoadedDBs +" +" CCTreeTraceForward +" CCTreeTraceReverse +" CCTreeRecurseDepthPlus +" CCTreeRecurseDepthMinus +" CCTreeWindowSaveCopy +" +" Only in preview window: +" CCTreeWindowHiCallTree (same as shorcut) +" Highlight calling tree for keyword at cursor +" +" Dynamic configuration: +" CCTreeOptsEnable