<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>.netrwhist</filename>
    </added>
    <added>
      <filename>doc/EnhancedCommentify.txt</filename>
    </added>
    <added>
      <filename>doc/apidock.txt</filename>
    </added>
    <added>
      <filename>doc/example.taskpaper</filename>
    </added>
    <added>
      <filename>doc/taskpaper.html</filename>
    </added>
    <added>
      <filename>doc/taskpaper.txt</filename>
    </added>
    <added>
      <filename>doc/taskpaper_hacking.txt</filename>
    </added>
    <added>
      <filename>doc/taskpaper_licence.txt</filename>
    </added>
    <added>
      <filename>ftdetect/cucumber.vim</filename>
    </added>
    <added>
      <filename>ftdetect/taskpaper.vim</filename>
    </added>
    <added>
      <filename>ftplugin/cucumber.vim</filename>
    </added>
    <added>
      <filename>ftplugin/ocaml_enhcomm.vim</filename>
    </added>
    <added>
      <filename>ftplugin/php_enhcomm.vim</filename>
    </added>
    <added>
      <filename>ftplugin/taskpaper.vim</filename>
    </added>
    <added>
      <filename>indent/cucumber.vim</filename>
    </added>
    <added>
      <filename>plugin/EnhancedCommentify.vim</filename>
    </added>
    <added>
      <filename>plugin/SimpleFold.vim</filename>
    </added>
    <added>
      <filename>plugin/apidock.vim</filename>
    </added>
    <added>
      <filename>plugin/rubytest.vim</filename>
    </added>
    <added>
      <filename>syntax/cucumber.vim</filename>
    </added>
    <added>
      <filename>syntax/taskpaper.vim</filename>
    </added>
    <added>
      <filename>syntax/textile.vim</filename>
    </added>
    <added>
      <filename>syntax/yaml.vim</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -55,3 +55,5 @@ exec &quot;Snippet h1 &lt;h1 id=\&quot;&quot;.st.et.&quot;\&quot;&gt;&quot;.st.et.&quot;&lt;/h1&gt;&quot;.st.et
 exec &quot;Snippet input &lt;input type=\&quot;&quot;.st.et.&quot;\&quot; name=\&quot;&quot;.st.et.&quot;\&quot; value=\&quot;&quot;.st.et.&quot;\&quot; &quot;.st.et.&quot;/&gt;&quot;.st.et
 exec &quot;Snippet style &lt;style type=\&quot;text/css\&quot; media=\&quot;screen\&quot;&gt;&lt;CR&gt;/* &lt;![CDATA[ */&lt;CR&gt;&quot;.st.et.&quot;&lt;CR&gt;/* ]]&gt; */&lt;CR&gt;&lt;/style&gt;&lt;CR&gt;&quot;.st.et
 exec &quot;Snippet base &lt;base href=\&quot;&quot;.st.et.&quot;\&quot;&quot;.st.et.&quot; /&gt;&quot;.st.et
+exec &quot;Snippet pe &lt;%= &quot;.st.et.&quot; %&gt;&quot;
+exec &quot;Snippet pr &lt;%- &quot;.st.et.&quot; %&gt;&quot;</diff>
      <filename>after/ftplugin/html_snippets.vim</filename>
    </modified>
    <modified>
      <diff>@@ -53,4 +53,4 @@ exec &quot;Snippet ril render :inline =&gt; \&quot;&quot;.st.et.&quot;\&quot;, :locals =&gt; { &quot;.st.et.&quot; =&gt; \&quot;&quot;
 exec &quot;Snippet rtl render :text =&gt; \&quot;&quot;.st.et.&quot;\&quot;, :layout =&gt; \&quot;&quot;.st.et.&quot;\&quot;&quot;.st.et
 exec &quot;Snippet reca redirect_to :controller =&gt; \&quot;&quot;.st.&quot;items&quot;.et.&quot;\&quot;, :action =&gt; \&quot;&quot;.st.&quot;list&quot;.et.&quot;\&quot;&quot;.st.et
 exec &quot;Snippet pe &lt;%= &quot;.st.et.&quot; %&gt;&quot;
-exec &quot;Snippet pr &lt;%- &quot;.st.et.&quot; -%&gt;&quot;
+exec &quot;Snippet pr &lt;%- &quot;.st.et.&quot; %&gt;&quot;</diff>
      <filename>after/ftplugin/rails_snippets.vim</filename>
    </modified>
    <modified>
      <diff>@@ -17,7 +17,7 @@ exec &quot;Snippet h5 &lt;h5&gt;&quot;.st.et.&quot;&lt;/h5&gt;&quot;.st.et
 exec &quot;Snippet h6 &lt;h6&gt;&quot;.st.et.&quot;&lt;/h6&gt;&quot;.st.et
 exec &quot;Snippet fieldset &lt;fieldset&gt;&lt;CR&gt;&quot;.st.et.&quot;&lt;CR&gt;&lt;/fieldset&gt;&quot;.st.et
 exec &quot;Snippet noscript &lt;noscript&gt;&lt;CR&gt;&quot;.st.et.&quot;&lt;CR&gt;&lt;/noscript&gt;&quot;.st.et
-exec &quot;Snippet ul &lt;ul &quot;.st.et.&quot;&gt;&lt;CR&gt;&quot;.st.et.&quot;&lt;CR&gt;&lt;/ul&gt;&quot;.st.et
+exec &quot;Snippet ul &lt;ul&quot;.st.et.&quot;&gt;&lt;CR&gt;&quot;.st.et.&quot;&lt;CR&gt;&lt;/ul&gt;&quot;.st.et
 exec &quot;Snippet xml &lt;?xml version=\&quot;1.0\&quot; encoding=\&quot;iso-8859-1\&quot;?&gt;&lt;CR&gt;&lt;CR&gt;&quot;.st.et
 exec &quot;Snippet body &lt;body id=\&quot;&quot;.st.et.&quot;\&quot; &quot;.st.et.&quot;&gt;&lt;CR&gt;&quot;.st.et.&quot;&lt;CR&gt;&lt;/body&gt;&quot;.st.et
 exec &quot;Snippet legend &lt;legend align=\&quot;&quot;.st.et.&quot;\&quot; accesskey=\&quot;&quot;.st.et.&quot;\&quot;&gt;&lt;CR&gt;&quot;.st.et.&quot;&lt;CR&gt;&lt;/legend&gt;&quot;.st.et
@@ -31,6 +31,7 @@ exec &quot;Snippet td &lt;td &quot;.st.et.&quot;&gt;&quot;.st.et.&quot;&lt;/td&gt;&quot;.st.et
 exec &quot;Snippet dt &lt;dt&gt;&quot;.st.et.&quot;&lt;CR&gt;&lt;/dt&gt;&lt;CR&gt;&lt;dd&gt;&quot;.st.et.&quot;&lt;/dd&gt;&quot;.st.et
 exec &quot;Snippet tfoot &lt;tfoot&gt;&lt;CR&gt;&quot;.st.et.&quot;&lt;CR&gt;&lt;/tfoot&gt;&quot;.st.et
 exec &quot;Snippet div &lt;!-- begin div.&quot;.st.&quot;id&quot;.et.&quot; --&gt;&lt;CR&gt;&lt;div id=\&quot;&quot;.st.&quot;id&quot;.et.&quot;\&quot;&gt;&lt;CR&gt;&quot;.st.et.&quot;&lt;CR&gt;&lt;/div&gt;&lt;CR&gt;&lt;!-- end div.&quot;.st.&quot;id&quot;.et.&quot; --&gt;&lt;CR&gt;&quot;.st.et
+exec &quot;Snippet span &lt;span&gt;&quot;.st.et.&quot;&lt;/span&gt;&quot;
 exec &quot;Snippet ol &lt;ol &quot;.st.et.&quot;&gt;&lt;CR&gt;&quot;.st.et.&quot;&lt;CR&gt;&lt;/ol&gt;&quot;.st.et
 exec &quot;Snippet txtarea &lt;textarea id=\&quot;&quot;.st.&quot;ID&quot;.et.&quot;\&quot; name=\&quot;&quot;.st.&quot;Name&quot;.et.&quot;\&quot; rows=\&quot;&quot;.st.et.&quot;\&quot; cols=\&quot;&quot;.st.et.&quot;\&quot; tabindex=\&quot;&quot;.st.et.&quot;\&quot; &quot;.st.et.&quot;&gt;&quot;.st.et.&quot;&lt;/textarea&gt;&quot;.st.et
 exec &quot;Snippet mailto &lt;a href=\&quot;mailto:&quot;.st.et.&quot;?subject=&quot;.st.et.&quot;\&quot;&gt;&quot;.st.et.&quot;&lt;/a&gt;&quot;.st.et
@@ -46,3 +47,10 @@ exec &quot;Snippet select &lt;select id=\&quot;&quot;.st.&quot;ID&quot;.et.&quot;\&quot; name=\&quot;&quot;.st.&quot;Name&quot;.et.&quot;\&quot; siz
 exec &quot;Snippet style &lt;style type=\&quot;text/css\&quot; media=\&quot;&quot;.st.&quot;screen&quot;.et.&quot;\&quot;&gt;&lt;CR&gt;/* &lt;![CDATA[ */&lt;CR&gt;&quot;.st.et.&quot;&lt;CR&gt;/* ]]&gt; */&lt;CR&gt;&lt;/style&gt;&lt;CR&gt;&quot;.st.et
 exec &quot;Snippet divheader &lt;!-- Begin HeaderDiv:: --&gt;&lt;CR&gt;&lt;div id=\&quot;HeaderDiv\&quot;&gt;&lt;CR&gt;&lt;!--logo in background --&gt;&lt;CR&gt;&lt;h1&gt;&quot;.st.&quot;CompanyName&quot;.et.&quot;&lt;/h1&gt;&lt;CR&gt;&lt;/div&gt;&lt;CR&gt;&lt;!-- End HeaderDiv:: --&gt;&lt;CR&gt;&quot;.st.et
 exec &quot;Snippet base &lt;base href=\&quot;&quot;.st.et.&quot;\&quot; &quot;.st.et.&quot;/&gt;&quot;.st.et
+exec &quot;Snippet pe &lt;%= &quot;.st.et.&quot; %&gt;&quot;
+exec &quot;Snippet pr &lt;%- &quot;.st.et.&quot; %&gt;&quot;
+exec &quot;Snippet li &lt;li&gt;&quot;.st.et.&quot;&lt;/li&gt;&quot;.st.et
+exec &quot;Snippet p &lt;p&gt;&quot;.st.et.&quot;&lt;/p&gt;&quot;.st.et
+exec &quot;Snippet dl &lt;dl&gt;&lt;CR&gt;&quot;.st.et.&quot;&lt;/dl&gt;&quot;
+exec &quot;Snippet dt &lt;dt&gt;&quot;.st.et.&quot;&lt;/dt&gt;&quot;
+exec &quot;Snippet dd &lt;dd&gt;&quot;.st.et.&quot;&lt;/dd&gt;&quot;</diff>
      <filename>after/ftplugin/xhtml_snippets.vim</filename>
    </modified>
    <modified>
      <diff>@@ -13,13 +13,25 @@
 if &amp;cp || exists(&quot;g:autoloaded_rails&quot;)
   finish
 endif
-let g:autoloaded_rails = '2.0'
+let g:autoloaded_rails = '3.3'
 
 let s:cpo_save = &amp;cpo
 set cpo&amp;vim
 
 &quot; Utility Functions {{{1
 
+let s:app_prototype = {}
+
+function! s:add_methods(namespace, method_names)
+  for name in a:method_names
+    let s:{a:namespace}_prototype[name] = s:function('s:'.a:namespace.'_'.name)
+  endfor
+endfunction
+
+function! s:function(name)
+    return function(substitute(a:name,'^s:',matchstr(expand('&lt;sfile&gt;'), '&lt;SNR&gt;\d\+_'),''))
+endfunction
+
 function! s:sub(str,pat,rep)
   return substitute(a:str,'\v\C'.a:pat,a:rep,'')
 endfunction
@@ -28,12 +40,8 @@ function! s:gsub(str,pat,rep)
   return substitute(a:str,'\v\C'.a:pat,a:rep,'g')
 endfunction
 
-function! s:string(str)
-  if exists(&quot;*string&quot;)
-    return string(a:str)
-  else
-    return &quot;'&quot; . s:gsub(a:str,&quot;'&quot;,&quot;'.\&quot;'\&quot;.'&quot;) . &quot;'&quot;
-  endif
+function! s:startswith(string,prefix)
+  return strpart(a:string, 0, strlen(a:prefix)) ==# a:prefix
 endfunction
 
 function! s:compact(ary)
@@ -48,7 +56,7 @@ function! s:scrub(collection,item)
   while idx != -1 &amp;&amp; cnt &lt; 100
     let col = strpart(col,0,idx).strpart(col,idx+strlen(a:item)+1)
     let idx = stridx(col,&quot;\n&quot;.a:item.&quot;\n&quot;)
-    let cnt = cnt + 1
+    let cnt += 1
   endwhile
   return strpart(col,1)
 endfunction
@@ -61,28 +69,6 @@ function! s:esccmd(p)
   return s:gsub(a:p,'[!%#]','\\&amp;')
 endfunction
 
-function! s:ra()
-  &quot; Rails root, escaped for use as single argument
-  return s:escarg(RailsRoot())
-endfunction
-
-function! s:rc()
-  &quot; Rails root, escaped for use with a command (spaces not escaped)
-  return s:esccmd(RailsRoot())
-endfunction
-
-function! s:escvar(r)
-  let r = fnamemodify(a:r,':~')
-  let r = s:gsub(r,'\W','\=&quot;_&quot;.char2nr(submatch(0)).&quot;_&quot;')
-  let r = s:gsub(r,'^\d','_&amp;')
-  return r
-endfunction
-
-function! s:rv()
-  &quot; Rails root, escaped to be a variable name
-  return s:escvar(RailsRoot())
-endfunction
-
 function! s:rquote(str)
   &quot; Imperfect but adequate for Ruby arguments
   if a:str =~ '^[A-Za-z0-9_/.:-]\+$'
@@ -98,90 +84,98 @@ function! s:sname()
   return fnamemodify(s:file,':t:r')
 endfunction
 
-function! s:hasfile(file)
-  return filereadable(RailsRoot().'/'.a:file)
+function! s:pop_command()
+  if exists(&quot;s:command_stack&quot;) &amp;&amp; len(s:command_stack) &gt; 0
+    exe remove(s:command_stack,-1)
+  endif
 endfunction
 
-function! s:rubyexestr(cmd)
-  if RailsRoot() =~ '://'
-    return &quot;ruby &quot;.a:cmd
+function! s:push_chdir()
+  if !exists(&quot;s:command_stack&quot;) | let s:command_stack = [] | endif
+  if exists(&quot;b:rails_root&quot;) &amp;&amp; !s:startswith(getcwd(), rails#app().path())
+    let chdir = exists(&quot;*haslocaldir&quot;) &amp;&amp; haslocaldir() ? &quot;lchdir &quot; : &quot;chdir &quot;
+    call add(s:command_stack,chdir.s:escarg(getcwd()))
+    exe chdir.s:escarg(rails#app().path())
   else
-    return &quot;ruby -C &quot;.s:rquote(RailsRoot()).&quot; &quot;.a:cmd
+    call add(s:command_stack,&quot;&quot;)
   endif
 endfunction
 
-function! s:rubyexestrwithfork(cmd)
-  if s:getopt(&quot;ruby_fork_port&quot;,&quot;ab&quot;) &amp;&amp; executable(&quot;ruby_fork_client&quot;)
-    return &quot;ruby_fork_client -p &quot;.s:getopt(&quot;ruby_fork_port&quot;,&quot;ab&quot;).&quot; &quot;.a:cmd
-  else
-    return s:rubyexestr(a:cmd)
-  endif
+function! s:app_path(...) dict
+  return join([self.root]+a:000,'/')
 endfunction
 
-function! s:rubyexebg(cmd)
-  let cmd = s:esccmd(s:rubyexestr(a:cmd))
-  if has(&quot;gui_win32&quot;)
-    if &amp;shellcmdflag == &quot;-c&quot; &amp;&amp; ($PATH . &amp;shell) =~? 'cygwin'
-      silent exe &quot;!cygstart -d &quot;.s:rquote(RailsRoot()).&quot; ruby &quot;.a:cmd
-    else
-      exe &quot;!start &quot;.cmd
-    endif
-  elseif exists(&quot;$STY&quot;) &amp;&amp; !has(&quot;gui_running&quot;) &amp;&amp; s:getopt(&quot;gnu_screen&quot;,&quot;abg&quot;) &amp;&amp; executable(&quot;screen&quot;)
-    silent exe &quot;!screen -ln -fn -t &quot;.s:sub(s:sub(a:cmd,'\s.*',''),'^%(script|-rcommand)/','rails-').' '.cmd
-  else
-    exe &quot;!&quot;.cmd
-  endif
-  return v:shell_error
+function! s:app_has_file(file) dict
+  return filereadable(self.path(a:file))
 endfunction
 
-function! s:rubyexe(cmd,...)
+function! s:app_find_file(name, ...) dict abort
+  let trim = strlen(self.path())+1
   if a:0
-    call s:rubyexebg(a:cmd)
+    let path = s:pathjoin(map(s:pathsplit(a:1),'self.path(v:val)'))
   else
-    exe &quot;!&quot;.s:esccmd(s:rubyexestr(a:cmd))
-  endif
-  return v:shell_error
+    let path = s:pathjoin([self.path()])
+  endif
+  let suffixesadd = s:pathjoin(get(a:000,1,&amp;suffixesadd))
+  let default = get(a:000,2,'')
+  let oldsuffixesadd = &amp;l:suffixesadd
+  try
+    let &amp;suffixesadd = suffixesadd
+    &quot; Versions before 7.1.256 returned directories from findfile
+    if type(default) == type(0) &amp;&amp; (v:version &lt; 702 || default == -1)
+      let all = findfile(a:name,path,-1)
+      if v:version &lt; 702
+        call filter(all,'!isdirectory(v:val)')
+      endif
+      call map(all,'s:gsub(strpart(fnamemodify(v:val,&quot;:p&quot;),trim),&quot;\\\\&quot;,&quot;/&quot;)')
+      return default &lt; 0 ? all : get(all,default-1,'')
+    elseif type(default) == type(0)
+      let found = findfile(a:name,path,default)
+    else
+      let i = 1
+      let found = findfile(a:name,path)
+      while v:version &lt; 702 &amp;&amp; found != &quot;&quot; &amp;&amp; isdirectory(found)
+        let i += 1
+        let found = findfile(a:name,path,i)
+      endwhile
+    endif
+    return found == &quot;&quot; ? found : s:gsub(strpart(fnamemodify(found,':p'),trim),'\\','/')
+  finally
+    let &amp;l:suffixesadd = oldsuffixesadd
+  endtry
 endfunction
 
-function! s:rubyeval(ruby,...)
-  if a:0 &gt; 0
-    let def = a:1
-  else
-    let def = &quot;&quot;
-  endif
-  if !executable(&quot;ruby&quot;)
-    return def
-  endif
-  let cmd = s:rubyexestr('-e '.s:rquote('begin; require %{rubygems}; rescue LoadError; end; begin; require %{active_support}; rescue LoadError; end; '.a:ruby))
-  &quot;let g:rails_last_ruby_command = cmd
-  &quot; If the shell is messed up, this command could cause an error message
-  silent! let results = system(cmd)
-  &quot;let g:rails_last_ruby_result = results
-  if v:shell_error != 0 &quot; results =~ '-e:\d' || results =~ 'ruby:.*(fatal)'
-    return def
-  else
-    return results
-  endif
+call s:add_methods('app',['path','has_file','find_file'])
+
+&quot; Split a path into a list.  From pathogen.vim
+function! s:pathsplit(path) abort
+  if type(a:path) == type([]) | return copy(a:path) | endif
+  let split = split(a:path,'\\\@&lt;!\%(\\\\\)*\zs,')
+  return map(split,'substitute(v:val,''\\\([\\, ]\)'',''\1'',&quot;g&quot;)')
 endfunction
 
-function! s:railseval(ruby,...)
-  if a:0 &gt; 0
-    let def = a:1
-  else
-    let def = &quot;&quot;
-  endif
-  if !executable(&quot;ruby&quot;)
-    return def
-  endif
-  let args = &quot;-r./config/boot -r &quot;.s:rquote(RailsRoot().&quot;/config/environment&quot;).&quot; -e &quot;.s:rquote(a:ruby)
-  let cmd = s:rubyexestrwithfork(args)
-  &quot; If the shell is messed up, this command could cause an error message
-  silent! let results = system(cmd)
-  if v:shell_error != 0 &quot; results =~ '-e:\d' || results =~ 'ruby:.*(fatal)'
-    return def
-  else
-    return results
-  endif
+&quot; Convert a list to a path.  From pathogen.vim
+function! s:pathjoin(...) abort
+  let i = 0
+  let path = &quot;&quot;
+  while i &lt; a:0
+    if type(a:000[i]) == type([])
+      let list = a:000[i]
+      let j = 0
+      while j &lt; len(list)
+        let escaped = substitute(list[j],'[\\, ]','\\&amp;','g')
+        if exists(&quot;+shellslash&quot;) &amp;&amp; !&amp;shellslash
+          let escaped = substitute(escaped,'^\(\w:\\\)\\','\1','')
+        endif
+        let path .= ',' . escaped
+        let j += 1
+      endwhile
+    else
+      let path .= &quot;,&quot; . a:000[i]
+    endif
+    let i += 1
+  endwhile
+  return substitute(path,'^,','','')
 endfunction
 
 function! s:endof(lnum)
@@ -199,14 +193,14 @@ function! s:endof(lnum)
   endif
   let endl = a:lnum
   while endl &lt;= line('$')
-    let endl = endl + 1
+    let endl += 1
     if getline(endl) =~ '^'.spc.endpat
       return endl
     elseif getline(endl) =~ '^=begin\&gt;'
       while getline(endl) !~ '^=end\&gt;' &amp;&amp; endl &lt;= line('$')
-        let endl = endl + 1
+        let endl += 1
       endwhile
-      let endl = endl + 1
+      let endl += 1
     elseif getline(endl) !~ '^'.spc &amp;&amp; getline(endl) !~ '^\s*\%(#.*\)\=$'
       return 0
     endif
@@ -214,25 +208,25 @@ function! s:endof(lnum)
   return 0
 endfunction
 
-function! s:lastmethodline(...)
-  if a:0
-    let line = a:1
-  else
-    let line = line(&quot;.&quot;)
-  endif
-  while line &gt; 0 &amp;&amp; getline(line) !~ &amp;l:define
-    let line = line - 1
+function! s:lastopeningline(pattern,limit,...)
+  let line = a:0 ? a:1 : line(&quot;.&quot;)
+  while line &gt; a:limit &amp;&amp; getline(line) !~ a:pattern
+    let line -= 1
   endwhile
   let lend = s:endof(line)
-  if lend &lt; 0 || lend &gt;= (a:0 ? a:1 : line(&quot;.&quot;))
+  if line &gt; a:limit &amp;&amp; (lend &lt; 0 || lend &gt;= (a:0 ? a:1 : line(&quot;.&quot;)))
     return line
   else
-    return 0
+    return -1
   endif
 endfunction
 
-function! s:lastmethod()
-  let line = s:lastmethodline()
+function! s:lastmethodline(...)
+  return s:lastopeningline(&amp;l:define,0,a:0 ? a:1 : line(&quot;.&quot;))
+endfunction
+
+function! s:lastmethod(...)
+  let line = s:lastmethodline(a:0 ? a:1 : line(&quot;.&quot;))
   if line
     return s:sub(matchstr(getline(line),'\%('.&amp;define.'\)\zs\h\%(\k\|[:.]\)*[?!=]\='),':$','')
   else
@@ -241,21 +235,7 @@ function! s:lastmethod()
 endfunction
 
 function! s:lastrespondtoline(...)
-  let mline = s:lastmethodline()
-  if a:0
-    let line = a:1
-  else
-    let line = line(&quot;.&quot;)
-  endif
-  while line &gt; mline &amp;&amp; getline(line) !~ '\C^\s*respond_to\s*\%(\&lt;do\)\s*|\zs\h\k*\ze|'
-    let line = line - 1
-  endwhile
-  let lend = s:endof(line)
-  if lend &gt;= (a:0 ? a:1 : line(&quot;.&quot;))
-    return line
-  else
-    return -1
-  endif
+  return s:lastopeningline('\C^\s*respond_to\s*\%(\&lt;do\)\s*|\zs\h\k*\ze|',s:lastmethodline(), a:0 ? a:1 : line(&quot;.&quot;))
 endfunction
 
 function! s:lastformat()
@@ -268,7 +248,7 @@ function! s:lastformat()
       if match != ''
         return match
       endif
-      let line = line - 1
+      let line -= 1
     endwhile
   endif
   return &quot;&quot;
@@ -281,15 +261,7 @@ function! s:format(...)
     let format = s:lastformat()
   endif
   if format == ''
-    if fnamemodify(RailsFilePath(),':e') == 'rhtml'
-      let format = 'html'
-    elseif fnamemodify(RailsFilePath(),':e') == 'rxml'
-      let format = 'xml'
-    elseif fnamemodify(RailsFilePath(),':e') == 'rjs'
-      let format = 'js'
-    elseif a:0
-      return a:1
-    endif
+    return get({'rhtml': 'html', 'rxml': 'xml', 'rjs': 'js'},fnamemodify(RailsFilePath(),':e'),a:0 ? a:1 : '')
   endif
   return format
 endfunction
@@ -333,7 +305,7 @@ function! s:controller(...)
   elseif f =~ '\&lt;public/stylesheets/.*\.css$'
     return s:sub(f,'.*&lt;public/stylesheets/(.{-})\.css$','\1')
   elseif a:0 &amp;&amp; a:1
-    return s:pluralize(s:model())
+    return rails#pluralize(s:model())
   endif
   return &quot;&quot;
 endfunction
@@ -354,62 +326,29 @@ function! s:model(...)
   elseif f =~ '\&lt;spec/models/.*_spec\.rb$'
     return s:sub(f,'.*&lt;spec/models/(.*)_spec\.rb$','\1')
   elseif f =~ '\&lt;\%(test\|spec\)/fixtures/.*\.\w*\~\=$'
-    return s:singularize(s:sub(f,'.*&lt;%(test|spec)/fixtures/(.*)\.\w*\~=$','\1'))
+    return rails#singularize(s:sub(f,'.*&lt;%(test|spec)/fixtures/(.*)\.\w*\~=$','\1'))
+  elseif f =~ '\&lt;\%(test\|spec\)/exemplars/.*_exemplar\.rb$'
+    return s:sub(f,'.*&lt;%(test|spec)/exemplars/(.*)_exemplar\.rb$','\1')
   elseif a:0 &amp;&amp; a:1
-    return s:singularize(s:controller())
+    return rails#singularize(s:controller())
   endif
   return &quot;&quot;
 endfunction
 
-function! s:underscore(str)
-  let str = s:gsub(a:str,'::','/')
-  let str = s:gsub(str,'(\u+)(\u\l)','\1_\2')
-  let str = s:gsub(str,'(\l|\d)(\u)','\1_\2')
-  let str = s:gsub(str,'-','_')
-  let str = tolower(str)
-  return str
-endfunction
-
-function! s:camelize(str)
-  let str = s:gsub(a:str,'/(.)','::\u\1')
-  let str = s:gsub(str,'%([_-]|&lt;)(.)','\u\1')
-  return str
-endfunction
-
-function! s:singularize(word)
-  &quot; Probably not worth it to be as comprehensive as Rails but we can
-  &quot; still hit the common cases.
-  let word = a:word
-  if word =~? '\.js$' || word == ''
-    return word
-  endif
-  let word = s:sub(word,'eople$','ersons')
-  let word = s:sub(word,'[aeio]@&lt;!ies$','ys')
-  let word = s:sub(word,'xe[ns]$','xs')
-  let word = s:sub(word,'ves$','fs')
-  let word = s:sub(word,'ss%(es)=$','sss')
-  let word = s:sub(word,'s$','')
-  return word
-endfunction
-
-function! s:pluralize(word)
-  let word = a:word
-  if word == ''
-    return word
+function! s:readfile(path,...)
+  let nr = bufnr('^'.a:path.'$')
+  if nr &lt; 0 &amp;&amp; exists('+shellslash') &amp;&amp; ! &amp;shellslash
+    let nr = bufnr('^'.s:gsub(a:path,'/','\\').'$')
   endif
-  let word = s:sub(word,'[aeio]@&lt;!y$','ie')
-  let word = s:sub(word,'%([osxz]|[cs]h)$','&amp;e')
-  let word = s:sub(word,'f@&lt;!f$','ve')
-  let word = word.&quot;s&quot;
-  let word = s:sub(word,'ersons$','eople')
-  return word
-endfunction
-
-function! s:usesubversion()
-  if !exists(&quot;b:rails_use_subversion&quot;)
-    let b:rails_use_subversion = s:getopt(&quot;subversion&quot;,&quot;abg&quot;) &amp;&amp; (RailsRoot()!=&quot;&quot;) &amp;&amp; isdirectory(RailsRoot().&quot;/.svn&quot;)
+  if bufloaded(nr)
+    return getbufline(nr,1,a:0 ? a:1 : '$')
+  elseif !filereadable(a:path)
+    return []
+  elseif a:0
+    return readfile(a:path,'',a:1)
+  else
+    return readfile(a:path)
   endif
-  return b:rails_use_subversion
 endfunction
 
 function! s:environment()
@@ -420,13 +359,8 @@ function! s:environment()
   endif
 endfunction
 
-function! s:environments(...)
-  let e = s:getopt(&quot;environment&quot;,&quot;abg&quot;)
-  if e == ''
-    return &quot;development\ntest\nproduction&quot;
-  else
-    return s:gsub(e,'[:;,- ]',&quot;\n&quot;)
-  endif
+function! s:Complete_environments(...)
+  return s:completion_filter(rails#app().environments(),a:0 ? a:1 : &quot;&quot;)
 endfunction
 
 function! s:warn(str)
@@ -446,7 +380,7 @@ function! s:error(str)
 endfunction
 
 function! s:debug(str)
-  if g:rails_debug
+  if exists(&quot;g:rails_debug&quot;) &amp;&amp; g:rails_debug
     echohl Debug
     echomsg a:str
     echohl None
@@ -458,6 +392,59 @@ endfunction
 
 &quot; RailsRoot() is the only official public function
 
+function! rails#underscore(str)
+  let str = s:gsub(a:str,'::','/')
+  let str = s:gsub(str,'(\u+)(\u\l)','\1_\2')
+  let str = s:gsub(str,'(\l|\d)(\u)','\1_\2')
+  let str = tolower(str)
+  return str
+endfunction
+
+function! rails#camelize(str)
+  let str = s:gsub(a:str,'/(.=)','::\u\1')
+  let str = s:gsub(str,'%([_-]|&lt;)(.)','\u\1')
+  return str
+endfunction
+
+function! rails#singularize(word)
+  &quot; Probably not worth it to be as comprehensive as Rails but we can
+  &quot; still hit the common cases.
+  let word = a:word
+  if word =~? '\.js$' || word == ''
+    return word
+  endif
+  let word = s:sub(word,'eople$','ersons')
+  let word = s:sub(word,'[aeio]@&lt;!ies$','ys')
+  let word = s:sub(word,'xe[ns]$','xs')
+  let word = s:sub(word,'ves$','fs')
+  let word = s:sub(word,'ss%(es)=$','sss')
+  let word = s:sub(word,'s$','')
+  let word = s:sub(word,'%(tatus|lias)\zse$','')
+  let word = s:sub(word,'%(nd|rt)\zsice$','ex')
+  return word
+endfunction
+
+function! rails#pluralize(word)
+  let word = a:word
+  if word == ''
+    return word
+  endif
+  let word = s:sub(word,'[aeio]@&lt;!y$','ie')
+  let word = s:sub(word,'%(nd|rt)@&lt;=ex$','ice')
+  let word = s:sub(word,'%([osxz]|[cs]h)$','&amp;e')
+  let word = s:sub(word,'f@&lt;!f$','ve')
+  let word .= 's'
+  let word = s:sub(word,'ersons$','eople')
+  return word
+endfunction
+
+function! rails#app(...)
+  let root = a:0 ? a:1 : RailsRoot()
+  &quot; TODO: populate dynamically
+  &quot; TODO: normalize path
+  return get(s:apps,root,0)
+endfunction
+
 function! RailsRevision()
   return 1000*matchstr(g:autoloaded_rails,'^\d\+')+matchstr(g:autoloaded_rails,'[1-9]\d*$')
 endfunction
@@ -478,9 +465,17 @@ function! RailsFilePath()
   endif
   let f = s:gsub(expand('%:p'),'\\ @!','/')
   let f = s:sub(f,'/$','')
-  if s:gsub(b:rails_root,'\\ @!','/') == strpart(f,0,strlen(b:rails_root))
+  let sep = matchstr(f,'^[^\\/]\{3,\}\zs[\\/]')
+  if sep != &quot;&quot;
+    let f = getcwd().sep.f
+  endif
+  if s:startswith(f,s:gsub(b:rails_root,'\\ @!','/')) || f == &quot;&quot;
     return strpart(f,strlen(b:rails_root)+1)
   else
+    if !exists(&quot;s:path_warn&quot;)
+      let s:path_warn = 1
+      call s:warn(&quot;File &quot;.f.&quot; does not appear to be under the Rails root &quot;.b:rails_root.&quot;. Please report to the rails.vim author!&quot;)
+    endif
     return f
   endif
 endfunction
@@ -492,19 +487,28 @@ endfunction
 function! RailsFileType()
   if !exists(&quot;b:rails_root&quot;)
     return &quot;&quot;
-  elseif exists(&quot;b:rails_file_type&quot;)
-    return b:rails_file_type
   elseif exists(&quot;b:rails_cached_file_type&quot;)
     return b:rails_cached_file_type
+  else
+    return rails#app().calculate_file_type(RailsFilePath())
   endif
-  let f = RailsFilePath()
-  let e = fnamemodify(RailsFilePath(),':e')
+endfunction
+
+function! s:app_calculate_file_type(path) dict
+  let f = a:path
+  let e = fnamemodify(f,':e')
   let r = &quot;&quot;
-  let top = getline(1).&quot; &quot;.getline(2).&quot; &quot;.getline(3).&quot; &quot;.getline(4).&quot; &quot;.getline(5).getline(6).&quot; &quot;.getline(7).&quot; &quot;.getline(8).&quot; &quot;.getline(9).&quot; &quot;.getline(10)
+  let full_path = self.path(f)
+  let nr = bufnr('^'.full_path.'$')
+  if nr &lt; 0 &amp;&amp; exists('+shellslash') &amp;&amp; ! &amp;shellslash
+    let nr = bufnr('^'.s:gsub(full_path,'/','\\').'$')
+  endif
   if f == &quot;&quot;
     let r = f
+  elseif nr &gt; 0 &amp;&amp; getbufvar(nr,'rails_file_type') != ''
+    return getbufvar(nr,'rails_file_type')
   elseif f =~ '_controller\.rb$' || f =~ '\&lt;app/controllers/.*\.rb$'
-    if top =~ '\&lt;wsdl_service_name\&gt;'
+    if join(s:readfile(full_path,50),&quot;\n&quot;) =~ '\&lt;wsdl_service_name\&gt;'
       let r = &quot;controller-api&quot;
     else
       let r = &quot;controller&quot;
@@ -518,12 +522,12 @@ function! RailsFileType()
   elseif f =~ '_helper\.rb$'
     let r = &quot;helper&quot;
   elseif f =~ '\&lt;app/models\&gt;'
+    let top = join(s:readfile(full_path,50),&quot;\n&quot;)
     let class = matchstr(top,'\&lt;Acti\w\w\u\w\+\%(::\h\w*\)\+\&gt;')
-    if class == &quot;ActiveResoure::Base&quot;
+    if class == &quot;ActiveResource::Base&quot;
       let class = &quot;ares&quot;
       let r = &quot;model-ares&quot;
     elseif class != ''
-      &quot;let class = s:sub(class,'::Base$','')
       let class = tolower(s:gsub(class,'[^A-Z]',''))
       let r = &quot;model-&quot;.s:sub(class,'^amb&gt;','mailer')
     elseif f =~ '_mailer\.rb$'
@@ -545,8 +549,18 @@ function! RailsFileType()
     let r = &quot;test-functional&quot;
   elseif f =~ '\&lt;test/integration/.*_test\.rb$'
     let r = &quot;test-integration&quot;
+  elseif f =~ '\&lt;spec/lib/.*_spec\.rb$'
+    let r = 'spec-lib'
+  elseif f =~ '\&lt;lib/.*\.rb$'
+    let r = 'lib'
   elseif f =~ '\&lt;spec/\w*s/.*_spec\.rb$'
     let r = s:sub(f,'.*&lt;spec/(\w*)s/.*','spec-\1')
+  elseif f =~ '\&lt;features/.*\.feature$'
+    let r = 'cucumber-feature'
+  elseif f =~ '\&lt;features/step_definitions/.*_steps\.rb$'
+    let r = 'cucumber-steps'
+  elseif f =~ '\&lt;features/.*\.rb$'
+    let r = 'cucumber'
   elseif f =~ '\&lt;\%(test\|spec\)/fixtures\&gt;'
     if e == &quot;yml&quot;
       let r = &quot;fixtures-yaml&quot;
@@ -573,39 +587,99 @@ function! RailsFileType()
   return r
 endfunction
 
-function! RailsType()
-  return RailsFileType()
+function! s:app_environments() dict
+  if self.cache.needs('environments')
+    call self.cache.set('environments',self.relglob('config/environments/','**/*','.rb'))
+  endif
+  return copy(self.cache.get('environments'))
 endfunction
 
-function! RailsEval(ruby,...)
-  if !exists(&quot;b:rails_root&quot;)
-    return a:0 ? a:1 : &quot;&quot;
-  elseif a:0
-    return s:railseval(a:ruby,a:1)
+&quot; Check for Test::Unit and rSpec by calling this method with an argument of
+&quot; 'test' or 'spec'.  Given no arguments, returns a list.
+function! s:app_test_suites(...) dict
+  if self.cache.needs('test_suites')
+    let suites = filter(['test','spec'],'isdirectory(self.path(v:val))')
+    call self.cache.set('test_suites',suites)
+  endif
+  if a:0
+    return index(self.cache.get('test_suites'),a:1) + 1
   else
-    return s:railseval(a:ruby)
+    return copy(self.cache.get('test_suites'))
   endif
 endfunction
 
+call s:add_methods('app',['calculate_file_type','environments','test_suites'])
+
 &quot; }}}1
-&quot; Autocommand Functions {{{1
+&quot; Ruby Execution {{{1
 
-function! s:QuickFixCmdPre()
-  if exists(&quot;b:rails_root&quot;)
-    if strpart(getcwd(),0,strlen(RailsRoot())) != RailsRoot()
-      let s:last_dir = getcwd()
-      echo &quot;lchdir &quot;.s:ra()
-      &quot;exe &quot;lchdir &quot;.s:ra()
-      lchdir `=RailsRoot()`
+function! s:app_ruby_shell_command(cmd) dict abort
+  if self.path() =~ '://'
+    return &quot;ruby &quot;.a:cmd
+  else
+    return &quot;ruby -C &quot;.s:rquote(self.path()).&quot; &quot;.a:cmd
+  endif
+endfunction
+
+function! s:app_background_ruby_command(cmd) dict abort
+  let cmd = s:esccmd(self.ruby_shell_command(a:cmd))
+  if has_key(self,'options') &amp;&amp; has_key(self.options,'gnu_screen')
+    let screen = self.options.gnu_screen
+  else
+    let screen = g:rails_gnu_screen
+  endif
+  if has(&quot;gui_win32&quot;)
+    if &amp;shellcmdflag == &quot;-c&quot; &amp;&amp; ($PATH . &amp;shell) =~? 'cygwin'
+      silent exe &quot;!cygstart -d &quot;.s:rquote(self.path()).&quot; ruby &quot;.a:cmd
+    else
+      exe &quot;!start &quot;.cmd
     endif
+  elseif exists(&quot;$STY&quot;) &amp;&amp; !has(&quot;gui_running&quot;) &amp;&amp; screen &amp;&amp; executable(&quot;screen&quot;)
+    silent exe &quot;!screen -ln -fn -t &quot;.s:sub(s:sub(a:cmd,'\s.*',''),'^%(script|-rcommand)/','rails-').' '.cmd
+  else
+    exe &quot;!&quot;.cmd
+  endif
+  return v:shell_error
+endfunction
+
+function! s:app_execute_ruby_command(cmd) dict abort
+  exe &quot;!&quot;.s:esccmd(self.ruby_shell_command(a:cmd))
+  return v:shell_error
+endfunction
+
+function! s:app_lightweight_ruby_eval(ruby,...) dict abort
+  let def = a:0 ? a:1 : &quot;&quot;
+  if !executable(&quot;ruby&quot;)
+    return def
   endif
+  let args = '-e '.s:rquote('begin; require %{rubygems}; rescue LoadError; end; begin; require %{active_support}; rescue LoadError; end; '.a:ruby)
+  let cmd = self.ruby_shell_command(args)
+  &quot; If the shell is messed up, this command could cause an error message
+  silent! let results = system(cmd)
+  return v:shell_error == 0 ? results : def
 endfunction
 
-function! s:QuickFixCmdPost()
-  if exists(&quot;s:last_dir&quot;)
-    &quot;exe &quot;lchdir &quot;.s:escarg(s:last_dir)
-    lchdir `=s:last_dir`
-    unlet s:last_dir
+function! s:app_eval(ruby,...) dict abort
+  let def = a:0 ? a:1 : &quot;&quot;
+  if !executable(&quot;ruby&quot;)
+    return def
+  endif
+  let args = &quot;-r./config/boot -r &quot;.s:rquote(self.path(&quot;config/environment&quot;)).&quot; -e &quot;.s:rquote(a:ruby)
+  let cmd = self.ruby_shell_command(args)
+  &quot; If the shell is messed up, this command could cause an error message
+  silent! let results = system(cmd)
+  return v:shell_error == 0 ? results : def
+endfunction
+
+call s:add_methods('app', ['ruby_shell_command','execute_ruby_command','background_ruby_command','lightweight_ruby_eval','eval'])
+
+function! RailsEval(ruby,...) abort
+  if !exists(&quot;b:rails_root&quot;)
+    return a:0 ? a:1 : &quot;&quot;
+  elseif a:0
+    return rails#app().eval(a:ruby,a:1)
+  else
+    return rails#app().eval(a:ruby)
   endif
 endfunction
 
@@ -636,14 +710,14 @@ function! RailsHelpCommand(...)
 endfunction
 
 function! s:BufCommands()
-  call s:BufFinderCommands() &quot; Provides Rcommand!
+  call s:BufFinderCommands()
   call s:BufNavCommands()
   call s:BufScriptWrappers()
-  Rcommand! -buffer -bar -nargs=? -bang -complete=custom,s:RakeComplete    Rake     :call s:Rake(&lt;bang&gt;0,&lt;q-args&gt;)
-  Rcommand! -buffer -bar -nargs=? -bang -complete=custom,s:PreviewComplete Rpreview :call s:Preview(&lt;bang&gt;0,&lt;q-args&gt;)
-  Rcommand! -buffer -bar -nargs=? -bang -complete=custom,s:environments    Rlog     :call s:Log(&lt;bang&gt;0,&lt;q-args&gt;)
-  Rcommand! -buffer -bar -nargs=* -bang -complete=custom,s:SetComplete     Rset     :call s:Set(&lt;bang&gt;0,&lt;f-args&gt;)
-  command! -buffer -bar -nargs=0 Rtags       :call s:Tags(&lt;bang&gt;0)
+  command! -buffer -bar -nargs=? -bang -count -complete=customlist,s:Complete_rake    Rake     :call s:Rake(&lt;bang&gt;0,!&lt;count&gt; &amp;&amp; &lt;line1&gt; ? -1 : &lt;count&gt;,&lt;q-args&gt;)
+  command! -buffer -bar -nargs=? -bang -range -complete=customlist,s:Complete_preview Rpreview :call s:Preview(&lt;bang&gt;0,&lt;line1&gt;,&lt;q-args&gt;)
+  command! -buffer -bar -nargs=? -bang -complete=customlist,s:Complete_environments   Rlog     :call s:Log(&lt;bang&gt;0,&lt;q-args&gt;)
+  command! -buffer -bar -nargs=* -bang -complete=customlist,s:Complete_set            Rset     :call s:Set(&lt;bang&gt;0,&lt;f-args&gt;)
+  command! -buffer -bar -nargs=0 Rtags       :call rails#app().tags_command()
   &quot; Embedding all this logic directly into the command makes the error
   &quot; messages more concise.
   command! -buffer -bar -nargs=? -bang Rdoc  :
@@ -652,15 +726,17 @@ function! s:BufCommands()
         \ else | call s:Doc(&lt;bang&gt;0,&lt;q-args&gt;) | endif
   command! -buffer -bar -nargs=0 -bang Rrefresh :if &lt;bang&gt;0|unlet! g:autoloaded_rails|source `=s:file`|endif|call s:Refresh(&lt;bang&gt;0)
   if exists(&quot;:Project&quot;)
-    command! -buffer -bar -nargs=? -bang  Rproject :call s:Project(&lt;bang&gt;0,&lt;q-args&gt;)
+    command! -buffer -bar -nargs=? Rproject :call s:Project(&lt;bang&gt;0,&lt;q-args&gt;)
+  elseif exists(&quot;:NERDTree&quot;)
+    command! -buffer -bar -nargs=? Rproject :NERDTree `=rails#app().path()`
   endif
   if exists(&quot;g:loaded_dbext&quot;)
-    Rcommand! -buffer -bar -nargs=? -bang  -complete=custom,s:environments   Rdbext   :call s:BufDatabase(2,&lt;q-args&gt;,&lt;bang&gt;0)
+    command! -buffer -bar -nargs=? -bang  -complete=customlist,s:Complete_environments Rdbext  :call s:BufDatabase(2,&lt;q-args&gt;,&lt;bang&gt;0)|let b:dbext_buffer_defaulted = 1
   endif
   let ext = expand(&quot;%:e&quot;)
   if ext =~ s:viewspattern()
     &quot; TODO: complete controller names with trailing slashes here
-    Rcommand! -buffer -bar -nargs=? -range -complete=custom,s:controllerList Rextract :&lt;line1&gt;,&lt;line2&gt;call s:Extract(&lt;bang&gt;0,&lt;f-args&gt;)
+    command! -buffer -bar -nargs=? -range -complete=customlist,s:controllerList Rextract :&lt;line1&gt;,&lt;line2&gt;call s:Extract(&lt;bang&gt;0,&lt;f-args&gt;)
     command! -buffer -bar -nargs=? -range Rpartial :call s:warn(&quot;Warning: :Rpartial has been deprecated in favor of :Rextract&quot;) | &lt;line1&gt;,&lt;line2&gt;Rextract&lt;bang&gt; &lt;args&gt;
   endif
   if RailsFilePath() =~ '\&lt;db/migrate/.*\.rb$'
@@ -676,8 +752,8 @@ function! s:Doc(bang, string)
     else
       return s:error(&quot;specify a g:rails_search_url with %s for a query placeholder&quot;)
     endif
-  elseif isdirectory(RailsRoot().&quot;/doc/api/classes&quot;)
-    let url = RailsRoot().&quot;/doc/api/index.html&quot;
+  elseif isdirectory(rails#app().path(&quot;doc/api/classes&quot;))
+    let url = rails#app().path(&quot;/doc/api/index.html&quot;)
   elseif s:getpidfor(&quot;0.0.0.0&quot;,&quot;8808&quot;) &gt; 0
     let url = &quot;http://localhost:8808&quot;
   else
@@ -697,7 +773,7 @@ function! s:Log(bang,arg)
   else
     let lf = &quot;log/&quot;.a:arg.&quot;.log&quot;
   endif
-  let size = getfsize(RailsRoot().&quot;/&quot;.lf)
+  let size = getfsize(rails#app().path(lf))
   if size &gt;= 1048576
     call s:warn(&quot;Log file is &quot;.((size+512)/1024).&quot;KB.  Consider :Rake log:clear&quot;)
   endif
@@ -706,16 +782,14 @@ function! s:Log(bang,arg)
     clast
   else
     if exists(&quot;:Tail&quot;)
-      &quot; TODO: check if :Tail works with `=`
-      exe &quot;Tail &quot;.s:ra().'/'.lf
+      Tail  `=rails#app().path(lf)`
     else
-      &quot;exe &quot;pedit &quot;.s:ra().'/'.lf
-      pedit `=RailsRoot().'/'.lf`
+      pedit `=rails#app().path(lf)`
     endif
   endif
 endfunction
 
-function! RailsNewApp(bang,...)
+function! rails#new_app_command(bang,...)
   if a:0 == 0
     if a:bang
       echo &quot;rails.vim version &quot;.g:autoloaded_rails
@@ -735,30 +809,24 @@ function! RailsNewApp(bang,...)
   let str = &quot;&quot;
   let c = 1
   while c &lt;= a:0
-    let str = str . &quot; &quot; . s:rquote(expand(a:{c}))
-    let c = c + 1
+    let str .= &quot; &quot; . s:rquote(expand(a:{c}))
+    let c += 1
   endwhile
-  &quot;let str = s:sub(str,'^ ','')
   let dir = expand(dir)
-  if isdirectory(fnamemodify(dir,':h').&quot;/.svn&quot;) &amp;&amp; g:rails_subversion
-    let append = &quot; -c&quot;
-  else
-    let append = &quot;&quot;
-  endif
+  let append = &quot;&quot;
   if g:rails_default_database != &quot;&quot; &amp;&amp; str !~ '-d \|--database='
-    let append = append.&quot; -d &quot;.g:rails_default_database
+    let append .= &quot; -d &quot;.g:rails_default_database
   endif
   if a:bang
-    let append = append.&quot; --force&quot;
+    let append .= &quot; --force&quot;
   endif
   exe &quot;!rails&quot;.append.str
   if filereadable(dir.&quot;/&quot;.g:rails_default_file)
-    &quot;exe &quot;edit &quot;.s:escarg(dir).&quot;/&quot;.g:rails_default_file
     edit `=dir.'/'.g:rails_default_file`
   endif
 endfunction
 
-function! s:Tags(bang)
+function! s:app_tags_command() dict
   if exists(&quot;g:Tlist_Ctags_Cmd&quot;)
     let cmd = g:Tlist_Ctags_Cmd
   elseif executable(&quot;exuberant-ctags&quot;)
@@ -772,36 +840,37 @@ function! s:Tags(bang)
   else
     return s:error(&quot;ctags not found&quot;)
   endif
-  exe &quot;!&quot;.cmd.&quot; -R &quot;.s:ra()
+  exe &quot;!&quot;.cmd.&quot; -f &quot;.s:escarg(self.path(&quot;tmp/tags&quot;)).&quot; --exclude=facebox.js --exclude=\&quot;*.*.js\&quot; --langmap=\&quot;ruby:+.rake.builder.rjs\&quot; -R &quot;.s:escarg(self.path())
 endfunction
 
+call s:add_methods('app',['tags_command'])
+
 function! s:Refresh(bang)
-  &quot; What else?
-  if a:bang
-    unlet! s:rails_helper_methods
-  endif
   if exists(&quot;g:rubycomplete_rails&quot;) &amp;&amp; g:rubycomplete_rails &amp;&amp; has(&quot;ruby&quot;)
     silent! ruby ActiveRecord::Base.reset_subclasses if defined?(ActiveRecord)
-    silent! ruby Dependencies.clear if defined?(Dependencies)
+    silent! ruby if defined?(ActiveSupport::Dependencies); ActiveSupport::Dependencies.clear; elsif defined?(Dependencies); Dependencies.clear; end
     if a:bang
       silent! ruby ActiveRecord::Base.clear_reloadable_connections! if defined?(ActiveRecord)
     endif
   endif
-  call s:cacheclear()
+  call rails#app().cache.clear()
   silent doautocmd User BufLeaveRails
-  if a:bang &amp;&amp; s:cacheworks()
-    let s:cache = {}
+  if a:bang
+    for key in keys(s:apps)
+      if type(s:apps[key]) == type({})
+        call s:apps[key].cache.clear()
+      endif
+      call extend(s:apps[key],filter(copy(s:app_prototype),'type(v:val) == type(function(&quot;tr&quot;))'),'force')
+    endfor
   endif
   let i = 1
   let max = bufnr('$')
   while i &lt;= max
     let rr = getbufvar(i,&quot;rails_root&quot;)
     if rr != &quot;&quot;
-      unlet! s:user_classes_{s:escvar(rr)}
-      unlet! s:dbext_type_{s:escvar(rr)}
       call setbufvar(i,&quot;rails_refresh&quot;,1)
     endif
-    let i = i + 1
+    let i += 1
   endwhile
   silent doautocmd User BufEnterRails
 endfunction
@@ -809,7 +878,7 @@ endfunction
 function! s:RefreshBuffer()
   if exists(&quot;b:rails_refresh&quot;) &amp;&amp; b:rails_refresh
     let oldroot = b:rails_root
-    unlet! b:rails_root b:rails_use_subversion
+    unlet! b:rails_root
     let b:rails_refresh = 0
     call RailsBufInit(oldroot)
     unlet! b:rails_refresh
@@ -819,7 +888,25 @@ endfunction
 &quot; }}}1
 &quot; Rake {{{1
 
-&quot; Depends: s:rubyexestrwithfork, s:sub, s:lastmethodline, s:getopt, s;rquote, s:QuickFixCmdPre, ...
+function! s:app_rake_tasks() dict
+  if self.cache.needs('rake_tasks')
+    call s:push_chdir()
+    try
+      let lines = split(system(&quot;rake -T&quot;),&quot;\n&quot;)
+    finally
+      call s:pop_command()
+    endtry
+    if v:shell_error != 0
+      return []
+    endif
+    call map(lines,'matchstr(v:val,&quot;^rake\\s\\+\\zs\\S*&quot;)')
+    call filter(lines,'v:val != &quot;&quot;')
+    call self.cache.set('rake_tasks',lines)
+  endif
+  return self.cache.get('rake_tasks')
+endfunction
+
+call s:add_methods('app', ['rake_tasks'])
 
 &quot; Current directory
 let s:efm='%D(in\ %f),'
@@ -887,12 +974,18 @@ function! s:makewithruby(arg,...)
     endif
   endif
   let old_make = &amp;makeprg
-  let &amp;l:makeprg = s:rubyexestrwithfork(a:arg)
+  let &amp;l:makeprg = rails#app().ruby_shell_command(a:arg)
   make
   let &amp;l:makeprg = old_make
 endfunction
 
-function! s:Rake(bang,arg)
+function! s:Rake(bang,lnum,arg)
+  let self = rails#app()
+  let lnum = a:lnum &lt; 0 ? 0 : a:lnum
+  if &amp;l:makeprg !~# 'rake'
+    let old_make = &amp;makeprg
+    let &amp;makeprg = 'rake'
+  endif
   let oldefm = &amp;efm
   if a:bang
     let &amp;l:errorformat = s:efm_backtrace
@@ -900,8 +993,8 @@ function! s:Rake(bang,arg)
   let t = RailsFileType()
   let arg = a:arg
   if &amp;filetype == &quot;ruby&quot; &amp;&amp; arg == '' &amp;&amp; g:rails_modelines
-    let lnum = s:lastmethodline()
-    let str = getline(lnum).&quot;\n&quot;.getline(lnum+1).&quot;\n&quot;.getline(lnum+2).&quot;\n&quot;
+    let mnum = s:lastmethodline(lnum)
+    let str = getline(mnum).&quot;\n&quot;.getline(mnum+1).&quot;\n&quot;.getline(mnum+2).&quot;\n&quot;
     let pat = '\s\+\zs.\{-\}\ze\%(\n\|\s\s\|#{\@!\|$\)'
     let mat = matchstr(str,'#\s*rake'.pat)
     let mat = s:sub(mat,'\s+$','')
@@ -915,25 +1008,25 @@ function! s:Rake(bang,arg)
       let arg = opt
     endif
   endif
-  let withrubyargs = '-r ./config/boot -r '.s:rquote(RailsRoot().'/config/environment').' -e &quot;puts \%((in \#{Dir.getwd}))&quot; '
-  if arg =~# '^\%(stats\|routes\|notes\|db:\%(charset\|collation\|version\)\)\%(:\|$\)'
+  let withrubyargs = '-r ./config/boot -r '.s:rquote(self.path('config/environment')).' -e &quot;puts \%((in \#{Dir.getwd}))&quot; '
+  if arg =~# '^\%(stats\|routes\|secret\|notes\|db:\%(charset\|collation\|fixtures:identify\|version\)\)\%(:\|$\)'
     &quot; So you can see the output even with an inadequate redirect
-    call s:QuickFixCmdPre()
+    call s:push_chdir()
     exe &quot;!&quot;.&amp;makeprg.&quot; &quot;.arg
-    call s:QuickFixCmdPost()
+    call s:pop_command()
   elseif arg =~ '^preview\&gt;'
-    exe 'R'.s:gsub(arg,':','/')
+    exe (lnum == 0 ? '' : lnum).'R'.s:gsub(arg,':','/')
   elseif arg =~ '^runner:'
     let arg = s:sub(arg,'^runner:','')
     let root = matchstr(arg,'%\%(:\w\)*')
     let file = expand(root).matchstr(arg,'%\%(:\w\)*\zs.*')
-    if file =~ '[@#].*$'
-      let extra = &quot; -- -n &quot;.matchstr(file,'[@#]\zs.*')
-      let file = s:sub(file,'[@#].*','')
+    if file =~ '#.*$'
+      let extra = &quot; -- -n &quot;.matchstr(file,'#\zs.*')
+      let file = s:sub(file,'#.*','')
     else
       let extra = ''
     endif
-    if s:hasfile(file) || s:hasfile(file.'.rb')
+    if self.has_file(file) || self.has_file(file.'.rb')
       call s:makewithruby(withrubyargs.'-r&quot;'.file.'&quot;'.extra,file !~# '_\%(spec\|test\)\%(\.rb\)\=$')
     else
       call s:makewithruby(withrubyargs.'-e '.s:esccmd(s:rquote(arg)))
@@ -943,28 +1036,40 @@ function! s:Rake(bang,arg)
   elseif arg =~ '^run:'
     let arg = s:sub(arg,'^run:','')
     let arg = s:sub(arg,'^%:h',expand('%:h'))
-    let arg = s:sub(arg,'^%(\%|$|[@#]@=)',expand('%'))
-    let arg = s:sub(arg,'[@#](\w+)$',' -- -n\1')
+    let arg = s:sub(arg,'^%(\%|$|#@=)',expand('%'))
+    let arg = s:sub(arg,'#(\w+[?!=]=)$',' -- -n\1')
     call s:makewithruby(withrubyargs.'-r'.arg,arg !~# '_\%(spec\|test\)\.rb$')
   elseif arg != ''
     exe 'make '.arg
+  elseif t =~ '^config-routes\&gt;'
+    call s:push_chdir()
+    exe &quot;!&quot;.&amp;makeprg.&quot; routes&quot;
+    call s:pop_command()
+  elseif t =~ '^fixtures-yaml\&gt;' &amp;&amp; lnum
+    call s:push_chdir()
+    exe &quot;!&quot;.&amp;makeprg.&quot; db:fixtures:identify LABEL=&quot;.s:lastmethod(lnum)
+    call s:pop_command()
+  elseif t =~ '^fixtures\&gt;' &amp;&amp; lnum == 0
+    exe &quot;make db:fixtures:load FIXTURES=&quot;.s:sub(fnamemodify(RailsFilePath(),':r'),'^.{-}/fixtures/','')
   elseif t =~ '^task\&gt;'
-    let lnum = s:lastmethodline()
-    let line = getline(lnum)
+    let mnum = s:lastmethodline(lnum)
+    let line = getline(mnum)
     &quot; We can't grab the namespace so only run tasks at the start of the line
     if line =~ '^\%(task\|file\)\&gt;'
-      exe 'make '.s:lastmethod()
+      exe 'make '.s:lastmethod(lnum)
     else
       make
     endif
   elseif t =~ '^spec\&gt;'
-    if RailsFilePath() =~# '\&lt;test/test_helper\.rb$'
+    if RailsFilePath() =~# '\&lt;spec/spec_helper\.rb$'
       make spec SPEC_OPTS=
+    elseif lnum &gt; 0
+      exe 'make spec SPEC=&quot;%:p&quot; SPEC_OPTS=--line='.lnum
     else
       make spec SPEC=&quot;%:p&quot; SPEC_OPTS=
     endif
   elseif t =~ '^test\&gt;'
-    let meth = s:lastmethod()
+    let meth = s:lastmethod(lnum)
     if meth =~ '^test_'
       let call = &quot; -n&quot;.meth.&quot;&quot;
     else
@@ -980,38 +1085,57 @@ function! s:Rake(bang,arg)
   elseif t=~ '^\%(db-\)\=migration\&gt;' &amp;&amp; RailsFilePath() !~# '\&lt;db/schema\.rb$'
     let ver = matchstr(RailsFilePath(),'\&lt;db/migrate/0*\zs\d*\ze_')
     if ver != &quot;&quot;
-      exe &quot;make db:migrate VERSION=&quot;.ver
+      let method = s:lastmethod(lnum)
+      if method == &quot;down&quot;
+        exe &quot;make db:migrate:down VERSION=&quot;.ver
+      elseif method == &quot;up&quot;
+        exe &quot;make db:migrate:up VERSION=&quot;.ver
+      elseif lnum &gt; 0
+        exe &quot;make db:migrate:down db:migrate:up VERSION=&quot;.ver
+      else
+        exe &quot;make db:migrate VERSION=&quot;.ver
+      endif
     else
       make db:migrate
     endif
+  elseif self.test_suites('spec') &amp;&amp; RailsFilePath() =~# '^app/.*\.rb' &amp;&amp; self.has_file(s:sub(RailsFilePath(),'^app/(.*)\.rb$','spec/\1_spec.rb'))
+    make spec SPEC=&quot;%:p:r:s?[\/]app[\/]?/spec/?_spec.rb&quot; SPEC_OPTS=
   elseif t=~ '^model\&gt;'
     make test:units TEST=&quot;%:p:r:s?[\/]app[\/]models[\/]?/test/unit/?_test.rb&quot;
   elseif t=~ '^api\&gt;'
     make test:units TEST=&quot;%:p:r:s?[\/]app[\/]apis[\/]?/test/functional/?_test.rb&quot;
   elseif t=~ '^\&lt;\%(controller\|helper\|view\)\&gt;'
     if RailsFilePath() =~ '\&lt;app/' &amp;&amp; s:controller() !~# '^\%(application\)\=$'
-      exe 'make test:functionals TEST=&quot;'.s:ra().'/test/functional/'.s:controller().'_controller_test.rb&quot;'
+      exe 'make test:functionals TEST=&quot;'.s:escarg(rails#app().path('test/functional/'.s:controller().'_controller_test.rb')).'&quot;'
     else
       make test:functionals
     endif
+  elseif t =~ '^cucumber-feature\&gt;'
+    if lnum &gt; 0
+      exe 'make features FEATURE=&quot;%:p&quot;:'.lnum
+    else
+      make features FEATURE=&quot;%:p&quot;
+    endif
+  elseif t =~ '^cucumber\&gt;'
+    make features
   else
     make
   endif
   if oldefm != ''
     let &amp;l:errorformat = oldefm
   endif
+  if exists('old_make')
+    let &amp;l:makeprg = old_make
+  endif
 endfunction
 
-function! s:RakeComplete(A,L,P)
-  return g:rails_rake_tasks
+function! s:Complete_rake(A,L,P)
+  return s:completion_filter(rails#app().rake_tasks(),a:A)
 endfunction
 
 &quot; }}}1
 &quot; Preview {{{1
 
-&quot; Depends: s:getopt, s:sub, s:controller, s:lastmethod
-&quot; Provides: s:initOpenURL
-
 function! s:initOpenURL()
   if !exists(&quot;:OpenURL&quot;)
     if has(&quot;gui_mac&quot;) || has(&quot;gui_macvim&quot;) || exists(&quot;$SECURITYSESSIONID&quot;)
@@ -1032,12 +1156,7 @@ function! s:scanlineforuri(lnum)
     let method = matchstr(url,'^\u\+')
     let url = matchstr(url,'\s\+\zs.*')
     if method !=? &quot;GET&quot;
-      if url =~ '?'
-        let url = url.'&amp;'
-      else
-        let url = url.'?'
-      endif
-      let url = url.'_method='.tolower(method)
+      let url .= (url =~ '?' ? '&amp;' : '?') . '_method='.tolower(method)
     endif
   endif
   if url != &quot;&quot;
@@ -1047,26 +1166,26 @@ function! s:scanlineforuri(lnum)
   endif
 endfunction
 
-function! s:defaultpreview()
+function! s:defaultpreview(lnum)
   let ret = ''
-  if s:getopt('preview','l') != ''
-    let uri = s:getopt('preview','l')
+  if s:getopt('preview','l',a:lnum) != ''
+    let uri = s:getopt('preview','l',a:lnum)
   elseif s:controller() != '' &amp;&amp; s:controller() != 'application' &amp;&amp; RailsFilePath() !~ '^public/'
     if RailsFileType() =~ '^controller\&gt;'
-      let start = s:lastmethodline() - 1
+      let start = s:lastmethodline(a:lnum) - 1
       if start + 1
         while getline(start) =~ '^\s*\%(#.*\)\=$'
           let ret = s:scanlineforuri(start).ret
-          let start = start - 1
+          let start -= 1
         endwhile
-        let ret = ret.s:controller().'/'.s:lastmethod().'/'
+        let ret .= s:controller().'/'.s:lastmethod(a:lnum).'/'
       else
-        let ret = ret.s:controller().'/'
+        let ret .= s:controller().'/'
       endif
     elseif s:getopt('preview','b') != ''
       let ret = s:getopt('preview','b')
     elseif RailsFileType() =~ '^view\%(-partial\|-layout\)\@!'
-      let ret = ret.s:controller().'/'.expand('%:t:r:r').'/'
+      let ret .= s:controller().'/'.expand('%:t:r:r').'/'
     endif
   elseif s:getopt('preview','b') != ''
     let uri = s:getopt('preview','b')
@@ -1078,7 +1197,7 @@ function! s:defaultpreview()
   return ret
 endfunction
 
-function! s:Preview(bang,arg)
+function! s:Preview(bang,lnum,arg)
   let root = s:getopt(&quot;root_url&quot;)
   if root == ''
     let root = s:getopt(&quot;url&quot;)
@@ -1089,7 +1208,7 @@ function! s:Preview(bang,arg)
   elseif a:arg != ''
     let uri = root.'/'.s:sub(a:arg,'^/','')
   else
-    let uri = matchstr(s:defaultpreview(),'.\{-\}\%(\n\@=\|$\)')
+    let uri = matchstr(s:defaultpreview(a:lnum),'.\{-\}\%(\n\@=\|$\)')
     let uri = root.'/'.s:sub(s:sub(uri,'^/',''),'/$','')
   endif
   call s:initOpenURL()
@@ -1109,7 +1228,7 @@ function! s:Preview(bang,arg)
         setlocal filetype=xhtml
       endif
     endif
-    call RailsBufInit(RailsRoot())
+    call RailsBufInit(rails#app().path())
     map &lt;buffer&gt; &lt;silent&gt; q :bwipe&lt;CR&gt;
     wincmd p
     if !a:bang
@@ -1118,47 +1237,61 @@ function! s:Preview(bang,arg)
   endif
 endfunction
 
-function! s:PreviewComplete(A,L,P)
-  return s:defaultpreview()
+function! s:Complete_preview(A,L,P)
+  return split(s:defaultpreview(a:L =~ '^\d' ? matchstr(a:L,'^\d\+') : line('.')),&quot;\n&quot;)
 endfunction
 
 &quot; }}}1
 &quot; Script Wrappers {{{1
 
-&quot; Depends: s:rquote, s:rubyexebg, s:rubyexe, s:rubyexestrwithfork, s:sub, s:getopt, s:usesubversion, s:user_classes_..., ..., s:pluginList, ...
-
 function! s:BufScriptWrappers()
-  Rcommand! -buffer -bar -nargs=+       -complete=custom,s:ScriptComplete   Rscript       :call s:Script(&lt;bang&gt;0,&lt;f-args&gt;)
-  Rcommand! -buffer -bar -nargs=*       -complete=custom,s:ConsoleComplete  Rconsole      :call s:Console(&lt;bang&gt;0,'console',&lt;f-args&gt;)
-  &quot;Rcommand! -buffer -bar -nargs=*                                           Rbreakpointer :call s:Console(&lt;bang&gt;0,'breakpointer',&lt;f-args&gt;)
-  Rcommand! -buffer -bar -nargs=*       -complete=custom,s:GenerateComplete Rgenerate     :call s:Generate(&lt;bang&gt;0,&lt;f-args&gt;)
-  Rcommand! -buffer -bar -nargs=*       -complete=custom,s:DestroyComplete  Rdestroy      :call s:Destroy(&lt;bang&gt;0,&lt;f-args&gt;)
-  Rcommand! -buffer -bar -nargs=? -bang -complete=custom,s:ServerComplete   Rserver       :call s:Server(&lt;bang&gt;0,&lt;q-args&gt;)
-  Rcommand! -buffer -bang -nargs=1 -range=0 -complete=custom,s:RubyComplete Rrunner       :call s:Runner(&lt;bang&gt;0 ? -2 : (&lt;count&gt;==&lt;line2&gt;?&lt;count&gt;:-1),&lt;f-args&gt;)
-  Rcommand! -buffer       -nargs=1 -range=0 -complete=custom,s:RubyComplete Rp            :call s:Runner(&lt;count&gt;==&lt;line2&gt;?&lt;count&gt;:-1,'p begin '.&lt;f-args&gt;.' end')
-  Rcommand! -buffer       -nargs=1 -range=0 -complete=custom,s:RubyComplete Rpp           :call s:Runner(&lt;count&gt;==&lt;line2&gt;?&lt;count&gt;:-1,'require %{pp}; pp begin '.&lt;f-args&gt;.' end')
-  Rcommand! -buffer       -nargs=1 -range=0 -complete=custom,s:RubyComplete Ry            :call s:Runner(&lt;count&gt;==&lt;line2&gt;?&lt;count&gt;:-1,'y begin '.&lt;f-args&gt;.' end')
-endfunction
-
-function! s:Script(bang,cmd,...)
+  command! -buffer -bar -nargs=*       -complete=customlist,s:Complete_script   Rscript       :call rails#app().script_command(&lt;bang&gt;0,&lt;f-args&gt;)
+  command! -buffer -bar -nargs=*       -complete=customlist,s:Complete_console  Rconsole      :call s:warn(&quot;Warning: :Rconsole has been deprecated in favor of :Rscript&quot;)|call rails#app().script_command(&lt;bang&gt;0,'console',&lt;f-args&gt;)
+  command! -buffer -bar -nargs=*       -complete=customlist,s:Complete_generate Rgenerate     :call rails#app().generate_command(&lt;bang&gt;0,&lt;f-args&gt;)
+  command! -buffer -bar -nargs=*       -complete=customlist,s:Complete_destroy  Rdestroy      :call rails#app().destroy_command(&lt;bang&gt;0,&lt;f-args&gt;)
+  command! -buffer -bar -nargs=? -bang -complete=customlist,s:Complete_server   Rserver       :call rails#app().server_command(&lt;bang&gt;0,&lt;q-args&gt;)
+  command! -buffer -bang -nargs=1 -range=0 -complete=customlist,s:Complete_ruby Rrunner       :call rails#app().runner_command(&lt;bang&gt;0 ? -2 : (&lt;count&gt;==&lt;line2&gt;?&lt;count&gt;:-1),&lt;f-args&gt;)
+  command! -buffer       -nargs=1 -range=0 -complete=customlist,s:Complete_ruby Rp            :call rails#app().runner_command(&lt;count&gt;==&lt;line2&gt;?&lt;count&gt;:-1,'p begin '.&lt;f-args&gt;.' end')
+  command! -buffer       -nargs=1 -range=0 -complete=customlist,s:Complete_ruby Rpp           :call rails#app().runner_command(&lt;count&gt;==&lt;line2&gt;?&lt;count&gt;:-1,'require %{pp}; pp begin '.&lt;f-args&gt;.' end')
+  command! -buffer       -nargs=1 -range=0 -complete=customlist,s:Complete_ruby Ry            :call rails#app().runner_command(&lt;count&gt;==&lt;line2&gt;?&lt;count&gt;:-1,'y begin '.&lt;f-args&gt;.' end')
+endfunction
+
+function! s:app_generators() dict
+  if self.cache.needs('generators')
+    let generators = self.relglob(&quot;vendor/plugins/&quot;,&quot;*/generators/*&quot;)
+    let generators += self.relglob(&quot;&quot;,&quot;lib/generators/*&quot;)
+    call filter(generators,'v:val =~ &quot;/$&quot;')
+    let generators += split(glob(expand(&quot;~/.rails/generators&quot;).&quot;/*&quot;),&quot;\n&quot;)
+    call map(generators,'s:sub(v:val,&quot;^.*[\\\\/]generators[\\\\/]\\ze.&quot;,&quot;&quot;)')
+    call map(generators,'s:sub(v:val,&quot;[\\\\/]$&quot;,&quot;&quot;)')
+    call self.cache.set('generators',generators)
+  endif
+  return sort(split(g:rails_generators,&quot;\n&quot;) + self.cache.get('generators'))
+endfunction
+
+function! s:app_script_command(bang,...) dict
   let str = &quot;&quot;
-  let c = 1
+  let cmd = a:0 ? a:1 : &quot;console&quot;
+  let c = 2
   while c &lt;= a:0
-    let str = str . &quot; &quot; . s:rquote(a:{c})
-    let c = c + 1
+    let str .= &quot; &quot; . s:rquote(a:{c})
+    let c += 1
   endwhile
-  if a:bang
-    call s:rubyexebg(s:rquote(&quot;script/&quot;.a:cmd).str)
+  if cmd ==# &quot;plugin&quot;
+    call self.cache.clear('generators')
+  endif
+  if a:bang || cmd =~# 'console'
+    return self.background_ruby_command(s:rquote(&quot;script/&quot;.cmd).str)
   else
-    call s:rubyexe(s:rquote(&quot;script/&quot;.a:cmd).str)
+    return self.execute_ruby_command(s:rquote(&quot;script/&quot;.cmd).str)
   endif
 endfunction
 
-function! s:Runner(count,args)
+function! s:app_runner_command(count,args) dict
   if a:count == -2
-    call s:Script(a:bang,&quot;runner&quot;,a:args)
+    return self.script_command(a:bang,&quot;runner&quot;,a:args)
   else
-    let str = s:rubyexestrwithfork('-r./config/boot -e &quot;require '.&quot;'commands/runner'&quot;.'&quot; '.s:rquote(a:args))
+    let str = self.ruby_shell_command('-r./config/boot -e &quot;require '.&quot;'commands/runner'&quot;.'&quot; '.s:rquote(a:args))
     let res = s:sub(system(str),'\n$','')
     if a:count &lt; 0
       echo res
@@ -1168,16 +1301,6 @@ function! s:Runner(count,args)
   endif
 endfunction
 
-function! s:Console(bang,cmd,...)
-  let str = &quot;&quot;
-  let c = 1
-  while c &lt;= a:0
-    let str = str . &quot; &quot; . s:rquote(a:{c})
-    let c = c + 1
-  endwhile
-  call s:rubyexebg(s:rquote(&quot;script/&quot;.a:cmd).str)
-endfunction
-
 function! s:getpidfor(bind,port)
     if has(&quot;win32&quot;) || has(&quot;win64&quot;)
       let netstat = system(&quot;netstat -anop tcp&quot;)
@@ -1191,7 +1314,7 @@ function! s:getpidfor(bind,port)
     return pid
 endfunction
 
-function! s:Server(bang,arg)
+function! s:app_server_command(bang,arg) dict
   let port = matchstr(a:arg,'\%(-p\|--port=\=\)\s*\zs\d\+')
   if port == ''
     let port = &quot;3000&quot;
@@ -1213,190 +1336,186 @@ function! s:Server(bang,arg)
       return
     endif
   endif
-  if has(&quot;win32&quot;) || has(&quot;win64&quot;) || (exists(&quot;$STY&quot;) &amp;&amp; !has(&quot;gui_running&quot;) &amp;&amp; s:getopt(&quot;gnu_screen&quot;,&quot;abg&quot;) &amp;&amp; executable(&quot;screen&quot;))
-    call s:rubyexebg(s:rquote(&quot;script/server&quot;).&quot; &quot;.a:arg)
+  if has_key(self,'options') &amp;&amp; has_key(self.options,'gnu_screen')
+    let screen = self.options.gnu_screen
+  else
+    let screen = g:rails_gnu_screen
+  endif
+  if has(&quot;win32&quot;) || has(&quot;win64&quot;) || (exists(&quot;$STY&quot;) &amp;&amp; !has(&quot;gui_running&quot;) &amp;&amp; screen &amp;&amp; executable(&quot;screen&quot;))
+    call self.background_ruby_command(s:rquote(&quot;script/server&quot;).&quot; &quot;.a:arg)
   else
     &quot;--daemon would be more descriptive but lighttpd does not support it
-    call s:rubyexe(s:rquote(&quot;script/server&quot;).&quot; &quot;.a:arg.&quot; -d&quot;)
+    call self.execute_ruby_command(s:rquote(&quot;script/server&quot;).&quot; &quot;.a:arg.&quot; -d&quot;)
   endif
   call s:setopt('a:root_url','http://'.(bind=='0.0.0.0'?'localhost': bind).':'.port.'/')
 endfunction
 
-function! s:Destroy(bang,...)
+function! s:app_destroy_command(bang,...) dict
   if a:0 == 0
-    call s:rubyexe(&quot;script/destroy&quot;)
-    return
+    return self.execute_ruby_command(&quot;script/destroy&quot;)
   elseif a:0 == 1
-    call s:rubyexe(&quot;script/destroy &quot;.s:rquote(a:1))
-    return
+    return self.execute_ruby_command(&quot;script/destroy &quot;.s:rquote(a:1))
   endif
   let str = &quot;&quot;
   let c = 1
   while c &lt;= a:0
-    let str = str . &quot; &quot; . s:rquote(a:{c})
-    let c = c + 1
+    let str .= &quot; &quot; . s:rquote(a:{c})
+    let c += 1
   endwhile
-  call s:rubyexe(s:rquote(&quot;script/destroy&quot;).str.(s:usesubversion()?' -c':''))
-  unlet! s:user_classes_{s:rv()}
+  call self.execute_ruby_command(s:rquote(&quot;script/destroy&quot;).str)
+  call self.cache.clear('user_classes')
 endfunction
 
-function! s:Generate(bang,...)
+function! s:app_generate_command(bang,...) dict
   if a:0 == 0
-    call s:rubyexe(&quot;script/generate&quot;)
-    return
+    return self.execute_ruby_command(&quot;script/generate&quot;)
   elseif a:0 == 1
-    call s:rubyexe(&quot;script/generate &quot;.s:rquote(a:1))
-    return
+    return self.execute_ruby_command(&quot;script/generate &quot;.s:rquote(a:1))
   endif
   let target = s:rquote(a:1)
   let str = &quot;&quot;
   let c = 2
   while c &lt;= a:0
-    let str = str . &quot; &quot; . s:rquote(a:{c})
-    let c = c + 1
+    let str .= &quot; &quot; . s:rquote(a:{c})
+    let c += 1
   endwhile
   if str !~ '-p\&gt;' &amp;&amp; str !~ '--pretend\&gt;'
-    let execstr = s:rubyexestr('-r./config/boot -e &quot;require '.&quot;'commands/generate'&quot;.'&quot; -- '.target.&quot; -p -f&quot;.str)
+    let execstr = self.ruby_shell_command('-r./config/boot -e &quot;require '.&quot;'commands/generate'&quot;.'&quot; -- '.target.&quot; -p -f&quot;.str)
     let res = system(execstr)
     let file = matchstr(res,'\s\+\%(create\|force\)\s\+\zs\f\+\.rb\ze\n')
     if file == &quot;&quot;
       let file = matchstr(res,'\s\+\%(exists\)\s\+\zs\f\+\.rb\ze\n')
     endif
-    &quot;echo file
   else
     let file = &quot;&quot;
   endif
-  if !s:rubyexe(&quot;script/generate &quot;.target.(s:usesubversion()?' -c':'').str) &amp;&amp; file != &quot;&quot;
-    unlet! s:user_classes_{s:rv()}
-    &quot;exe &quot;edit &quot;.s:ra().&quot;/&quot;.file
-    edit `=RailsRoot().'/'.file`
+  if !self.execute_ruby_command(&quot;script/generate &quot;.target.str) &amp;&amp; file != &quot;&quot;
+    call self.cache.clear('user_classes')
+    call self.cache.clear('test_suites')
+    if file =~ '^db/migrate/\d\d\d\d'
+      let file = get(self.relglob('',s:sub(file,'\d+','[0-9]*[0-9]')),-1,file)
+    endif
+    edit `=self.path(file)`
   endif
 endfunction
 
-function! s:ScriptComplete(ArgLead,CmdLine,P)
+call s:add_methods('app', ['generators','script_command','runner_command','server_command','destroy_command','generate_command'])
+
+function! s:Complete_script(ArgLead,CmdLine,P)
   let cmd = s:sub(a:CmdLine,'^\u\w*\s+','')
-  let P = a:P - strlen(a:CmdLine)+strlen(cmd)
-  if cmd !~ '^[ A-Za-z0-9_=-]*$'
-    &quot; You're on your own, bud
-    return &quot;&quot;
-  elseif cmd =~ '^\w*$'
-    return &quot;about\nconsole\ndestroy\ngenerate\nperformance/benchmarker\nperformance/profiler\nplugin\nproccess/reaper\nprocess/spawner\nrunner\nserver&quot;
-  elseif cmd =~ '^\%(plugin\)\s\+'.a:ArgLead.'$'
-    return &quot;discover\nlist\ninstall\nupdate\nremove\nsource\nunsource\nsources&quot;
-  elseif cmd =~ '\%(plugin\)\s\+\%(install\|remove\)\s\+'.a:ArgLead.'$' || cmd =~ '\%(generate\|destroy\)\s\+plugin\s\+'.a:ArgLead.'$'
+  &quot;let P = a:P - strlen(a:CmdLine)+strlen(cmd)
+  if cmd !~ '^[ A-Za-z0-9_=:-]*$'
+    return []
+  elseif cmd =~# '^\w*$'
+    return s:completion_filter(rails#app().relglob(&quot;script/&quot;,&quot;**/*&quot;),a:ArgLead)
+  elseif cmd =~# '^\%(plugin\)\s\+'.a:ArgLead.'$'
+    return s:completion_filter([&quot;discover&quot;,&quot;list&quot;,&quot;install&quot;,&quot;update&quot;,&quot;remove&quot;,&quot;source&quot;,&quot;unsource&quot;,&quot;sources&quot;],a:ArgLead)
+  elseif cmd =~# '\%(plugin\)\s\+\%(install\|remove\)\s\+'.a:ArgLead.'$' || cmd =~ '\%(generate\|destroy\)\s\+plugin\s\+'.a:ArgLead.'$'
     return s:pluginList(a:ArgLead,a:CmdLine,a:P)
-  elseif cmd =~ '^\%(generate\|destroy\)\s\+'.a:ArgLead.'$'
-    return g:rails_generators
-  elseif cmd =~ '^\%(generate\|destroy\)\s\+\w\+\s\+'.a:ArgLead.'$'
+  elseif cmd =~# '^\%(generate\|destroy\)\s\+'.a:ArgLead.'$'
+    return s:completion_filter(rails#app().generators(),a:ArgLead)
+  elseif cmd =~# '^\%(generate\|destroy\)\s\+\w\+\s\+'.a:ArgLead.'$'
     let target = matchstr(cmd,'^\w\+\s\+\zs\w\+\ze\s\+')
-    let pattern = &quot;&quot; &quot; TODO
     if target =~# '^\%(\w*_\)\=controller$'
-      return s:sub(s:controllerList(pattern,&quot;&quot;,&quot;&quot;),'^application\n=','')
-    elseif target =~# '^\%(\w*_\)\=model$' || target =~# '^scaffold\%(_resource\)\=$' || target == 'mailer'
-      return s:modelList(pattern,&quot;&quot;,&quot;&quot;)
-    elseif target == 'migration' || target == 'session_migration'
-      return s:migrationList(pattern,&quot;&quot;,&quot;&quot;)
-    elseif target == 'integration_test'
-      return s:integrationtestList(pattern,&quot;&quot;,&quot;&quot;)
-    elseif target == 'observer'
-      &quot; script/generate observer is in Edge Rails
-      let observers = s:observerList(pattern,&quot;&quot;,&quot;&quot;)
-      let models = s:modelList(pattern,&quot;&quot;,&quot;&quot;)
-      if cmd =~ '^destroy\&gt;'
-        let models = &quot;&quot;
+      return filter(s:controllerList(a:ArgLead,&quot;&quot;,&quot;&quot;),'v:val !=# &quot;application&quot;')
+    elseif target =~# '^\%(\w*_\)\=model$' || target =~# '^scaffold\%(_resource\)\=$' || target ==# 'mailer'
+      return s:modelList(a:ArgLead,&quot;&quot;,&quot;&quot;)
+    elseif target ==# 'migration' || target ==# 'session_migration'
+      return s:migrationList(a:ArgLead,&quot;&quot;,&quot;&quot;)
+    elseif target ==# 'integration_test'
+      return s:integrationtestList(a:ArgLead,&quot;&quot;,&quot;&quot;)
+    elseif target ==# 'observer'
+      let observers = s:observerList(&quot;&quot;,&quot;&quot;,&quot;&quot;)
+      let models = s:modelList(&quot;&quot;,&quot;&quot;,&quot;&quot;)
+      if cmd =~# '^destroy\&gt;'
+        let models = []
       endif
-      while strlen(models) &gt; 0
-        let tmp = matchstr(models.&quot;\n&quot;,'.\{-\}\ze\n')
-        let models = s:sub(models,'.{-}%(\n|$)','')
-        if stridx(&quot;\n&quot;.observers.&quot;\n&quot;,&quot;\n&quot;.tmp.&quot;\n&quot;) == -1 &amp;&amp; tmp !~'_observer$'
-          let observers = observers.&quot;\n&quot;.tmp
-        endif
-      endwhile
-      return s:sub(observers,'^\n','')
-    elseif target == 'web_service'
-      return s:apiList(pattern,&quot;&quot;,&quot;&quot;)
+      call filter(models,'index(observers,v:val) &lt; 0')
+      return s:completion_filter(observers + models,a:ArgLead)
+    elseif target ==# 'web_service'
+      return s:apiList(a:ArgLead,&quot;&quot;,&quot;&quot;)
+    else
+      return []
+    endif
+  elseif cmd =~# '^\%(generate\|destroy\)\s\+scaffold\s\+\w\+\s\+'.a:ArgLead.'$'
+    return filter(s:controllerList(a:ArgLead,&quot;&quot;,&quot;&quot;),'v:val !=# &quot;application&quot;')
+    return s:completion_filter(rails#app().environments())
+  elseif cmd =~# '^\%(console\)\s\+\(--\=\w\+\s\+\)\='.a:ArgLead.&quot;$&quot;
+    return s:completion_filter(rails#app().environments()+[&quot;-s&quot;,&quot;--sandbox&quot;],a:ArgLead)
+  elseif cmd =~# '^\%(server\)\s\+.*-e\s\+'.a:ArgLead.&quot;$&quot;
+    return s:completion_filter(rails#app().environments(),a:ArgLead)
+  elseif cmd =~# '^\%(server\)\s\+'
+    if a:ArgLead =~# '^--environment='
+      return s:completion_filter(map(copy(rails#app().environments()),'&quot;--environment=&quot;.v:val'),a:ArgLead)
     else
-      return &quot;&quot;
+      return filter([&quot;-p&quot;,&quot;-b&quot;,&quot;-e&quot;,&quot;-m&quot;,&quot;-d&quot;,&quot;-u&quot;,&quot;-c&quot;,&quot;-h&quot;,&quot;--port=&quot;,&quot;--binding=&quot;,&quot;--environment=&quot;,&quot;--mime-types=&quot;,&quot;--daemon&quot;,&quot;--debugger&quot;,&quot;--charset=&quot;,&quot;--help&quot;],'s:startswith(v:val,a:ArgLead)')
     endif
-  elseif cmd =~ '^\%(generate\|destroy\)\s\+scaffold\s\+\w\+\s\+'.a:ArgLead.'$'
-    return s:sub(s:controllerList(&quot;&quot;,&quot;&quot;,&quot;&quot;),'^application\n=','')
-  elseif cmd =~ '^\%(console\)\s\+\(--\=\w\+\s\+\)\='.a:ArgLead.&quot;$&quot;
-    return s:environments().&quot;\n-s\n--sandbox&quot;
-  elseif cmd =~ '^\%(server\)\s\+.*-e\s\+'.a:ArgLead.&quot;$&quot;
-    return s:environments()
-  elseif cmd =~ '^\%(server\)\s\+'
-    return &quot;-p\n-b\n-e\n-m\n-d\n-u\n-c\n-h\n--port=\n--binding=\n--environment=\n--mime-types=\n--daemon\n--debugger\n--charset=\n--help\n&quot;
   endif
   return &quot;&quot;
-&quot;  return s:relglob(RailsRoot().&quot;/script/&quot;,a:ArgLead.&quot;*&quot;)
 endfunction
 
 function! s:CustomComplete(A,L,P,cmd)
   let L = &quot;Rscript &quot;.a:cmd.&quot; &quot;.s:sub(a:L,'^\h\w*\s+','')
   let P = a:P - strlen(a:L) + strlen(L)
-  return s:ScriptComplete(a:A,L,P)
+  return s:Complete_script(a:A,L,P)
 endfunction
 
-function! s:ServerComplete(A,L,P)
+function! s:Complete_server(A,L,P)
   return s:CustomComplete(a:A,a:L,a:P,&quot;server&quot;)
 endfunction
 
-function! s:ConsoleComplete(A,L,P)
+function! s:Complete_console(A,L,P)
   return s:CustomComplete(a:A,a:L,a:P,&quot;console&quot;)
 endfunction
 
-function! s:GenerateComplete(A,L,P)
+function! s:Complete_generate(A,L,P)
   return s:CustomComplete(a:A,a:L,a:P,&quot;generate&quot;)
 endfunction
 
-function! s:DestroyComplete(A,L,P)
+function! s:Complete_destroy(A,L,P)
   return s:CustomComplete(a:A,a:L,a:P,&quot;destroy&quot;)
 endfunction
 
-function! s:RubyComplete(A,L,R)
-  return s:gsub(RailsUserClasses(),' ','\n').&quot;\nActiveRecord::Base&quot;
+function! s:Complete_ruby(A,L,P)
+  return s:completion_filter(rails#app().user_classes()+[&quot;ActiveRecord::Base&quot;],a:A)
 endfunction
 
 &quot; }}}1
 &quot; Navigation {{{1
 
 function! s:BufNavCommands()
-  &quot; TODO: completion
-  &quot;silent exe &quot;command! -bar -buffer -nargs=? Rcd :cd &quot;.s:ra().&quot;/&lt;args&gt;&quot;
-  &quot;silent exe &quot;command! -bar -buffer -nargs=? Rlcd :lcd &quot;.s:ra().&quot;/&lt;args&gt;&quot;
-  command!   -buffer -bar -nargs=? Rcd   :cd `=RailsRoot().'/'.&lt;q-args&gt;`
-  command!   -buffer -bar -nargs=? Rlcd :lcd `=RailsRoot().'/'.&lt;q-args&gt;`
-  &quot; Vim 6.2 chokes on script local completion functions (e.g., s:FindList).
-  &quot; :Rcommand! is a thin wrapper arround :command! which works around this
-  Rcommand!   -buffer -bar -nargs=* -count=1 -complete=custom,s:FindList Rfind       :call s:Find(&lt;bang&gt;0,&lt;count&gt;,'' ,&lt;f-args&gt;)
-  Rcommand!   -buffer -bar -nargs=* -count=1 -complete=custom,s:FindList REfind      :call s:Find(&lt;bang&gt;0,&lt;count&gt;,'E',&lt;f-args&gt;)
-  Rcommand!   -buffer -bar -nargs=* -count=1 -complete=custom,s:FindList RSfind      :call s:Find(&lt;bang&gt;0,&lt;count&gt;,'S',&lt;f-args&gt;)
-  Rcommand!   -buffer -bar -nargs=* -count=1 -complete=custom,s:FindList RVfind      :call s:Find(&lt;bang&gt;0,&lt;count&gt;,'V',&lt;f-args&gt;)
-  Rcommand!   -buffer -bar -nargs=* -count=1 -complete=custom,s:FindList RTfind      :call s:Find(&lt;bang&gt;0,&lt;count&gt;,'T',&lt;f-args&gt;)
-  Rcommand!   -buffer -bar -nargs=* -count=1 -complete=custom,s:FindList Rsfind      :&lt;count&gt;RSfind&lt;bang&gt; &lt;args&gt;
-  Rcommand!   -buffer -bar -nargs=* -count=1 -complete=custom,s:FindList Rtabfind    :&lt;count&gt;RTfind&lt;bang&gt; &lt;args&gt;
-  Rcommand!   -buffer -bar -nargs=* -bang    -complete=custom,s:EditList Redit       :call s:Edit(&lt;bang&gt;0,&lt;count&gt;,'' ,&lt;f-args&gt;)
-  Rcommand!   -buffer -bar -nargs=* -bang    -complete=custom,s:EditList REedit      :call s:Edit(&lt;bang&gt;0,&lt;count&gt;,'E',&lt;f-args&gt;)
-  Rcommand!   -buffer -bar -nargs=* -bang    -complete=custom,s:EditList RSedit      :call s:Edit(&lt;bang&gt;0,&lt;count&gt;,'S',&lt;f-args&gt;)
-  Rcommand!   -buffer -bar -nargs=* -bang    -complete=custom,s:EditList RVedit      :call s:Edit(&lt;bang&gt;0,&lt;count&gt;,'V',&lt;f-args&gt;)
-  Rcommand!   -buffer -bar -nargs=* -bang    -complete=custom,s:EditList RTedit      :call s:Edit(&lt;bang&gt;0,&lt;count&gt;,'T',&lt;f-args&gt;)
-  command! -buffer -bar -nargs=0 A  :call s:Alternate(&lt;bang&gt;0,&quot;&quot;)
-  command! -buffer -bar -nargs=0 AE :call s:Alternate(&lt;bang&gt;0,&quot;E&quot;)
-  command! -buffer -bar -nargs=0 AS :call s:Alternate(&lt;bang&gt;0,&quot;S&quot;)
-  command! -buffer -bar -nargs=0 AV :call s:Alternate(&lt;bang&gt;0,&quot;V&quot;)
-  command! -buffer -bar -nargs=0 AT :call s:Alternate(&lt;bang&gt;0,&quot;T&quot;)
-  command! -buffer -bar -nargs=0 AN :call s:Related(&lt;bang&gt;0,&quot;&quot;)
-  command! -buffer -bar -nargs=0 R  :call s:Related(&lt;bang&gt;0,&quot;&quot;)
-  command! -buffer -bar -nargs=0 RE :call s:Related(&lt;bang&gt;0,&quot;E&quot;)
-  command! -buffer -bar -nargs=0 RS :call s:Related(&lt;bang&gt;0,&quot;S&quot;)
-  command! -buffer -bar -nargs=0 RV :call s:Related(&lt;bang&gt;0,&quot;V&quot;)
-  command! -buffer -bar -nargs=0 RT :call s:Related(&lt;bang&gt;0,&quot;T&quot;)
-  &quot;command! -buffer -bar -nargs=0 RN :call s:Alternate(&lt;bang&gt;0,&quot;&quot;)
+  command!   -buffer -bar -nargs=? -complete=customlist,s:Complete_cd Rcd   :cd `=rails#app().path(&lt;q-args&gt;)`
+  command!   -buffer -bar -nargs=? -complete=customlist,s:Complete_cd Rlcd :lcd `=rails#app().path(&lt;q-args&gt;)`
+  command!   -buffer -bar -nargs=* -count=1 -complete=customlist,s:Complete_find Rfind       :call s:Find(&lt;bang&gt;0,&lt;count&gt;,'' ,&lt;f-args&gt;)
+  command!   -buffer -bar -nargs=* -count=1 -complete=customlist,s:Complete_find REfind      :call s:Find(&lt;bang&gt;0,&lt;count&gt;,'E',&lt;f-args&gt;)
+  command!   -buffer -bar -nargs=* -count=1 -complete=customlist,s:Complete_find RSfind      :call s:Find(&lt;bang&gt;0,&lt;count&gt;,'S',&lt;f-args&gt;)
+  command!   -buffer -bar -nargs=* -count=1 -complete=customlist,s:Complete_find RVfind      :call s:Find(&lt;bang&gt;0,&lt;count&gt;,'V',&lt;f-args&gt;)
+  command!   -buffer -bar -nargs=* -count=1 -complete=customlist,s:Complete_find RTfind      :call s:Find(&lt;bang&gt;0,&lt;count&gt;,'T',&lt;f-args&gt;)
+  command!   -buffer -bar -nargs=* -count=1 -complete=customlist,s:Complete_find Rsfind      :&lt;count&gt;RSfind&lt;bang&gt; &lt;args&gt;
+  command!   -buffer -bar -nargs=* -count=1 -complete=customlist,s:Complete_find Rtabfind    :&lt;count&gt;RTfind&lt;bang&gt; &lt;args&gt;
+  command!   -buffer -bar -nargs=* -bang    -complete=customlist,s:Complete_edit Redit       :call s:Edit(&lt;bang&gt;0,&lt;count&gt;,'' ,&lt;f-args&gt;)
+  command!   -buffer -bar -nargs=* -bang    -complete=customlist,s:Complete_edit REedit      :call s:Edit(&lt;bang&gt;0,&lt;count&gt;,'E',&lt;f-args&gt;)
+  command!   -buffer -bar -nargs=* -bang    -complete=customlist,s:Complete_edit RSedit      :call s:Edit(&lt;bang&gt;0,&lt;count&gt;,'S',&lt;f-args&gt;)
+  command!   -buffer -bar -nargs=* -bang    -complete=customlist,s:Complete_edit RVedit      :call s:Edit(&lt;bang&gt;0,&lt;count&gt;,'V',&lt;f-args&gt;)
+  command!   -buffer -bar -nargs=* -bang    -complete=customlist,s:Complete_edit RTedit      :call s:Edit(&lt;bang&gt;0,&lt;count&gt;,'T',&lt;f-args&gt;)
+  command! -buffer -bar -nargs=* -complete=customlist,s:Complete_find    A  :call s:Alternate(&lt;bang&gt;0,&quot;&quot;, &lt;f-args&gt;)
+  command! -buffer -bar -nargs=* -complete=customlist,s:Complete_find    AE :call s:Alternate(&lt;bang&gt;0,&quot;E&quot;,&lt;f-args&gt;)
+  command! -buffer -bar -nargs=* -complete=customlist,s:Complete_find    AS :call s:Alternate(&lt;bang&gt;0,&quot;S&quot;,&lt;f-args&gt;)
+  command! -buffer -bar -nargs=* -complete=customlist,s:Complete_find    AV :call s:Alternate(&lt;bang&gt;0,&quot;V&quot;,&lt;f-args&gt;)
+  command! -buffer -bar -nargs=* -complete=customlist,s:Complete_find    AT :call s:Alternate(&lt;bang&gt;0,&quot;T&quot;,&lt;f-args&gt;)
+  command! -buffer -bar -nargs=* -complete=customlist,s:Complete_edit    AN :call s:Related(&lt;bang&gt;0,&quot;&quot; ,&lt;f-args&gt;)
+  command! -buffer -bar -nargs=* -complete=customlist,s:Complete_edit    R  :call s:Related(&lt;bang&gt;0,&quot;&quot; ,&lt;f-args&gt;)
+  command! -buffer -bar -nargs=* -complete=customlist,s:Complete_edit    RE :call s:Related(&lt;bang&gt;0,&quot;E&quot;,&lt;f-args&gt;)
+  command! -buffer -bar -nargs=* -complete=customlist,s:Complete_edit    RS :call s:Related(&lt;bang&gt;0,&quot;S&quot;,&lt;f-args&gt;)
+  command! -buffer -bar -nargs=* -complete=customlist,s:Complete_edit    RV :call s:Related(&lt;bang&gt;0,&quot;V&quot;,&lt;f-args&gt;)
+  command! -buffer -bar -nargs=* -complete=customlist,s:Complete_edit    RT :call s:Related(&lt;bang&gt;0,&quot;T&quot;,&lt;f-args&gt;)
 endfunction
 
 function! s:djump(def)
-  let def = s:sub(a:def,'^[@#]','')
-  if def != ''
+  let def = s:sub(a:def,'^[#:]','')
+  if def =~ '^\d\+$'
+    exe def
+  elseif def != ''
     let ext = matchstr(def,'\.\zs.*')
     let def = matchstr(def,'[^.]*')
     let v:errmsg = ''
@@ -1406,7 +1525,6 @@ function! s:djump(def)
       let end = s:endof(line('.'))
       let rline = search(rpat,'',end)
       if rline &gt; 0
-        &quot;call cursor(rline,1)
         let variable = matchstr(getline(rline),rpat)
         let success = search('\C^\s*'.variable.'\s*\.\s*\zs'.ext.'\&gt;','',end)
         if !success
@@ -1423,13 +1541,13 @@ function! s:Find(bang,count,arg,...)
   if a:0
     let i = 1
     while i &lt; a:0
-      let str = str . s:escarg(a:{i}) . &quot; &quot;
-      let i = i + 1
+      let str .= s:escarg(a:{i}) . &quot; &quot;
+      let i += 1
     endwhile
     let file = a:{i}
-    let tail = matchstr(file,'[@#].*$')
+    let tail = matchstr(file,'#.*$\|:\d*\%(:in\&gt;.*\)\=$')
     if tail != &quot;&quot;
-      let file = s:sub(file,'[@#].*$','')
+      let file = s:sub(file,'#.*$|:\d*%(:in&gt;.*)=$','')
     endif
     if file != &quot;&quot;
       let file = s:RailsIncludefind(file)
@@ -1438,17 +1556,7 @@ function! s:Find(bang,count,arg,...)
     let file = s:RailsFind()
     let tail = &quot;&quot;
   endif
-  if file =~ '^\%(app\|config\|db\|public\|spec\|test\|vendor\)/.*\.' || !a:0 || 1
-    call s:findedit((a:count==1?'' : a:count).cmd,file.tail,str)
-  else
-    &quot; Old way
-    let fcmd = (a:count==1?'' : a:count).s:findcmdfor(cmd)
-    let fcmd = s:sub(fcmd,'(\d+)vert ','vert \1')
-    if file != &quot;&quot;
-      exe fcmd.' '.str.s:escarg(file)
-    endif
-    call s:djump(tail)
-  endif
+  call s:findedit((a:count==1?'' : a:count).cmd,file.tail,str)
 endfunction
 
 function! s:Edit(bang,count,arg,...)
@@ -1457,9 +1565,8 @@ function! s:Edit(bang,count,arg,...)
     let str = &quot;&quot;
     let i = 1
     while i &lt; a:0
-      &quot;let str = str . s:escarg(a:{i}) . &quot; &quot;
-      let str = str . &quot;`=a:&quot;.i.&quot;` &quot;
-      let i = i + 1
+      let str .= &quot;`=a:&quot;.i.&quot;` &quot;
+      let i += 1
     endwhile
     let file = a:{i}
     call s:findedit(s:editcmdfor(cmd),file,str)
@@ -1468,16 +1575,33 @@ function! s:Edit(bang,count,arg,...)
   endif
 endfunction
 
-function! s:FindList(ArgLead, CmdLine, CursorPos)
-  if exists(&quot;*UserFileComplete&quot;) &quot; genutils.vim
-    return UserFileComplete(s:RailsIncludefind(a:ArgLead), a:CmdLine, a:CursorPos, 1, &amp;path)
-  else
-    return &quot;&quot;
-  endif
+function! s:fuzzyglob(arg)
+  return '*'.s:gsub(s:gsub(a:arg,'[^/]','[&amp;]*'),'/','/*')
 endfunction
 
-function! s:EditList(ArgLead, CmdLine, CursorPos)
-  return s:relglob(&quot;&quot;,a:ArgLead.&quot;*[^~]&quot;)
+function! s:Complete_find(ArgLead, CmdLine, CursorPos)
+  let paths = s:pathsplit(&amp;l:path)
+  let seen = {}
+  for path in paths
+    if s:startswith(path,rails#app().path()) &amp;&amp; path !~ '[][*]'
+      let path = path[strlen(rails#app().path()) + 1 : ]
+      for file in rails#app().relglob(path == '' ? '' : path.'/',s:fuzzyglob(rails#underscore(a:ArgLead)), a:ArgLead =~# '\u' ? '.rb' : '')
+        let seen[file] = 1
+      endfor
+    endif
+  endfor
+  let results = sort(map(keys(seen),'s:sub(v:val,&quot;[.]rb$&quot;,&quot;&quot;)'))
+  return s:autocamelize(results,a:ArgLead)
+endfunction
+
+function! s:Complete_edit(ArgLead, CmdLine, CursorPos)
+  return s:completion_filter(rails#app().relglob(&quot;&quot;,s:fuzzyglob(a:ArgLead)),a:ArgLead)
+endfunction
+
+function! s:Complete_cd(ArgLead, CmdLine, CursorPos)
+  let all = rails#app().relglob(&quot;&quot;,a:ArgLead.&quot;*&quot;)
+  call filter(all,'v:val =~ &quot;/$&quot;')
+  return filter(all,'s:startswith(v:val,a:ArgLead)')
 endfunction
 
 function! RailsIncludeexpr()
@@ -1541,15 +1665,17 @@ function! s:RailsFind()
   if res != &quot;&quot;|return res.(fnamemodify(res,':e') == '' ? '.rb' : '')|endif
   let res = s:findit('\v&lt;File.dirname\(__FILE__\)\s*\+\s*[:'.&quot;'&quot;.'&quot;](\f+)&gt;['.&quot;'&quot;.'&quot;]=',expand('%:h').'\1')
   if res != &quot;&quot;|return res|endif
+  let res = rails#underscore(s:findit('\v\s*&lt;%(include|extend)\(=\s*&lt;(\f+)&gt;','\1'))
+  if res != &quot;&quot;|return res.&quot;.rb&quot;|endif
   let res = s:findamethod('require','\1')
   if res != &quot;&quot;|return res.(fnamemodify(res,':e') == '' ? '.rb' : '')|endif
   let res = s:findamethod('belongs_to\|has_one\|composed_of\|validates_associated\|scaffold','app/models/\1.rb')
   if res != &quot;&quot;|return res|endif
-  let res = s:singularize(s:findamethod('has_many\|has_and_belongs_to_many','app/models/\1'))
+  let res = rails#singularize(s:findamethod('has_many\|has_and_belongs_to_many','app/models/\1'))
   if res != &quot;&quot;|return res.&quot;.rb&quot;|endif
-  let res = s:singularize(s:findamethod('create_table\|drop_table\|add_column\|rename_column\|remove_column\|add_index','app/models/\1'))
+  let res = rails#singularize(s:findamethod('create_table\|change_table\|drop_table\|add_column\|rename_column\|remove_column\|add_index','app/models/\1'))
   if res != &quot;&quot;|return res.&quot;.rb&quot;|endif
-  let res = s:singularize(s:findasymbol('through','app/models/\1'))
+  let res = rails#singularize(s:findasymbol('through','app/models/\1'))
   if res != &quot;&quot;|return res.&quot;.rb&quot;|endif
   let res = s:findamethod('fixtures','fixtures/\1')
   if res != &quot;&quot;
@@ -1557,9 +1683,11 @@ function! s:RailsFind()
   endif
   let res = s:findamethod('map\.resources','app/controllers/\1_controller.rb')
   if res != &quot;&quot;|return res|endif
-  let res = s:findamethod('layout','app/views/layouts/\1')
+  let res = s:findamethod('map\.resource','app/controllers/\1')
+  if res != &quot;&quot;|return rails#pluralize(res).&quot;_controller.rb&quot;|endif
+  let res = s:findamethod('layout','\=s:findlayout(submatch(1))')
   if res != &quot;&quot;|return res|endif
-  let res = s:findasymbol('layout','app/views/layouts/\1')
+  let res = s:findasymbol('layout','\=s:findlayout(submatch(1))')
   if res != &quot;&quot;|return res|endif
   let res = s:findamethod('helper','app/helpers/\1_helper.rb')
   if res != &quot;&quot;|return res|endif
@@ -1567,6 +1695,8 @@ function! s:RailsFind()
   if res != &quot;&quot;|return res|endif
   let res = s:findasymbol('action','\1')
   if res != &quot;&quot;|return res|endif
+  let res = s:findasymbol('template','app/views/\1')
+  if res != &quot;&quot;|return res|endif
   let res = s:sub(s:sub(s:findasymbol('partial','\1'),'^/',''),'\k+$','_&amp;')
   if res != &quot;&quot;|return res.&quot;\n&quot;.s:findview(res)|endif
   let res = s:sub(s:sub(s:findfromview('render\s*(\=\s*:partial\s\+=&gt;\s*','\1'),'^/',''),'\k+$','_&amp;')
@@ -1580,7 +1710,9 @@ function! s:RailsFind()
   let res = s:sub(s:findfromview('javascript_include_tag','public/javascripts/\1.js'),'/defaults&gt;','/application')
   if res != &quot;&quot;|return res|endif
   if RailsFileType() =~ '^controller\&gt;'
-    let res = s:findit('\s*\&lt;def\s\+\(\k\+\)\&gt;(\=',s:sub(s:sub(RailsFilePath(),'/controllers/','/views/'),'_controller\.rb$','').'/\1')
+    let contr = s:controller()
+    let view = s:findit('\s*\&lt;def\s\+\(\k\+\)\&gt;(\=','/\1')
+    let res = s:findview(contr.'/'.view)
     if res != &quot;&quot;|return res|endif
   endif
   let isf_keep = &amp;isfname
@@ -1592,57 +1724,40 @@ function! s:RailsFind()
   return res
 endfunction
 
-function! s:initnamedroutes()
-  if s:cacheneeds(&quot;named_routes&quot;)
+function! s:app_named_route_file(route) dict
+  call self.route_names()
+  if self.cache.has(&quot;named_routes&quot;) &amp;&amp; has_key(self.cache.get(&quot;named_routes&quot;),a:route)
+    return self.cache.get(&quot;named_routes&quot;)[a:route]
+  endif
+  return &quot;&quot;
+endfunction
+
+function! s:app_route_names() dict
+  if self.cache.needs(&quot;named_routes&quot;)
     let exec = &quot;ActionController::Routing::Routes.named_routes.each {|n,r| puts %{#{n} app/controllers/#{r.requirements[:controller]}_controller.rb##{r.requirements[:action]}}}&quot;
-    let string = s:railseval(exec)
+    let string = self.eval(exec)
     let routes = {}
-    let list = split(string,&quot;\n&quot;)
-    let i = 0
-    &quot; If we use for, Vim 6.2 dumbly treats endfor like endfunction
-    while i &lt; len(list)
-      let route = split(list[i],&quot; &quot;)
+    for line in split(string,&quot;\n&quot;)
+      let route = split(line,&quot; &quot;)
       let name = route[0]
       let routes[name] = route[1]
-      let i = i + 1
-    endwhile
-    call s:cacheset(&quot;named_routes&quot;,routes)
+    endfor
+    call self.cache.set(&quot;named_routes&quot;,routes)
   endif
-endfunction
 
-function! s:namedroutefile(route)
-  call s:initnamedroutes()
-  if s:cachehas(&quot;named_routes&quot;) &amp;&amp; has_key(s:cache(&quot;named_routes&quot;),a:route)
-    return s:cache(&quot;named_routes&quot;)[a:route]
-  endif
-  return &quot;&quot;
+  return keys(self.cache.get(&quot;named_routes&quot;))
 endfunction
 
+call s:add_methods('app', ['route_names','named_route_file'])
+
 function! RailsNamedRoutes()
-  call s:initnamedroutes()
-  if s:cachehas(&quot;named_routes&quot;)
-    return keys(s:cache(&quot;named_routes&quot;))
-  else
-    &quot; Dead code
-    if s:cacheneeds(&quot;route_names&quot;)
-      let lines = readfile(RailsRoot().&quot;/config/routes.rb&quot;)
-      let plurals = map(filter(copy(lines),'v:val =~# &quot;^  map\\.resources\\s\\+:\\w&quot;'),'matchstr(v:val,&quot;^  map\\.resources\\=\\s\\+:\\zs\\w\\+&quot;)')
-      let singulars = map(copy(plurals),'s:singularize(v:val)')
-      let extras = map(copy(singulars),'&quot;new_&quot;.v:val')+map(copy(singulars),'&quot;edit_&quot;.v:val')
-      let all = plurals + singulars + extras
-      let named = map(filter(copy(lines),'v:val =~# &quot;^  map\\.\\%(connect\\&gt;\\|resources\\=\\&gt;\\)\\@!\\w\\+&quot;'),'matchstr(v:val,&quot;^  map\\.\\zs\\w\\+&quot;)')
-      call s:cacheset(&quot;route_names&quot;,named+all+map(copy(all),'&quot;formatted_&quot;.v:val'))
-    endif
-    if s:cachehas(&quot;route_names&quot;)
-      return s:cache(&quot;route_names&quot;)
-    endif
-  endif
+  return rails#app().route_names()
 endfunction
 
 function! s:RailsIncludefind(str,...)
-  if a:str == &quot;ApplicationController&quot;
+  if a:str ==# &quot;ApplicationController&quot;
     return &quot;app/controllers/application.rb&quot;
-  elseif a:str == &quot;Test::Unit::TestCase&quot;
+  elseif a:str ==# &quot;Test::Unit::TestCase&quot;
     return &quot;test/unit/testcase.rb&quot;
   elseif a:str == &quot;&lt;%=&quot;
     &quot; Probably a silly idea
@@ -1659,73 +1774,71 @@ function! s:RailsIncludefind(str,...)
   endif
   let str = s:sub(str,'^\s*','')
   let str = s:sub(str,'\s*$','')
-  let str = s:sub(str,'^[:@]','')
-  &quot;let str = s:sub(str,&quot;\\([\&quot;']\\)\\(.*\\)\\1&quot;,'\2')
+  let str = s:sub(str,'^:=[:@]','')
   let str = s:sub(str,':0x\x+$','') &quot; For #&lt;Object:0x...&gt; style output
   let str = s:gsub(str,&quot;[\&quot;']&quot;,'')
-  if line =~ '\&lt;\(require\|load\)\s*(\s*$'
+  if line =~# '\&lt;\(require\|load\)\s*(\s*$'
     return str
   endif
-  let str = s:underscore(str)
+  let str = rails#underscore(str)
   let fpat = '\(\s*\%(&quot;\f*&quot;\|:\f*\|'.&quot;'\\f*'&quot;.'\)\s*,\s*\)*'
-  if a:str =~ '\u'
+  if a:str =~# '\u'
     &quot; Classes should always be in .rb files
-    let str = str . '.rb'
-  elseif line =~ ':partial\s*=&gt;\s*'
+    let str .= '.rb'
+  elseif line =~# ':partial\s*=&gt;\s*'
     let str = s:sub(str,'([^/]+)$','_\1')
     let str = s:findview(str)
-  elseif line =~ '\&lt;layout\s*(\=\s*' || line =~ ':layout\s*=&gt;\s*'
+  elseif line =~# '\&lt;layout\s*(\=\s*' || line =~# ':layout\s*=&gt;\s*'
     let str = s:findview(s:sub(str,'^/=','layouts/'))
-  elseif line =~ ':controller\s*=&gt;\s*'
+  elseif line =~# ':controller\s*=&gt;\s*'
     let str = 'app/controllers/'.str.'_controller.rb'
-  elseif line =~ '\&lt;helper\s*(\=\s*'
+  elseif line =~# '\&lt;helper\s*(\=\s*'
     let str = 'app/helpers/'.str.'_helper.rb'
-  elseif line =~ '\&lt;fixtures\s*(\='.fpat
+  elseif line =~# '\&lt;fixtures\s*(\='.fpat
     if RailsFilePath() =~# '\&lt;spec/'
       let str = s:sub(str,'^/@!','spec/fixtures/')
     else
       let str = s:sub(str,'^/@!','test/fixtures/')
     endif
-  elseif line =~ '\&lt;stylesheet_\(link_tag\|path\)\s*(\='.fpat
+  elseif line =~# '\&lt;stylesheet_\(link_tag\|path\)\s*(\='.fpat
     let str = s:sub(str,'^/@!','/stylesheets/')
     let str = 'public'.s:sub(str,'^[^.]*$','&amp;.css')
-  elseif line =~ '\&lt;javascript_\(include_tag\|path\)\s*(\='.fpat
-    if str == &quot;defaults&quot;
+  elseif line =~# '\&lt;javascript_\(include_tag\|path\)\s*(\='.fpat
+    if str ==# &quot;defaults&quot;
       let str = &quot;application&quot;
     endif
     let str = s:sub(str,'^/@!','/javascripts/')
     let str = 'public'.s:sub(str,'^[^.]*$','&amp;.js')
-  elseif line =~ '\&lt;\(has_one\|belongs_to\)\s*(\=\s*'
+  elseif line =~# '\&lt;\(has_one\|belongs_to\)\s*(\=\s*'
     let str = 'app/models/'.str.'.rb'
-  elseif line =~ '\&lt;has_\(and_belongs_to_\)\=many\s*(\=\s*'
-    let str = 'app/models/'.s:singularize(str).'.rb'
-  elseif line =~ '\&lt;def\s\+' &amp;&amp; expand(&quot;%:t&quot;) =~ '_controller\.rb'
+  elseif line =~# '\&lt;has_\(and_belongs_to_\)\=many\s*(\=\s*'
+    let str = 'app/models/'.rails#singularize(str).'.rb'
+  elseif line =~# '\&lt;def\s\+' &amp;&amp; expand(&quot;%:t&quot;) =~# '_controller\.rb'
     let str = s:sub(s:sub(RailsFilePath(),'/controllers/','/views/'),'_controller\.rb$','/'.str)
-    &quot;let str = s:sub(expand(&quot;%:p&quot;),'.*[\/]app[\/]controllers[\/](.{-})_controller.rb','views/\1').'/'.str
     &quot; FIXME: support nested extensions
     let vt = s:view_types.&quot;,&quot;
     while vt != &quot;&quot;
       let t = matchstr(vt,'[^,]*')
       let vt = s:sub(vt,'[^,]*,','')
       if filereadable(str.&quot;.&quot;.t)
-        let str = str.&quot;.&quot;.t
+        let str .= '.'.t
         break
       endif
     endwhile
-  elseif str =~ '_\%(path\|url\)$'
+  elseif str =~# '_\%(path\|url\)$'
     &quot; REST helpers
     let str = s:sub(str,'_%(path|url)$','')
     let str = s:sub(str,'^hash_for_','')
-    let file = s:namedroutefile(str)
+    let file = rails#app().named_route_file(str)
     if file == &quot;&quot;
       let str = s:sub(str,'^formatted_','')
-      if str =~ '^\%(new\|edit\)_'
-        let str = 'app/controllers/'.s:sub(s:pluralize(str),'^(new|edit)_(.*)','\2_controller.rb#\1')
-      elseif str == s:singularize(str)
+      if str =~# '^\%(new\|edit\)_'
+        let str = 'app/controllers/'.s:sub(rails#pluralize(str),'^(new|edit)_(.*)','\2_controller.rb#\1')
+      elseif str ==# rails#singularize(str)
         &quot; If the word can't be singularized, it's probably a link to the show
         &quot; method.  We should verify by checking for an argument, but that's
         &quot; difficult the way things here are currently structured.
-        let str = 'app/controllers/'.s:pluralize(str).'_controller.rb#show'
+        let str = 'app/controllers/'.rails#pluralize(str).'_controller.rb#show'
       else
         let str = 'app/controllers/'.str.'_controller.rb#index'
       endif
@@ -1734,13 +1847,13 @@ function! s:RailsIncludefind(str,...)
     endif
   elseif str !~ '/'
     &quot; If we made it this far, we'll risk making it singular.
-    let str = s:singularize(str)
+    let str = rails#singularize(str)
     let str = s:sub(str,'_id$','')
   endif
   if str =~ '^/' &amp;&amp; !filereadable(str)
     let str = s:sub(str,'^/','')
   endif
-  if str =~ '^lib/' &amp;&amp; !filereadable(str)
+  if str =~# '^lib/' &amp;&amp; !filereadable(str)
     let str = s:sub(str,'^lib/','')
   endif
   return str
@@ -1754,7 +1867,7 @@ function! s:addfilecmds(type)
   let cmds = 'ESVT '
   let cmd = ''
   while cmds != ''
-    let cplt = &quot; -complete=custom,&quot;.s:sid.l.&quot;List&quot;
+    let cplt = &quot; -complete=customlist,&quot;.s:sid.l.&quot;List&quot;
     exe &quot;command! -buffer -bar -nargs=*&quot;.cplt.&quot; R&quot;.cmd.l.&quot; :call s:&quot;.l.'Edit(&lt;bang&gt;0,&quot;'.cmd.'&quot;,&lt;f-args&gt;)'
     let cmd = strpart(cmds,0,1)
     let cmds = strpart(cmds,1)
@@ -1762,7 +1875,8 @@ function! s:addfilecmds(type)
 endfunction
 
 function! s:BufFinderCommands()
-  command! -buffer -bar -bang -nargs=+ Rcommand :call s:Command(&lt;bang&gt;0,&lt;f-args&gt;)
+  command! -buffer -bar -nargs=+ Rnavcommand :call s:Navcommand(&lt;bang&gt;0,&lt;f-args&gt;)
+  command! -buffer -bar -nargs=+ Rcommand    :call s:warn(&quot;Warning: :Rcommand has been deprecated in favor of :Rnavcommand&quot;)|call s:Navcommand(&lt;bang&gt;0,&lt;f-args&gt;)
   call s:addfilecmds(&quot;model&quot;)
   call s:addfilecmds(&quot;view&quot;)
   call s:addfilecmds(&quot;controller&quot;)
@@ -1772,212 +1886,204 @@ function! s:BufFinderCommands()
   call s:addfilecmds(&quot;api&quot;)
   call s:addfilecmds(&quot;layout&quot;)
   call s:addfilecmds(&quot;fixtures&quot;)
-  call s:addfilecmds(&quot;unittest&quot;)
-  call s:addfilecmds(&quot;functionaltest&quot;)
-  call s:addfilecmds(&quot;integrationtest&quot;)
+  if rails#app().test_suites('test') || rails#app().test_suites('spec')
+    call s:addfilecmds(&quot;unittest&quot;)
+    call s:addfilecmds(&quot;functionaltest&quot;)
+  endif
+  if rails#app().test_suites('test')
+    call s:addfilecmds(&quot;integrationtest&quot;)
+  endif
+  if rails#app().test_suites('spec')
+    call s:addfilecmds(&quot;spec&quot;)
+  endif
   call s:addfilecmds(&quot;stylesheet&quot;)
   call s:addfilecmds(&quot;javascript&quot;)
+  call s:addfilecmds(&quot;plugin&quot;)
   call s:addfilecmds(&quot;task&quot;)
   call s:addfilecmds(&quot;lib&quot;)
-  call s:addfilecmds(&quot;plugin&quot;)
+  call s:addfilecmds(&quot;environment&quot;)
+  call s:addfilecmds(&quot;initializer&quot;)
+endfunction
+
+function! s:completion_filter(results,A)
+  let results = sort(type(a:results) == type(&quot;&quot;) ? split(a:results,&quot;\n&quot;) : copy(a:results))
+  call filter(results,'v:val !~# &quot;\\~$&quot;')
+  let filtered = filter(copy(results),'s:startswith(v:val,a:A)')
+  if !empty(filtered) | return filtered | endif
+  let regex = s:gsub(a:A,'[^/]','[&amp;].*')
+  let filtered = filter(copy(results),'v:val =~# &quot;^&quot;.regex')
+  if !empty(filtered) | return filtered | endif
+  let regex = s:gsub(a:A,'.','[&amp;].*')
+  let filtered = filter(copy(results),'v:val =~# regex')
+  return filtered
 endfunction
 
 function! s:autocamelize(files,test)
   if a:test =~# '^\u'
-    return s:camelize(a:files)
+    return s:completion_filter(map(copy(a:files),'rails#camelize(v:val)'),a:test)
   else
-    return a:files
+    return s:completion_filter(a:files,a:test)
   endif
 endfunction
 
-function! RailsUserClasses()
-  if !exists(&quot;b:rails_root&quot;)
-    return &quot;&quot;
-  elseif s:getopt('classes','ab') != ''
-    return s:getopt('classes','ab')
-  endif
-  let var = &quot;user_classes_&quot;.s:rv()
-  if !exists(&quot;s:&quot;.var)
-    let s:{var} = s:sub(s:sub(s:gsub(s:camelize(
-        \ s:relglob(&quot;app/models/&quot;,&quot;**/*&quot;,&quot;.rb&quot;) . &quot;\n&quot; .
-        \ s:sub(s:relglob(&quot;app/controllers/&quot;,&quot;**/*&quot;,&quot;.rb&quot;),'&lt;application&gt;','&amp;_controller') . &quot;\n&quot; .
-        \ s:relglob(&quot;app/helpers/&quot;,&quot;**/*&quot;,&quot;.rb&quot;) . &quot;\n&quot; .
-        \ s:relglob(&quot;lib/&quot;,&quot;**/*&quot;,&quot;.rb&quot;) . &quot;\n&quot; .
-        \ &quot;&quot;),'\n+',' '),'^\s+',''),'\s+$','')
-  endif
-  return s:{var}
-endfunction
-
-function! s:relglob(path,glob,...)
-  &quot; How could such a simple operation be so complicated?
+function! s:app_relglob(path,glob,...) dict
   if exists(&quot;+shellslash&quot;) &amp;&amp; ! &amp;shellslash
     let old_ss = &amp;shellslash
     let &amp;shellslash = 1
   endif
-  if a:path =~ '[\/]$'
-    let path = a:path
-  else
-    let path = a:path . ''
-  endif
-  if path !~ '^/' &amp;&amp; path !~ '^\w:' &amp;&amp; RailsRoot() != ''
-    let path = RailsRoot() . '/' . path
+  let path = a:path
+  if path !~ '^/' &amp;&amp; path !~ '^\w:'
+    let path = self.path(path)
   endif
   let suffix = a:0 ? a:1 : ''
-  let badres = glob(path.a:glob.suffix).&quot;\n&quot;
-  if v:version &lt;= 602
-    &quot; Nasty Vim bug in version 6.2
-    let badres = glob(path.a:glob.suffix).&quot;\n&quot;
-  endif
-  let goodres = &quot;&quot;
-  let striplen = strlen(path)
-  let stripend = strlen(suffix)
-  while strlen(badres) &gt; 0
-    let idx = stridx(badres,&quot;\n&quot;)
-    &quot;if idx == -1
-      &quot;let idx = strlen(badres)
-    &quot;endif
-    let tmp = strpart(badres,0,idx)
-    let badres = strpart(badres,idx+1)
-    let goodres = goodres.strpart(tmp,striplen,strlen(tmp)-striplen-stripend)
-    if suffix == '' &amp;&amp; isdirectory(tmp) &amp;&amp; goodres !~ '/$'
-      let goodres = goodres.&quot;/&quot;
+  let full_paths = split(glob(path.a:glob.suffix),&quot;\n&quot;)
+  let relative_paths = []
+  for entry in full_paths
+    if suffix == '' &amp;&amp; isdirectory(entry) &amp;&amp; entry !~ '/$'
+      let entry .= '/'
     endif
-    let goodres = goodres.&quot;\n&quot;
-  endwhile
-  &quot;let goodres = s:gsub(&quot;\n&quot;.goodres,'\n.{-}\~\n','\n')
+    let relative_paths += [entry[strlen(path) : -strlen(suffix)-1]]
+  endfor
   if exists(&quot;old_ss&quot;)
     let &amp;shellslash = old_ss
   endif
-  return s:compact(goodres)
+  return relative_paths
 endfunction
 
-if v:version &lt;= 602
-  &quot; Yet another  Vim 6.2 limitation
-  let s:recurse = &quot;*&quot;
-else
-  let s:recurse = &quot;**/*&quot;
-endif
+call s:add_methods('app', ['relglob'])
+
+function! s:relglob(...)
+  return join(call(rails#app().relglob,a:000,rails#app()),&quot;\n&quot;)
+endfunction
 
 function! s:helperList(A,L,P)
-  return s:autocamelize(s:relglob(&quot;app/helpers/&quot;,s:recurse,&quot;_helper.rb&quot;),a:A)
+  return s:autocamelize(rails#app().relglob(&quot;app/helpers/&quot;,&quot;**/*&quot;,&quot;_helper.rb&quot;),a:A)
 endfunction
 
 function! s:controllerList(A,L,P)
-  let con = s:gsub(s:relglob(&quot;app/controllers/&quot;,s:recurse,&quot;.rb&quot;),'_controller&gt;','')
+  let con = rails#app().relglob(&quot;app/controllers/&quot;,&quot;**/*&quot;,&quot;.rb&quot;)
+  call map(con,'s:sub(v:val,&quot;_controller$&quot;,&quot;&quot;)')
   return s:autocamelize(con,a:A)
 endfunction
 
 function! s:viewList(A,L,P)
   let c = s:controller(1)
-  let top = s:relglob(&quot;app/views/&quot;,a:A.&quot;*[^~]&quot;)
-  if c != ''
-    let local = s:relglob(&quot;app/views/&quot;.c.&quot;/&quot;,a:A.&quot;*.*[^~]&quot;)
-    if local != ''
-      return local.&quot;\n&quot;.top
-    endif
+  let top = rails#app().relglob(&quot;app/views/&quot;,s:fuzzyglob(a:A))
+  call filter(top,'v:val !~# &quot;\\~$&quot;')
+  if c != '' &amp;&amp; a:A !~ '/'
+    let local = rails#app().relglob(&quot;app/views/&quot;.c.&quot;/&quot;,&quot;*.*[^~]&quot;)
+    return s:completion_filter(local+top,a:A)
   endif
-  return top
+  return s:completion_filter(top,a:A)
 endfunction
 
 function! s:layoutList(A,L,P)
-  return s:relglob(&quot;app/views/layouts/&quot;,&quot;*&quot;)
+  return s:completion_filter(rails#app().relglob(&quot;app/views/layouts/&quot;,&quot;*&quot;),a:A)
 endfunction
 
 function! s:stylesheetList(A,L,P)
-  return s:relglob(&quot;public/stylesheets/&quot;,s:recurse,&quot;.css&quot;)
+  return s:completion_filter(rails#app().relglob(&quot;public/stylesheets/&quot;,&quot;**/*&quot;,&quot;.css&quot;),a:A)
 endfunction
 
 function! s:javascriptList(A,L,P)
-  return s:relglob(&quot;public/javascripts/&quot;,s:recurse,&quot;.js&quot;)
+  return s:completion_filter(rails#app().relglob(&quot;public/javascripts/&quot;,&quot;**/*&quot;,&quot;.js&quot;),a:A)
 endfunction
 
 function! s:modelList(A,L,P)
-  let models = s:relglob(&quot;app/models/&quot;,s:recurse,&quot;.rb&quot;).&quot;\n&quot;
-  &quot; . matches everything, and no good way to exclude newline.  Lame.
-  let models = s:gsub(models,'[ -~]*_observer\n',&quot;&quot;)
-  let models = s:compact(models)
+  let models = rails#app().relglob(&quot;app/models/&quot;,&quot;**/*&quot;,&quot;.rb&quot;)
+  call filter(models,'v:val !~# &quot;_observer$&quot;')
   return s:autocamelize(models,a:A)
 endfunction
 
 function! s:observerList(A,L,P)
-  return s:autocamelize(s:relglob(&quot;app/models/&quot;,s:recurse,&quot;_observer.rb&quot;),a:A)
+  return s:autocamelize(rails#app().relglob(&quot;app/models/&quot;,&quot;**/*&quot;,&quot;_observer.rb&quot;),a:A)
 endfunction
 
 function! s:fixturesList(A,L,P)
-  return s:compact(s:relglob(&quot;test/fixtures/&quot;,s:recurse).&quot;\n&quot;.s:relglob(&quot;spec/fixtures/&quot;,s:recurse))
+  return s:completion_filter(rails#app().relglob(&quot;test/fixtures/&quot;,&quot;**/*&quot;)+rails#app().relglob(&quot;spec/fixtures/&quot;,&quot;**/*&quot;),a:A)
 endfunction
 
 function! s:migrationList(A,L,P)
   if a:A =~ '^\d'
-    let migrations = s:relglob(&quot;db/migrate/&quot;,a:A.&quot;[0-9_]*&quot;,&quot;.rb&quot;)
-    let migrations = s:gsub(migrations,'_.{-}($|\n)','\1')
-    return migrations
+    let migrations = rails#app().relglob(&quot;db/migrate/&quot;,a:A.&quot;[0-9_]*&quot;,&quot;.rb&quot;)
+    return map(migrations,'matchstr(v:val,&quot;^[0-9]*&quot;)')
   else
-    let migrations = s:relglob(&quot;db/migrate/&quot;,&quot;[0-9]*[0-9]_&quot;.a:A.&quot;*&quot;,&quot;.rb&quot;)
-    let migrations = s:gsub(migrations,'(^|\n)\d+_','\1')
+    let migrations = rails#app().relglob(&quot;db/migrate/&quot;,&quot;[0-9]*[0-9]_*&quot;,&quot;.rb&quot;)
+    call map(migrations,'s:sub(v:val,&quot;^[0-9]*_&quot;,&quot;&quot;)')
     return s:autocamelize(migrations,a:A)
   endif
 endfunction
 
 function! s:apiList(A,L,P)
-  return s:autocamelize(s:relglob(&quot;app/apis/&quot;,s:recurse,&quot;_api.rb&quot;),a:A)
+  return s:autocamelize(rails#app().relglob(&quot;app/apis/&quot;,&quot;**/*&quot;,&quot;_api.rb&quot;),a:A)
 endfunction
 
 function! s:unittestList(A,L,P)
-  return s:autocamelize(s:relglob(&quot;test/unit/&quot;,s:recurse,&quot;_test.rb&quot;),a:A)
+  let found = []
+  if rails#app().test_suites('test')
+    let found += rails#app().relglob(&quot;test/unit/&quot;,&quot;**/*&quot;,&quot;_test.rb&quot;)
+  endif
+  if rails#app().test_suites('spec')
+    let found += rails#app().relglob(&quot;spec/models/&quot;,&quot;**/*&quot;,&quot;_spec.rb&quot;)
+  endif
+  return s:autocamelize(found,a:A)
 endfunction
 
 function! s:functionaltestList(A,L,P)
-  return s:autocamelize(s:relglob(&quot;test/functional/&quot;,s:recurse,&quot;_test.rb&quot;),a:A)
+  let found = []
+  if rails#app().test_suites('test')
+    let found += rails#app().relglob(&quot;test/functional/&quot;,&quot;**/*&quot;,&quot;_test.rb&quot;)
+  endif
+  if rails#app().test_suites('spec')
+    let found += rails#app().relglob(&quot;spec/controllers/&quot;,&quot;**/*&quot;,&quot;_spec.rb&quot;)
+  endif
+  return s:autocamelize(found,a:A)
 endfunction
 
 function! s:integrationtestList(A,L,P)
-  return s:autocamelize(s:relglob(&quot;test/integration/&quot;,s:recurse,&quot;_test.rb&quot;),a:A)
+  return s:autocamelize(rails#app().relglob(&quot;test/integration/&quot;,&quot;**/*&quot;,&quot;_test.rb&quot;),a:A)
+endfunction
+
+function! s:specList(A,L,P)
+  return s:completion_filter(rails#app().relglob(&quot;spec/&quot;,&quot;**/*&quot;,&quot;_spec.rb&quot;),a:A)
 endfunction
 
 function! s:pluginList(A,L,P)
   if a:A =~ '/'
-    return s:relglob('vendor/plugins/',matchstr(a:A,'.\{-\}/').'**/*')
+    return s:completion_filter(rails#app().relglob('vendor/plugins/',matchstr(a:A,'.\{-\}/').'**/*'),a:A)
   else
-    return s:relglob('vendor/plugins/',&quot;*&quot;,&quot;/init.rb&quot;)
+    return s:completion_filter(rails#app().relglob('vendor/plugins/',&quot;*&quot;,&quot;/init.rb&quot;),a:A)
   endif
 endfunction
 
 &quot; Task files, not actual rake tasks
 function! s:taskList(A,L,P)
-  let top = s:relglob(&quot;lib/tasks/&quot;,s:recurse,&quot;.rake&quot;)
+  let all = rails#app().relglob(&quot;lib/tasks/&quot;,&quot;**/*&quot;,&quot;.rake&quot;)
   if RailsFilePath() =~ '\&lt;vendor/plugins/.'
-    let path = s:sub(RailsFilePath(),'&lt;vendor/plugins/[^/]*/\zs.*','tasks/')
-    return s:relglob(path,s:recurse,&quot;.rake&quot;) . &quot;\n&quot; . top
-  else
-    return top
+    let path = s:sub(RailsFilePath(),'&lt;vendor/plugins/[^/]*/\zs.*','')
+    let all = rails#app().relglob(path.&quot;tasks/&quot;,&quot;**/*&quot;,&quot;.rake&quot;)+rails#app().relglob(path.&quot;lib/tasks/&quot;,&quot;**/*&quot;,&quot;.rake&quot;)+all
   endif
+  return s:autocamelize(all,a:A)
 endfunction
 
 function! s:libList(A,L,P)
-  let all = s:relglob('lib/',s:recurse,&quot;.rb&quot;)
+  let all = rails#app().relglob('lib/',&quot;**/*&quot;,&quot;.rb&quot;)
   if RailsFilePath() =~ '\&lt;vendor/plugins/.'
     let path = s:sub(RailsFilePath(),'&lt;vendor/plugins/[^/]*/\zs.*','lib/')
-    let all = s:relglob(path,s:recurse,&quot;.rb&quot;) . &quot;\n&quot; . all
+    let all = rails#app().relglob(path,&quot;**/*&quot;,&quot;.rb&quot;) + all
   endif
   return s:autocamelize(all,a:A)
 endfunction
 
-function! s:Command(bang,...)
-  if a:bang
-    let str = &quot;&quot;
-    let i = 0
-    while i &lt; a:0
-      let i = i + 1
-      if a:{i} =~# '^-complete=custom,s:' &amp;&amp; v:version &lt;= 602
-        let str = str . &quot; &quot; . s:sub(a:{i},',s:',','.s:sid)
-      else
-        let str = str . &quot; &quot; . a:{i}
-      endif
-    endwhile
-    exe &quot;command!&quot;.str
-    return
-  endif
+function! s:environmentList(A,L,P)
+  return s:completion_filter(rails#app().relglob(&quot;config/environments/&quot;,&quot;**/*&quot;,&quot;.rb&quot;),a:A)
+endfunction
+
+function! s:initializerList(A,L,P)
+  return s:completion_filter(rails#app().relglob(&quot;config/initializers/&quot;,&quot;**/*&quot;,&quot;.rb&quot;),a:A)
+endfunction
+
+function! s:Navcommand(bang,...)
   let suffix = &quot;.rb&quot;
   let filter = &quot;**/*&quot;
   let prefix = &quot;&quot;
@@ -1985,7 +2091,7 @@ function! s:Command(bang,...)
   let name = &quot;&quot;
   let i = 0
   while i &lt; a:0
-    let i = i + 1
+    let i += 1
     let arg = a:{i}
     if arg =~# '^-suffix='
       let suffix = matchstr(arg,'-suffix=\zs.*')
@@ -1998,7 +2104,7 @@ function! s:Command(bang,...)
       if name == &quot;&quot;
         let name = arg
       else
-        let prefix = prefix.&quot;\\n&quot;.s:sub(arg,'/=$','/')
+        let prefix .= &quot;\\n&quot;.s:sub(arg,'/=$','/')
       endif
     endif
   endwhile
@@ -2009,7 +2115,7 @@ function! s:Command(bang,...)
   let cmds = 'ESVT '
   let cmd = ''
   while cmds != ''
-    exe 'command! -buffer -bar -bang -nargs=* -complete=custom,'.s:sid.'CommandList R'.cmd.name.&quot; :call s:CommandEdit(&lt;bang&gt;0,'&quot;.cmd.&quot;','&quot;.name.&quot;',\&quot;&quot;.prefix.&quot;\&quot;,&quot;.s:string(suffix).&quot;,&quot;.s:string(filter).&quot;,&quot;.s:string(default).&quot;,&lt;f-args&gt;)&quot;
+    exe 'command! -buffer -bar -bang -nargs=* -complete=customlist,'.s:sid.'CommandList R'.cmd.name.&quot; :call s:CommandEdit(&lt;bang&gt;0,'&quot;.cmd.&quot;','&quot;.name.&quot;',\&quot;&quot;.prefix.&quot;\&quot;,&quot;.string(suffix).&quot;,&quot;.string(filter).&quot;,&quot;.string(default).&quot;,&lt;f-args&gt;)&quot;
     let cmd = strpart(cmds,0,1)
     let cmds = strpart(cmds,1)
   endwhile
@@ -2019,17 +2125,16 @@ function! s:CommandList(A,L,P)
   let cmd = matchstr(a:L,'\CR[A-Z]\=\w\+')
   exe cmd.&quot; &amp;&quot;
   let lp = s:last_prefix . &quot;\n&quot;
-  let res = &quot;&quot;
+  let res = []
   while lp != &quot;&quot;
     let p = matchstr(lp,'.\{-\}\ze\n')
     let lp = s:sub(lp,'.{-}\n','')
-    let res = res . s:relglob(p,s:last_filter,s:last_suffix).&quot;\n&quot;
+    let res += rails#app().relglob(p,s:last_filter,s:last_suffix)
   endwhile
-  let res = s:compact(res)
   if s:last_camelize
     return s:autocamelize(res,a:A)
   else
-    return res
+    return s:completion_filter(res,a:A)
   endif
 endfunction
 
@@ -2061,58 +2166,54 @@ function! s:EditSimpleRb(bang,cmd,name,target,prefix,suffix)
   let cmd = s:findcmdfor(a:cmd.(a:bang?'!':''))
   if a:target == &quot;&quot;
     &quot; Good idea to emulate error numbers like this?
-    return s:error(&quot;E471: Argument required&quot;) &quot; : R',a:name)
-  &quot;else
-    &quot;let g:target = a:target
+    return s:error(&quot;E471: Argument required&quot;)
   endif
-  let f = s:underscore(a:target)
-  let jump = matchstr(f,'[@#].*')
-  let f = s:sub(f,'[@#].*','')
+  let f = rails#underscore(a:target)
+  let jump = matchstr(f,'#.*\|:\d*\%(:in\)\=$')
+  let f = s:sub(f,'#.*|:\d*%(:in)=$','')
   if f == '.'
     let f = s:sub(f,'\.$','')
   else
-    let f = f.a:suffix.jump
-    if a:suffix !~ '\.'
-      &quot;let f = f.&quot;.rb&quot;
-    endif
+    let f .= a:suffix.jump
   endif
   let f = s:gsub(a:prefix,'\n',f.'\n').f
   return s:findedit(cmd,f)
 endfunction
 
-function! s:migrationfor(file)
-  let tryagain = 0
+function! s:app_migration(file) dict
   let arg = a:file
-  if arg =~ '^\d$'
+  if arg =~ '^0\+$'
+    if self.has_file('db/schema.rb')
+      return 'db/schema.rb'
+    elseif self.has_file('db/'.s:environment().'_structure.sql')
+      return 'db/'.s:environment().'_structure.sql'
+    else
+      return 'db/schema.rb'
+    endif
+  elseif arg =~ '^\d$'
     let glob = '00'.arg.'_*.rb'
   elseif arg =~ '^\d\d$'
     let glob = '0'.arg.'_*.rb'
   elseif arg =~ '^\d\d\d$'
     let glob = ''.arg.'_*.rb'
   elseif arg == ''
-    if s:model(1) != ''
-      let glob = '*_'.s:pluralize(s:model(1)).'.rb'
-      let tryagain = 1
-    else
-      let glob = '*.rb'
-    endif
+    let glob = '*.rb'
   else
-    let glob = '*'.arg.'*rb'
-  endif
-  let migr = s:sub(glob(RailsRoot().'/db/migrate/'.glob),'.*\n','')
-  if migr == '' &amp;&amp; tryagain
-    let migr = s:sub(glob(RailsRoot().'/db/migrate/*.rb'),'.*\n','')
+    let glob = '*'.rails#underscore(arg).'*rb'
   endif
-  if strpart(migr,0,strlen(RailsRoot())) == RailsRoot()
-    let migr = strpart(migr,1+strlen(RailsRoot()))
+  let migr = s:sub(glob(self.path('db/migrate/').glob),'.*\n','')
+  if s:startswith(migr,self.path())
+    let migr = strpart(migr,1+strlen(self.path()))
   endif
   return migr
 endfunction
 
+call s:add_methods('app', ['migration'])
+
 function! s:migrationEdit(bang,cmd,...)
   let cmd = s:findcmdfor(a:cmd.(a:bang?'!':''))
   let arg = a:0 ? a:1 : ''
-  let migr = arg == &quot;.&quot; ? &quot;db/migrate&quot; : s:migrationfor(arg)
+  let migr = arg == &quot;.&quot; ? &quot;db/migrate&quot; : rails#app().migration(arg)
   if migr != ''
     call s:findedit(cmd,migr)
   else
@@ -2122,9 +2223,9 @@ endfunction
 
 function! s:fixturesEdit(bang,cmd,...)
   if a:0
-    let c = s:underscore(a:1)
+    let c = rails#underscore(a:1)
   else
-    let c = s:pluralize(s:model(1))
+    let c = rails#pluralize(s:model(1))
   endif
   if c == &quot;&quot;
     return s:error(&quot;E471: Argument required&quot;)
@@ -2133,10 +2234,10 @@ function! s:fixturesEdit(bang,cmd,...)
   let e = e == '' ? e : '.'.e
   let c = fnamemodify(c,':r')
   let file = 'test/fixtures/'.c.e
-  if file =~ '\.\w\+$' &amp;&amp; !s:hasfile(&quot;spec/fixtures/&quot;.c.e)
+  if file =~ '\.\w\+$' &amp;&amp; !rails#app().has_file(&quot;spec/fixtures/&quot;.c.e)
     call s:edit(a:cmd.(a:bang?'!':''),file)
   else
-    call s:findedit(a:cmd.(a:bang?'!':''),file.&quot;\nspec/fixtures/&quot;.c.e)
+    call s:findedit(a:cmd.(a:bang?'!':''),rails#app().find_file(c.e,[&quot;test/fixtures&quot;,&quot;spec/fixtures&quot;],[&quot;.yml&quot;,&quot;.csv&quot;],file))
   endif
 endfunction
 
@@ -2176,8 +2277,8 @@ function! s:viewEdit(bang,cmd,...)
     call s:findedit(a:cmd.(a:bang?'!':''),file)
   else
     let format = s:format('html')
-    if glob(RailsRoot().'/'.file.'.'.format.'.*[^~]') != ''
-      let file = file . '.' . format
+    if glob(rails#app().path(file.'.'.format).'.*[^~]') != ''
+      let file .= '.' . format
     endif
     call s:findedit(a:cmd.(a:bang?'!':''),file)
   endif
@@ -2196,9 +2297,9 @@ function! s:findview(name)
   endif
   if c =~ '\.\w\+\.\w\+$' || c =~ '\.'.s:viewspattern().'$'
     return pre.c
-  elseif s:hasfile(pre.c.&quot;.rhtml&quot;)
+  elseif rails#app().has_file(pre.c.&quot;.rhtml&quot;)
     let file = pre.c.&quot;.rhtml&quot;
-  elseif s:hasfile(pre.c.&quot;.rxml&quot;)
+  elseif rails#app().has_file(pre.c.&quot;.rxml&quot;)
     let file = pre.c.&quot;.rxml&quot;
   else
     let format = &quot;.&quot; . s:format('html')
@@ -2207,7 +2308,7 @@ function! s:findview(name)
       while vt != &quot;&quot;
         let t = matchstr(vt,'[^,]*')
         let vt = s:sub(vt,'[^,]*,','')
-        if s:hasfile(pre.c.format.&quot;.&quot;.t)
+        if rails#app().has_file(pre.c.format.&quot;.&quot;.t)
           let file = pre.c.format.&quot;.&quot;.t
           break
         endif
@@ -2223,12 +2324,12 @@ function! s:findview(name)
 endfunction
 
 function! s:findlayout(name)
-  return s:findview(&quot;layouts/&quot;.a:name)
+  return s:findview(&quot;layouts/&quot;.(a:name == '' ? 'application' : a:name))
 endfunction
 
 function! s:layoutEdit(bang,cmd,...)
   if a:0
-    let c = s:underscore(a:1)
+    let c = rails#underscore(a:1)
   else
     let c = s:controller(1)
   endif
@@ -2240,7 +2341,7 @@ function! s:layoutEdit(bang,cmd,...)
     let file = s:findlayout(&quot;application&quot;)
   endif
   if file == &quot;&quot;
-    let file = &quot;app/views/layouts/application.rhtml&quot;
+    let file = &quot;app/views/layouts/application.html.erb&quot;
   endif
   call s:edit(a:cmd.(a:bang?'!':''),s:sub(file,'^/',''))
 endfunction
@@ -2250,12 +2351,12 @@ function! s:controllerEdit(bang,cmd,...)
   if a:0 == 0
     let controller = s:controller(1)
     if RailsFileType() =~ '^view\%(-layout\|-partial\)\@!'
-      let suffix = suffix.'#'.expand('%:t:r')
+      let suffix .= '#'.expand('%:t:r')
     endif
   else
     let controller = a:1
   endif
-  if s:hasfile(&quot;app/controllers/&quot;.controller.&quot;_controller.rb&quot;) || !s:hasfile(&quot;app/controllers/&quot;.controller.&quot;.rb&quot;)
+  if rails#app().has_file(&quot;app/controllers/&quot;.controller.&quot;_controller.rb&quot;) || !rails#app().has_file(&quot;app/controllers/&quot;.controller.&quot;.rb&quot;)
     let suffix = &quot;_controller&quot;.suffix
   endif
   return s:EditSimpleRb(a:bang,a:cmd,&quot;controller&quot;,controller,&quot;app/controllers/&quot;,suffix)
@@ -2278,40 +2379,62 @@ function! s:javascriptEdit(bang,cmd,...)
 endfunction
 
 function! s:unittestEdit(bang,cmd,...)
-  let f = a:0 ? a:1 : s:model(1)
-  if !a:0 &amp;&amp; RailsFileType() =~ '^model-aro\&gt;' &amp;&amp; f != '' &amp;&amp; f !~ '_observer$'
-    if s:hasfile(&quot;test/unit/&quot;.f.&quot;_observer.rb&quot;) || !s:hasfile(&quot;test/unit/&quot;.f.&quot;.rb&quot;)
-      let f = f . &quot;_observer&quot;
+  let cmd = s:findcmdfor(a:cmd.(a:bang?'!':''))
+  let f = rails#underscore(a:0 ? a:1 : s:model(1))
+  let mapping = {'test': ['test/unit/','_test.rb'], 'spec': ['spec/models/','_spec.rb']}
+  let tests = map(rails#app().test_suites(),'get(mapping,v:val,&quot;test&quot;)')
+  if empty(tests)
+    let tests = [mapping[tests]]
+  endif
+  for [prefix, suffix] in tests
+    if !a:0 &amp;&amp; RailsFileType() =~# '^model-aro\&gt;' &amp;&amp; f != '' &amp;&amp; f !~# '_observer$'
+      if rails#app().has_file(prefix.f.'_observer'.suffix)
+        return s:findedit(cmd,prefix.f.'_observer'.suffix)
+      endif
     endif
-  endif
-  return s:EditSimpleRb(a:bang,a:cmd,&quot;unittest&quot;,f,&quot;test/unit/&quot;,&quot;_test.rb&quot;)
+  endfor
+  for [prefix, suffix] in tests
+    if rails#app().has_file(prefix.f.suffix)
+      return s:findedit(cmd,prefix.f.suffix)
+    endif
+  endfor
+  return s:findedit(cmd,tests[0][0].f.tests[0][1])
 endfunction
 
 function! s:functionaltestEdit(bang,cmd,...)
+  let cmd = s:findcmdfor(a:cmd.(a:bang?'!':''))
+  let f = rails#underscore(a:0 ? a:1 : s:controller(1))
+  let mapping = {'test': ['test/functional/','_test.rb'], 'spec': ['spec/controllers/','_spec.rb']}
+  let tests = map(rails#app().test_suites(),'get(mapping,v:val,&quot;test&quot;)')
+  if empty(tests)
+    let tests = [mapping[tests]]
+  endif
+  for [prefix, suffix] in tests
+    if rails#app().has_file(prefix.f.suffix)
+      return s:findedit(cmd,prefix.f.suffix)
+    elseif rails#app().has_file(prefix.f.'_controller'.suffix)
+      return s:findedit(cmd,prefix.f.'_controller'.suffix)
+    elseif rails#app().has_file(prefix.f.'_api'.suffix)
+      return s:findedit(cmd,prefix.f.'_api'.suffix)
+    endif
+  endfor
+  return s:findedit(cmd,tests[0][0].f.tests[0][1])
+endfunction
+
+function! s:integrationtestEdit(bang,cmd,...)
   if a:0
-    let f = a:1
+    return s:EditSimpleRb(a:bang,a:cmd,&quot;integrationtest&quot;,a:1,&quot;test/integration/&quot;,&quot;_test.rb&quot;)
   else
-    let f = s:controller()
-  endif
-  if f != '' &amp;&amp; !s:hasfile(&quot;test/functional/&quot;.f.&quot;_test.rb&quot;)
-    if s:hasfile(&quot;test/functional/&quot;.f.&quot;_controller_test.rb&quot;)
-      let f = f . &quot;_controller&quot;
-    elseif s:hasfile(&quot;test/functional/&quot;.f.&quot;_api_test.rb&quot;)
-      let f = f . &quot;_api&quot;
-    endif
+    call s:EditSimpleRb(a:bang,a:cmd,&quot;integrationtest&quot;,&quot;test_helper&quot;,&quot;test/&quot;,&quot;.rb&quot;)
   endif
-  return s:EditSimpleRb(a:bang,a:cmd,&quot;functionaltest&quot;,f,&quot;test/functional/&quot;,&quot;_test.rb&quot;)
 endfunction
 
-function! s:integrationtestEdit(bang,cmd,...)
+function! s:specEdit(bang,cmd,...)
   if a:0
-    let f = a:1
-  elseif s:model() != ''
-    let f = s:model()
+    return s:EditSimpleRb(a:bang,a:cmd,&quot;spec&quot;,a:1,&quot;spec/&quot;,&quot;_spec.rb&quot;)
   else
-    let f = s:controller()
+    call s:EditSimpleRb(a:bang,a:cmd,&quot;spec&quot;,&quot;spec_helper&quot;,&quot;spec/&quot;,&quot;.rb&quot;)
   endif
-  return s:EditSimpleRb(a:bang,a:cmd,&quot;integrationtest&quot;,f,&quot;test/integration/&quot;,&quot;_test.rb&quot;)
 endfunction
 
 function! s:pluginEdit(bang,cmd,...)
@@ -2323,13 +2446,13 @@ function! s:pluginEdit(bang,cmd,...)
     let extra = &quot;vendor/plugins/&quot; . plugin . &quot;/\n&quot;
   endif
   if a:0
-    if a:1 =~ '^[^/.]*/\=$' &amp;&amp; s:hasfile(&quot;vendor/plugins/&quot;.a:1.&quot;/init.rb&quot;)
+    if a:1 =~ '^[^/.]*/\=$' &amp;&amp; rails#app().has_file(&quot;vendor/plugins/&quot;.a:1.&quot;/init.rb&quot;)
       return s:EditSimpleRb(a:bang,a:cmd,&quot;plugin&quot;,s:sub(a:1,'/$',''),&quot;vendor/plugins/&quot;,&quot;/init.rb&quot;)
     elseif plugin == &quot;&quot;
       call s:edit(cmd,&quot;vendor/plugins/&quot;.s:sub(a:1,'\.$',''))
     elseif a:1 == &quot;.&quot;
       call s:findedit(cmd,&quot;vendor/plugins/&quot;.plugin)
-    elseif isdirectory(RailsRoot().&quot;/vendor/plugins/&quot;.matchstr(a:1,'^[^/]*'))
+    elseif isdirectory(rails#app().path(&quot;vendor/plugins/&quot;.matchstr(a:1,'^[^/]*')))
       call s:edit(cmd,&quot;vendor/plugins/&quot;.a:1)
     else
       call s:findedit(cmd,&quot;vendor/plugins/&quot;.a:1.&quot;\nvendor/plugins/&quot;.plugin.&quot;/&quot;.a:1)
@@ -2344,7 +2467,7 @@ function! s:taskEdit(bang,cmd,...)
   let extra = &quot;&quot;
   if RailsFilePath() =~ '\&lt;vendor/plugins/.'
     let plugin = matchstr(RailsFilePath(),'\&lt;vendor/plugins/[^/]*')
-    let extra = plugin.&quot;/tasks/\n&quot;
+    let extra = plugin.&quot;/tasks/\n&quot;.plugin.&quot;/lib/tasks/\n&quot;
   endif
   if a:0
     call s:EditSimpleRb(a:bang,a:cmd,&quot;task&quot;,a:1,extra.&quot;lib/tasks/&quot;,&quot;.rake&quot;)
@@ -2359,13 +2482,20 @@ function! s:libEdit(bang,cmd,...)
     let extra = s:sub(RailsFilePath(),'&lt;vendor/plugins/[^/]*/\zs.*','lib/').&quot;\n&quot;
   endif
   if a:0
-    call s:EditSimpleRb(a:bang,a:cmd,&quot;task&quot;,a:0? a:1 : &quot;&quot;,extra.&quot;lib/&quot;,&quot;.rb&quot;)
+    call s:EditSimpleRb(a:bang,a:cmd,&quot;lib&quot;,a:0? a:1 : &quot;&quot;,extra.&quot;lib/&quot;,&quot;.rb&quot;)
   else
-    &quot; Easter egg
-    call s:EditSimpleRb(a:bang,a:cmd,&quot;task&quot;,&quot;environment&quot;,&quot;config/&quot;,&quot;.rb&quot;)
+    call s:EditSimpleRb(a:bang,a:cmd,&quot;lib&quot;,&quot;routes&quot;,&quot;config/&quot;,&quot;.rb&quot;)
   endif
 endfunction
 
+function! s:environmentEdit(bang,cmd,...)
+  return s:EditSimpleRb(a:bang,a:cmd,&quot;environment&quot;,a:0? a:1 : &quot;../environment&quot;,&quot;config/environments/&quot;,&quot;.rb&quot;)
+endfunction
+
+function! s:initializerEdit(bang,cmd,...)
+  return s:EditSimpleRb(a:bang,a:cmd,&quot;initializer&quot;,a:0? a:1 : &quot;../routes&quot;,&quot;config/initializers/&quot;,&quot;.rb&quot;)
+endfunction
+
 &quot; }}}1
 &quot; Alternate/Related {{{1
 
@@ -2418,74 +2548,62 @@ function! s:try(cmd) abort
   return 1
 endfunction
 
-function! s:findedit(cmd,file,...) abort
+function! s:findedit(cmd,files,...) abort
   let cmd = s:findcmdfor(a:cmd)
-  if a:file =~ '\n'
-    let filelist = a:file . &quot;\n&quot;
-    let file = ''
-    while file == '' &amp;&amp; filelist != ''
-      let maybe = matchstr(filelist,'^.\{-\}\ze\n')
-      let filelist = s:sub(filelist,'^.{-}\n','')
-      if s:hasfile(s:sub(maybe,'[@#].*',''))
-        let file = maybe
-      endif
-    endwhile
-    if file == ''
-      let file = matchstr(a:file.&quot;\n&quot;,'^.\{-\}\ze\n')
-    endif
+  let files = type(a:files) == type([]) ? copy(a:files) : split(a:files,&quot;\n&quot;)
+  if len(files) == 1
+    let file = files[0]
   else
-    let file = a:file
+    let file = get(filter(copy(files),'rails#app().has_file(s:sub(v:val,&quot;#.*|:\\d*$&quot;,&quot;&quot;))'),0,get(files,0,''))
   endif
-  if file =~ '[@#]'
-    let djump = matchstr(file,'[@#]\zs.*')
-    let file = matchstr(file,'.\{-\}\ze[@#]')
+  if file =~ '#\|:\d*\%(:in\)\=$'
+    let djump = matchstr(file,'#\zs.*\|:\zs\d*\ze\%(:in\)\=$')
+    let file = s:sub(file,'#.*|:\d*%(:in)=$','')
   else
     let djump = ''
   endif
   if file == ''
     let testcmd = &quot;edit&quot;
-  elseif RailsRoot() =~ '://' || cmd =~ 'edit' || cmd =~ 'split'
+  elseif isdirectory(rails#app().path(file))
+    let arg = file == &quot;.&quot; ? rails#app().path() : rails#app().path(file)
+    let testcmd = s:editcmdfor(cmd).' '.(a:0 ? a:1 . ' ' : '').s:escarg(arg)
+    exe testcmd
+    return
+  elseif rails#app().path() =~ '://' || cmd =~ 'edit' || cmd =~ 'split'
     if file !~ '^/' &amp;&amp; file !~ '^\w:' &amp;&amp; file !~ '://'
-      let file = s:ra().'/'.file
+      let file = s:escarg(rails#app().path(file))
     endif
     let testcmd = s:editcmdfor(cmd).' '.(a:0 ? a:1 . ' ' : '').file
-  elseif isdirectory(RailsRoot().'/'.file)
-    let testcmd = s:editcmdfor(cmd).' '.(a:0 ? a:1 . ' ' : '').s:ra().'/'.file
-    exe testcmd
-    return
   else
     let testcmd = cmd.' '.(a:0 ? a:1 . ' ' : '').file
   endif
   if s:try(testcmd)
-    &quot; Shorten the file name (I don't fully understand how Vim decides when to
-    &quot; use a relative/absolute path for the file name, so lets blindly force it
-    &quot; to be as short as possible)
-    &quot;silent! file %:~:.
-    &quot;silent! lcd .
     call s:djump(djump)
   endif
 endfunction
 
 function! s:edit(cmd,file,...)
   let cmd = s:editcmdfor(a:cmd)
-  let cmd = cmd.' '.(a:0 ? a:1 . ' ' : '')
+  let cmd .= ' '.(a:0 ? a:1 . ' ' : '')
   let file = a:file
   if file !~ '^/' &amp;&amp; file !~ '^\w:' &amp;&amp; file !~ '://'
-    &quot;let file = s:ra().'/'.file
-    exe cmd.&quot;`=RailsRoot().'/'.file`&quot;
+    exe cmd.&quot;`=fnamemodify(rails#app().path(file),':.')`&quot;
   else
     exe cmd.file
   endif
-  &quot;exe cmd.file
 endfunction
 
-function! s:Alternate(bang,cmd)
-  let cmd = a:cmd.(a:bang?&quot;!&quot;:&quot;&quot;)
-  let file = s:AlternateFile()
-  if file != &quot;&quot;
-    call s:findedit(cmd,file)
+function! s:Alternate(bang,cmd,...)
+  if a:0
+    return call('s:Find',[a:bang,1,a:cmd]+a:000)
   else
-    call s:warn(&quot;No alternate file is defined&quot;)
+    let cmd = a:cmd.(a:bang?&quot;!&quot;:&quot;&quot;)
+    let file = s:AlternateFile()
+    if file != &quot;&quot;
+      call s:findedit(cmd,file)
+    else
+      call s:warn(&quot;No alternate file is defined&quot;)
+    endif
   endif
 endfunction
 
@@ -2504,13 +2622,13 @@ function! s:AlternateFile()
   elseif f =~ '\&lt;config/environment\.rb$' | return &quot;config/database.yml&quot;
   elseif f =~ '\&lt;db/migrate/\d\d\d_'
     let num = matchstr(f,'\&lt;db/migrate/0*\zs\d\+\ze_')-1
-    return num ? s:migrationfor(num) : &quot;db/schema.rb&quot;
+    return rails#app().migration(num)
   elseif f =~ '\&lt;application\.js$'
     return &quot;app/helpers/application_helper.rb&quot;
   elseif t =~ '^js\&gt;'
     return &quot;public/javascripts/application.js&quot;
   elseif f =~ '\&lt;db/schema\.rb$'
-    return s:migrationfor(&quot;&quot;)
+    return rails#app().migration('')
   elseif t =~ '^view\&gt;'
     if t =~ '\&lt;layout\&gt;'
       let dest = fnamemodify(f,':r:s?/layouts\&gt;??').'/layout.'.fnamemodify(f,':e')
@@ -2518,18 +2636,24 @@ function! s:AlternateFile()
       let dest = f
     endif
     &quot; Go to the (r)spec, helper, controller, or (mailer) model
-    let spec       = fnamemodify(dest,':r:s?\&lt;app/?spec/?').&quot;_view_spec.rb&quot;
+    let spec1      = fnamemodify(dest,':s?\&lt;app/?spec/?').&quot;_spec.rb&quot;
+    let spec2      = fnamemodify(dest,':r:s?\&lt;app/?spec/?').&quot;_spec.rb&quot;
+    let spec3      = fnamemodify(dest,':r:r:s?\&lt;app/?spec/?').&quot;_spec.rb&quot;
     let helper     = fnamemodify(dest,':h:s?/views/?/helpers/?').&quot;_helper.rb&quot;
     let controller = fnamemodify(dest,':h:s?/views/?/controllers/?').&quot;_controller.rb&quot;
     let model      = fnamemodify(dest,':h:s?/views/?/models/?').&quot;.rb&quot;
-    if s:hasfile(spec)
-      return spec
-    elseif s:hasfile(helper)
+    if rails#app().has_file(spec1)
+      return spec1
+    elseif rails#app().has_file(spec2)
+      return spec2
+    elseif rails#app().has_file(spec3)
+      return spec3
+    elseif rails#app().has_file(helper)
       return helper
-    elseif s:hasfile(controller)
+    elseif rails#app().has_file(controller)
       let jumpto = expand(&quot;%:t:r&quot;)
       return controller.'#'.jumpto
-    elseif s:hasfile(model)
+    elseif rails#app().has_file(model)
       return model
     else
       return helper
@@ -2541,37 +2665,36 @@ function! s:AlternateFile()
     let controller = s:sub(s:sub(f,'/helpers/','/controllers/'),'_helper\.rb$','_controller.rb')
     let controller = s:sub(controller,'application_controller','application')
     let spec = s:sub(s:sub(f,'&lt;app/','spec/'),'\.rb$','_spec.rb')
-    if s:hasfile(spec)
+    if rails#app().has_file(spec)
       return spec
     else
       return controller
     endif
   elseif t =~ '\&lt;fixtures\&gt;' &amp;&amp; f =~ '\&lt;spec/'
-    let file = s:singularize(expand(&quot;%:t:r&quot;)).'_spec.rb'
+    let file = rails#singularize(expand(&quot;%:t:r&quot;)).'_spec.rb'
     return file
   elseif t =~ '\&lt;fixtures\&gt;'
-    let file = s:singularize(expand(&quot;%:t:r&quot;)).'_test.rb' &quot; .expand('%:e')
+    let file = rails#singularize(expand(&quot;%:t:r&quot;)).'_test.rb' &quot; .expand('%:e')
     return file
   elseif f == ''
     call s:warn(&quot;No filename present&quot;)
   elseif f =~ '\&lt;test/unit/routing_test\.rb$'
     return 'config/routes.rb'
   elseif t=~ '^spec-view\&gt;'
-    return s:sub(s:sub(f,'&lt;spec/','app/'),'_view_spec\.rb$','')
+    return s:sub(s:sub(f,'&lt;spec/','app/'),'_spec\.rb$','')
   elseif fnamemodify(f,&quot;:e&quot;) == &quot;rb&quot;
     let file = fnamemodify(f,&quot;:r&quot;)
     if file =~ '_\%(test\|spec\)$'
       let file = s:sub(file,'_%(test|spec)$','.rb')
     else
-      let file = file.'_test.rb'
+      let file .= '_test.rb'
     endif
     if t =~ '^model\&gt;'
       return s:sub(file,'app/models/','test/unit/').&quot;\n&quot;.s:sub(s:sub(file,'_test\.rb$','_spec.rb'),'app/models/','spec/models/')
     elseif t =~ '^controller\&gt;'
-      &quot;return s:sub(file,'app/controllers/','test/functional/')
       return s:sub(file,'&lt;app/controllers/','test/functional/').&quot;\n&quot;.s:sub(s:sub(file,'_test\.rb$','_spec.rb'),'app/controllers/','spec/controllers/')
     elseif t =~ '^test-unit\&gt;'
-      return s:sub(file,'test/unit/','app/models/')
+      return s:sub(file,'test/unit/','app/models/').&quot;\n&quot;.s:sub(file,'test/unit/','lib/')
     elseif t =~ '^test-functional\&gt;'
       if file =~ '_api\.rb'
         return s:sub(file,'test/functional/','app/apis/')
@@ -2580,6 +2703,10 @@ function! s:AlternateFile()
       else
         return s:sub(file,'test/functional/','')
       endif
+    elseif t == 'spec-lib'
+      return s:sub(file,'&lt;spec/','')
+    elseif t == 'lib'
+      return s:sub(f, '&lt;lib/(.*)\.rb$', 'test/unit/\1_test\.rb').&quot;\n&quot;.s:sub(f, '&lt;lib/(.*)\.rb$', 'spec/lib/\1_spec\.rb')
     elseif t =~ '^spec\&gt;'
       return s:sub(file,'&lt;spec/','app/')
     elseif file =~ '\&lt;vendor/.*/lib/'
@@ -2594,13 +2721,17 @@ function! s:AlternateFile()
   endif
 endfunction
 
-function! s:Related(bang,cmd)
-  let cmd = a:cmd.(a:bang?&quot;!&quot;:&quot;&quot;)
-  let file = s:RelatedFile()
-  if file != &quot;&quot;
-    call s:findedit(cmd,file)
+function! s:Related(bang,cmd,...)
+  if a:0
+    return call('s:Edit',[a:bang,1,a:cmd]+a:000)
   else
-    call s:warn(&quot;No related file is defined&quot;)
+    let cmd = a:cmd.(a:bang?&quot;!&quot;:&quot;&quot;)
+    let file = s:RelatedFile()
+    if file != &quot;&quot;
+      call s:findedit(cmd,file)
+    else
+      call s:warn(&quot;No related file is defined&quot;)
+    endif
   endif
 endfunction
 
@@ -2613,7 +2744,7 @@ function! s:RelatedFile()
   elseif t =~ '^\%(controller\|model-mailer\)\&gt;' &amp;&amp; lastmethod != &quot;&quot;
     let root = s:sub(s:sub(s:sub(f,'/application\.rb$','/shared_controller.rb'),'/%(controllers|models)/','/views/'),'%(_controller)=\.rb$','/'.lastmethod)
     let format = s:format('html')
-    if glob(RailsRoot().'/'.root.'.'.format.'.*[^~]') != ''
+    if glob(rails#app().path().'/'.root.'.'.format.'.*[^~]') != ''
       return root . '.' . format
     else
       return root
@@ -2635,7 +2766,7 @@ function! s:RelatedFile()
   elseif f =~ '\&lt;config/environment\.rb$' | return &quot;config/routes.rb&quot;
   elseif f =~ '\&lt;db/migrate/\d\d\d_'
     let num = matchstr(f,'\&lt;db/migrate/0*\zs\d\+\ze_')+1
-    let migr = s:migrationfor(num)
+    let migr = rails#app().migration(num)
     return migr == '' ? &quot;db/schema.rb&quot; : migr
   elseif t =~ '^test\&gt;' &amp;&amp; f =~ '\&lt;test/\w\+/'
     let target = s:sub(f,'.*&lt;test/\w+/','test/mocks/test/')
@@ -2647,8 +2778,6 @@ function! s:RelatedFile()
     return &quot;public/javascripts/application.js&quot;
   elseif t =~ '^view-layout\&gt;'
     return s:sub(s:sub(s:sub(f,'/views/','/controllers/'),'/layouts/(\k+)\..*$','/\1_controller.rb'),'&lt;application_controller\.rb$','application.rb')
-  &quot;elseif t=~ '^view-partial\&gt;'
-    &quot;call s:warn(&quot;No related file is defined&quot;)
   elseif t =~ '^view\&gt;'
     let controller  = s:sub(s:sub(f,'/views/','/controllers/'),'/(\k+%(\.\k+)=)\..*$','_controller.rb#\1')
     let controller2 = s:sub(s:sub(f,'/views/','/controllers/'),'/(\k+%(\.\k+)=)\..*$','.rb#\1')
@@ -2667,18 +2796,16 @@ function! s:RelatedFile()
   elseif t =~ '^controller\&gt;'
     return s:sub(s:sub(f,'/controllers/','/helpers/'),'%(_controller)=\.rb$','_helper.rb')
   elseif t=~ '^helper\&gt;'
-      return s:sub(s:sub(f,'/helpers/','/views/layouts/'),'%(_helper)=\.rb$','')
+    return s:findlayout(s:controller())
   elseif t =~ '^model-arb\&gt;'
-    &quot;call s:migrationEdit(0,cmd,'create_'.s:pluralize(expand('%:t:r')))
-    return s:migrationfor('create_'.s:pluralize(expand('%:t:r')))
+    return rails#app().migration('create_'.rails#pluralize(s:gsub(s:model(),'/','_')))
   elseif t =~ '^model-aro\&gt;'
     return s:sub(f,'_observer\.rb$','.rb')
   elseif t =~ '^api\&gt;'
     return s:sub(s:sub(f,'/apis/','/controllers/'),'_api\.rb$','_controller.rb')
   elseif f =~ '\&lt;db/schema\.rb$'
-    return s:migrationfor(1)
+    return rails#app().migration(1)
   else
-    &quot;call s:warn(&quot;No related file is defined&quot;)
     return &quot;&quot;
   endif
 endfunction
@@ -2686,8 +2813,6 @@ endfunction
 &quot; }}}1
 &quot; Partial Extraction {{{1
 
-&quot; Depends: s:error, s:sub, s:viewspattern, s:warn
-
 function! s:Extract(bang,...) range abort
   if a:0 == 0 || a:0 &gt; 1
     return s:error(&quot;Incorrect number of arguments&quot;)
@@ -2695,9 +2820,9 @@ function! s:Extract(bang,...) range abort
   if a:1 =~ '[^a-z0-9_/.]'
     return s:error(&quot;Invalid partial name&quot;)
   endif
-  let rails_root = RailsRoot()
+  let rails_root = rails#app().path()
   let ext = expand(&quot;%:e&quot;)
-  let file = a:1
+  let file = s:sub(a:1,'%(/|^)\zs_\ze[^/]*$','')
   let first = a:firstline
   let last = a:lastline
   let range = first.&quot;,&quot;.last
@@ -2718,10 +2843,10 @@ function! s:Extract(bang,...) range abort
   let fname = fnamemodify(file,&quot;:t&quot;)
   if fnamemodify(fname,&quot;:e&quot;) == &quot;&quot;
     let name = fname
-    let fname = fname.&quot;.&quot;.matchstr(expand(&quot;%:t&quot;),'\.\zs.*')
+    let fname .= &quot;.&quot;.matchstr(expand(&quot;%:t&quot;),'\.\zs.*')
   elseif fnamemodify(fname,&quot;:e&quot;) !~ '^'.s:viewspattern().'$'
     let name = fnamemodify(fname,&quot;:r&quot;)
-    let fname = fname.&quot;.&quot;.ext
+    let fname .= &quot;.&quot;.ext
   else
     let name = fnamemodify(fname,&quot;:r:r&quot;)
   endif
@@ -2729,7 +2854,7 @@ function! s:Extract(bang,...) range abort
   let collection = &quot;&quot;
   if dir =~ '^/'
     let out = (rails_root).dir.&quot;/_&quot;.fname
-  elseif dir == &quot;&quot;
+  elseif dir == &quot;&quot; || dir == &quot;.&quot;
     let out = (curdir).&quot;/_&quot;.fname
   elseif isdirectory(curdir.&quot;/&quot;.dir)
     let out = (curdir).&quot;/&quot;.dir.&quot;/_&quot;.fname
@@ -2738,8 +2863,6 @@ function! s:Extract(bang,...) range abort
   endif
   if filereadable(out)
     let partial_warn = 1
-    &quot;echoerr &quot;Partial exists&quot;
-    &quot;return
   endif
   if bufnr(out) &gt; 0
     if bufloaded(out)
@@ -2767,18 +2890,17 @@ function! s:Extract(bang,...) range abort
     if collection != ''
       let var = matchstr(collection,'^\k\+')
       let collection = s:sub(collection,'^\k+\&gt;','')
-      let first = first - 1
-      let last = last + 1
+      let first -= 1
+      let last += 1
     endif
   else
     let fspaces = spaces
   endif
-  &quot;silent exe range.&quot;write &quot;.out
   let renderstr = &quot;render :partial =&gt; '&quot;.fnamemodify(file,&quot;:r:r&quot;).&quot;'&quot;
   if collection != &quot;&quot;
-    let renderstr = renderstr.&quot;, :collection =&gt; &quot;.collection
+    let renderstr .= &quot;, :collection =&gt; &quot;.collection
   elseif &quot;@&quot;.name != var
-    let renderstr = renderstr.&quot;, :object =&gt; &quot;.var
+    let renderstr .= &quot;, :object =&gt; &quot;.var
   endif
   if ext =~? '^\%(rhtml\|erb\|dryml\)$'
     let renderstr = &quot;&lt;%= &quot;.renderstr.&quot; %&gt;&quot;
@@ -2810,8 +2932,7 @@ function! s:Extract(bang,...) range abort
   else
     new
   endif
-  let shortout = fnamemodify(out,':~:.')
-  &quot;exe &quot;silent file &quot;.s:escarg(shortout)
+  let shortout = fnamemodify(out,':.')
   silent file `=shortout`
   let &amp;ft = ft
   let @@ = partial
@@ -2832,8 +2953,6 @@ endfunction
 &quot; }}}1
 &quot; Migration Inversion {{{1
 
-&quot; Depends: s:sub, s:endof, s:gsub, s:error
-
 function! s:mkeep(str)
   &quot; Things to keep (like comments) from a migration statement
   return matchstr(a:str,' #[^{].*')
@@ -2881,7 +3000,7 @@ function! s:invertrange(beg,end)
           let add = s:sub(add,'\)=$',', :column =&gt; '.mat.'&amp;')
         endif
       endif
-      let add = add.s:mkeep(line)
+      let add .= s:mkeep(line)
     elseif line =~ '\&lt;remove_index\&gt;'
       let add = s:sub(s:sub(line,'&lt;remove_index','add_index'),':column\s*=&gt;\s*','')
     elseif line =~ '\&lt;rename_\%(table\|column\)\&gt;'
@@ -2909,7 +3028,7 @@ function! s:invertrange(beg,end)
       let add = &quot;&quot;
     endif
     let str = add.&quot;\n&quot;.str
-    let lnum = lnum + 1
+    let lnum += 1
   endwhile
   let str = s:gsub(str,'(\s*raise ActiveRecord::IrreversableMigration\n)+','\1')
   return str
@@ -2939,6 +3058,9 @@ function! s:Invert(bang)
   if !beg || !end
     return s:error(err)
   endif
+  if foldclosed(beg) &gt; 0
+    exe beg.&quot;foldopen!&quot;
+  endif
   if beg + 1 &lt; end
     exe (beg+1).&quot;,&quot;.(end-1).&quot;delete _&quot;
   endif
@@ -2954,73 +3076,49 @@ endfunction
 &quot; }}}1
 &quot; Cache {{{1
 
-function! s:cacheworks()
-  if v:version &lt; 700 || RailsRoot() == &quot;&quot;
-    return 0
-  endif
-  if !exists(&quot;s:cache&quot;)
-    let s:cache = {}
-  endif
-  if !has_key(s:cache,RailsRoot())
-    let s:cache[RailsRoot()] = {}
+let s:cache_prototype = {'dict': {}}
+
+function! s:cache_clear(...) dict
+  if a:0 == 0
+    let self.dict = {}
+  elseif has_key(self,'dict') &amp;&amp; has_key(self.dict,a:1)
+    unlet! self.dict[a:1]
   endif
-  return 1
 endfunction
 
-function! s:cacheclear(...)
-  if RailsRoot() == &quot;&quot; | return &quot;&quot; | endif
-  if !s:cacheworks() | return &quot;&quot; | endif
-  if a:0 == 1
-    if s:cachehas(a:1)
-      unlet! s:cache[RailsRoot()][a:1]
-    endif
-  else
-    let s:cache[RailsRoot()] = {}
+function! rails#cache_clear(...)
+  if exists('b:rails_root')
+    return call(rails#app().cache.clear,a:000,rails#app().cache)
   endif
 endfunction
 
-function! s:cache(...)
-  if !s:cacheworks() | return &quot;&quot; | endif
+function! s:cache_get(...) dict
   if a:0 == 1
-    return s:cache[RailsRoot()][a:1]
+    return self.dict[a:1]
   else
-    return s:cache[RailsRoot()]
+    return self.dict
   endif
 endfunction
 
-&quot;function! RailsCache(...)
-  &quot;if !s:cacheworks() | return &quot;&quot; | endif
-  &quot;if a:0 == 1
-    &quot;if s:cachehas(a:1)
-      &quot;return s:cache(a:1)
-    &quot;else
-      &quot;return &quot;&quot;
-    &quot;endif
-  &quot;else
-    &quot;return s:cache()
-  &quot;endif
-&quot;endfunction
-
-function! s:cachehas(key)
-  if !s:cacheworks() | return &quot;&quot; | endif
-  return has_key(s:cache(),a:key)
+function! s:cache_has(key) dict
+  return has_key(self.dict,a:key)
 endfunction
 
-function! s:cacheneeds(key)
-  if !s:cacheworks() | return &quot;&quot; | endif
-  return !has_key(s:cache(),a:key)
+function! s:cache_needs(key) dict
+  return !has_key(self.dict,a:key)
 endfunction
 
-function! s:cacheset(key,value)
-  if !s:cacheworks() | return &quot;&quot; | endif
-  let s:cache[RailsRoot()][a:key] = a:value
+function! s:cache_set(key,value) dict
+  let self.dict[a:key] = a:value
 endfunction
 
+call s:add_methods('cache', ['clear','needs','has','get','set'])
+
+let s:app_prototype.cache = s:cache_prototype
+
 &quot; }}}1
 &quot; Syntax {{{1
 
-&quot; Depends: s:rubyeval, s:gsub, cache functions
-
 function! s:resetomnicomplete()
   if exists(&quot;+completefunc&quot;) &amp;&amp; &amp;completefunc == 'syntaxcomplete#Complete'
     if exists(&quot;g:loaded_syntax_completion&quot;)
@@ -3032,54 +3130,58 @@ function! s:resetomnicomplete()
 endfunction
 
 function! s:helpermethods()
-  let s:rails_helper_methods = &quot;&quot;
+  return &quot;&quot;
         \.&quot;atom_feed auto_discovery_link_tag auto_link &quot;
-        \.&quot;benchmark button_to button_to_function &quot;
-        \.&quot;cache capture cdata_section check_box check_box_tag collection_select concat content_for content_tag content_tag_for country_options_for_select country_select cycle &quot;
+        \.&quot;benchmark button_to button_to_function button_to_remote &quot;
+        \.&quot;cache capture cdata_section check_box check_box_tag collection_select concat content_for content_tag content_tag_for current_cycle cycle &quot;
         \.&quot;date_select datetime_select debug define_javascript_functions distance_of_time_in_words distance_of_time_in_words_to_now div_for dom_class dom_id draggable_element draggable_element_js drop_receiving_element drop_receiving_element_js &quot;
         \.&quot;error_message_on error_messages_for escape_javascript escape_once evaluate_remote_response excerpt &quot;
         \.&quot;field_set_tag fields_for file_field file_field_tag form form_for form_remote_for form_remote_tag form_tag &quot;
         \.&quot;hidden_field hidden_field_tag highlight &quot;
         \.&quot;image_path image_submit_tag image_tag input &quot;
         \.&quot;javascript_cdata_section javascript_include_tag javascript_path javascript_tag &quot;
-        \.&quot;label label_tag link_to link_to_function link_to_if link_to_remote link_to_unless link_to_unless_current &quot;
+        \.&quot;l label label_tag link_to link_to_function link_to_if link_to_remote link_to_unless link_to_unless_current localize &quot;
         \.&quot;mail_to markdown &quot;
         \.&quot;number_to_currency number_to_human_size number_to_percentage number_to_phone number_with_delimiter number_with_precision &quot;
         \.&quot;observe_field observe_form option_groups_from_collection_for_select options_for_select options_from_collection_for_select &quot;
         \.&quot;partial_path password_field password_field_tag path_to_image path_to_javascript path_to_stylesheet periodically_call_remote pluralize &quot;
         \.&quot;radio_button radio_button_tag remote_form_for remote_function reset_cycle &quot;
         \.&quot;sanitize sanitize_css select select_date select_datetime select_day select_hour select_minute select_month select_second select_tag select_time select_year simple_format sortable_element sortable_element_js strip_links strip_tags stylesheet_link_tag stylesheet_path submit_tag submit_to_remote &quot;
-        \.&quot;tag text_area text_area_tag text_field text_field_tag textilize textilize_without_paragraph time_ago_in_words time_select time_zone_options_for_select time_zone_select truncate &quot;
+        \.&quot;t tag text_area text_area_tag text_field text_field_tag textilize textilize_without_paragraph time_ago_in_words time_select time_zone_options_for_select time_zone_select translate truncate &quot;
         \.&quot;update_page update_page_tag url_for &quot;
         \.&quot;visual_effect &quot;
         \.&quot;word_wrap&quot;
+endfunction
 
-  &quot; The list of helper methods used to be derived automatically.  Let's keep
-  &quot; this code around in case it's needed again.
-  if !exists(&quot;s:rails_helper_methods&quot;)
-    if g:rails_expensive
-      let s:rails_helper_methods = &quot;&quot;
-      if has(&quot;ruby&quot;)
-        &quot; &amp;&amp; (has(&quot;win32&quot;) || has(&quot;win32unix&quot;))
-        ruby begin; require 'rubygems'; rescue LoadError; end
-        if exists(&quot;g:rubycomplete_rails&quot;) &amp;&amp; g:rubycomplete_rails
-          ruby begin; require VIM::evaluate('RailsRoot()')+'/config/environment'; rescue Exception; end
-        else
-          ruby begin; require 'active_support'; require 'action_controller'; require 'action_view'; rescue LoadError; end
-        end
-        ruby begin; h = ActionView::Helpers.constants.grep(/Helper$/).collect {|c|ActionView::Helpers.const_get c}.collect {|c| c.public_instance_methods(false)}.collect {|es| es.reject {|e| e =~ /_with(out)?_deprecation$/ || es.include?(&quot;#{e}_without_deprecation&quot;)}}.flatten.sort.uniq.reject {|m| m =~ /[=?!]$/}; VIM::command('let s:rails_helper_methods = &quot;%s&quot;' % h.join(&quot; &quot;)); rescue Exception; end
-      endif
-      if s:rails_helper_methods == &quot;&quot;
-        let s:rails_helper_methods = s:rubyeval('require %{action_controller}; require %{action_view}; h = ActionView::Helpers.constants.grep(/Helper$/).collect {|c|ActionView::Helpers.const_get c}.collect {|c| c.public_instance_methods(false)}.collect {|es| es.reject {|e| e =~ /_with(out)?_deprecation$/ || es.include?(%{#{e}_without_deprecation})}}.flatten.sort.uniq.reject {|m| m =~ /[=?!]$/}; puts h.join(%{ })',&quot;link_to&quot;)
-      endif
+function! s:app_user_classes() dict
+  if self.cache.needs(&quot;user_classes&quot;)
+    let controllers = self.relglob(&quot;app/controllers/&quot;,&quot;**/*&quot;,&quot;.rb&quot;)
+    call map(controllers,'v:val == &quot;application&quot; ? v:val.&quot;_controller&quot; : v:val')
+    let classes =
+          \ self.relglob(&quot;app/models/&quot;,&quot;**/*&quot;,&quot;.rb&quot;) +
+          \ controllers +
+          \ self.relglob(&quot;app/helpers/&quot;,&quot;**/*&quot;,&quot;.rb&quot;) +
+          \ self.relglob(&quot;lib/&quot;,&quot;**/*&quot;,&quot;.rb&quot;)
+    call map(classes,'rails#camelize(v:val)')
+    call self.cache.set(&quot;user_classes&quot;,classes)
+  endif
+  return self.cache.get('user_classes')
+endfunction
+
+function! s:app_user_assertions() dict
+  if self.cache.needs(&quot;user_assertions&quot;)
+    if self.has_file(&quot;test/test_helper.rb&quot;)
+      let assertions = map(filter(s:readfile(self.path(&quot;test/test_helper.rb&quot;)),'v:val =~ &quot;^  def assert_&quot;'),'matchstr(v:val,&quot;^  def \\zsassert_\\w\\+&quot;)')
     else
-      let s:rails_helper_methods = &quot;link_to&quot;
+      let assertions = []
     endif
+    call self.cache.set(&quot;user_assertions&quot;,assertions)
   endif
-  &quot;let g:rails_helper_methods = s:rails_helper_methods
-  return s:rails_helper_methods
+  return self.cache.get('user_assertions')
 endfunction
 
+call s:add_methods('app', ['user_classes','user_assertions'])
+
 function! s:BufSyntax()
   if (!exists(&quot;g:rails_syntax&quot;) || g:rails_syntax)
     let t = RailsFileType()
@@ -3088,7 +3190,7 @@ function! s:BufSyntax()
     let s:prototype_classes = &quot;Prototype Class Abstract Try PeriodicalExecuter Enumerable Hash ObjectRange Element Ajax Responders Base Request Updater PeriodicalUpdater Toggle Insertion Before Top Bottom After ClassNames Form Serializers TimedObserver Observer EventObserver Event Position Effect Effect2 Transitions ScopedQueue Queues DefaultOptions Parallel Opacity Move MoveBy Scale Highlight ScrollTo Fade Appear Puff BlindUp BlindDown SwitchOff DropOut Shake SlideDown SlideUp Squish Grow Shrink Pulsate Fold&quot;
 
     let rails_helper_methods = '+\.\@&lt;!\&lt;\('.s:gsub(s:helpermethods(),'\s+','\\|').'\)\&gt;+'
-    let classes = s:gsub(RailsUserClasses(),'::',' ')
+    let classes = s:gsub(join(rails#app().user_classes(),' '),'::',' ')
     if &amp;syntax == 'ruby'
       if classes != ''
         exe &quot;syn keyword rubyRailsUserClass &quot;.classes.&quot; containedin=rubyClassDeclaration,rubyModuleDeclaration,rubyClass,rubyModule&quot;
@@ -3100,13 +3202,11 @@ function! s:BufSyntax()
         syn keyword rubyRailsAPIMethod api_method inflect_names
       endif
       if t =~ '^model$' || t =~ '^model-arb\&gt;'
-        syn keyword rubyRailsARMethod acts_as_list acts_as_nested_set acts_as_tree composed_of serialize
-        syn keyword rubyRailsARAssociationMethod belongs_to has_one has_many has_and_belongs_to_many
-        &quot;syn match rubyRailsARCallbackMethod '\&lt;\(before\|after\)_\(create\|destroy\|save\|update\|validation\|validation_on_create\|validation_on_update\)\&gt;'
+        syn keyword rubyRailsARMethod default_scope named_scope serialize
+        syn keyword rubyRailsARAssociationMethod belongs_to has_one has_many has_and_belongs_to_many composed_of
         syn keyword rubyRailsARCallbackMethod before_create before_destroy before_save before_update before_validation before_validation_on_create before_validation_on_update
         syn keyword rubyRailsARCallbackMethod after_create after_destroy after_save after_update after_validation after_validation_on_create after_validation_on_update
         syn keyword rubyRailsARClassMethod attr_accessible attr_protected establish_connection set_inheritance_column set_locking_column set_primary_key set_sequence_name set_table_name
-        &quot;syn keyword rubyRailsARCallbackMethod after_find after_initialize
         syn keyword rubyRailsARValidationMethod validate validate_on_create validate_on_update validates_acceptance_of validates_associated validates_confirmation_of validates_each validates_exclusion_of validates_format_of validates_inclusion_of validates_length_of validates_numericality_of validates_presence_of validates_size_of validates_uniqueness_of
         syn keyword rubyRailsMethod logger
       endif
@@ -3115,7 +3215,6 @@ function! s:BufSyntax()
       endif
       if t =~ '^model-mailer\&gt;'
         syn keyword rubyRailsMethod logger
-        &quot; Misnomer but who cares
         syn keyword rubyRailsControllerMethod helper helper_attr helper_method
       endif
       if t =~ '^controller\&gt;' || t =~ '^view\&gt;' || t=~ '^helper\&gt;'
@@ -3126,16 +3225,14 @@ function! s:BufSyntax()
         syn keyword rubyRailsMethod logger
       endif
       if t =~ '^helper\&gt;' || t=~ '^view\&gt;'
-        &quot;exe &quot;syn match rubyRailsHelperMethod &quot;.rails_helper_methods
         exe &quot;syn keyword rubyRailsHelperMethod &quot;.s:sub(s:helpermethods(),'&lt;select\s+','')
         syn match rubyRailsHelperMethod '\&lt;select\&gt;\%(\s*{\|\s*do\&gt;\|\s*(\=\s*&amp;\)\@!'
         syn match rubyRailsViewMethod '\.\@&lt;!\&lt;\(h\|html_escape\|u\|url_encode\|controller\)\&gt;'
         if t =~ '\&lt;partial\&gt;'
           syn keyword rubyRailsMethod local_assigns
         endif
-        &quot;syn keyword rubyRailsDeprecatedMethod start_form_tag end_form_tag link_to_image human_size update_element_function
       elseif t =~ '^controller\&gt;'
-        syn keyword rubyRailsControllerMethod helper helper_attr helper_method filter layout url_for serialize exempt_from_layout filter_parameter_logging hide_action cache_sweeper
+        syn keyword rubyRailsControllerMethod helper helper_attr helper_method filter layout url_for serialize exempt_from_layout filter_parameter_logging hide_action cache_sweeper protect_from_forgery
         syn match rubyRailsDeprecatedMethod '\&lt;render_\%(action\|text\|file\|template\|nothing\|without_layout\)\&gt;'
         syn keyword rubyRailsRenderMethod render_to_string redirect_to head
         syn match   rubyRailsRenderMethod '\&lt;respond_to\&gt;?\@!'
@@ -3143,16 +3240,14 @@ function! s:BufSyntax()
         syn keyword rubyRailsFilterMethod verify
       endif
       if t =~ '^\%(db-\)\=\%(migration\|schema\)\&gt;'
-        syn keyword rubyRailsMigrationMethod create_table drop_table rename_table add_column rename_column change_column change_column_default remove_column add_index remove_index
+        syn keyword rubyRailsMigrationMethod create_table change_table drop_table rename_table add_column rename_column change_column change_column_default remove_column add_index remove_index
       endif
       if t =~ '^test\&gt;'
-        if s:cacheneeds(&quot;user_asserts&quot;) &amp;&amp; filereadable(RailsRoot().&quot;/test/test_helper.rb&quot;)
-          call s:cacheset(&quot;user_asserts&quot;,map(filter(readfile(RailsRoot().&quot;/test/test_helper.rb&quot;),'v:val =~ &quot;^  def assert_&quot;'),'matchstr(v:val,&quot;^  def \\zsassert_\\w\\+&quot;)'))
-        endif
-        if s:cachehas(&quot;user_asserts&quot;) &amp;&amp; !empty(s:cache(&quot;user_asserts&quot;))
-          exe &quot;syn keyword rubyRailsUserMethod &quot;.join(s:cache(&quot;user_asserts&quot;))
+        if !empty(rails#app().user_assertions())
+          exe &quot;syn keyword rubyRailsUserMethod &quot;.join(rails#app().user_assertions())
         endif
         syn keyword rubyRailsTestMethod add_assertion assert assert_block assert_equal assert_in_delta assert_instance_of assert_kind_of assert_match assert_nil assert_no_match assert_not_equal assert_not_nil assert_not_same assert_nothing_raised assert_nothing_thrown assert_operator assert_raise assert_respond_to assert_same assert_send assert_throws assert_recognizes assert_generates assert_routing flunk fixtures fixture_path use_transactional_fixtures use_instantiated_fixtures assert_difference assert_no_difference assert_valid
+        syn keyword rubyRailsTestMethod test setup teardown
         if t !~ '^test-unit\&gt;'
           syn match   rubyRailsTestControllerMethod  '\.\@&lt;!\&lt;\%(get\|post\|put\|delete\|head\|process\|assigns\)\&gt;'
           syn keyword rubyRailsTestControllerMethod assert_response assert_redirected_to assert_template assert_recognizes assert_generates assert_routing assert_dom_equal assert_dom_not_equal assert_select assert_select_rjs assert_select_encoded assert_select_email assert_tag assert_no_tag
@@ -3162,6 +3257,7 @@ function! s:BufSyntax()
         syn keyword rubyRailsTestMethod violated pending
         if t !~ '^spec-model\&gt;'
           syn match   rubyRailsTestControllerMethod  '\.\@&lt;!\&lt;\%(get\|post\|put\|delete\|head\|process\|assigns\)\&gt;'
+          syn keyword rubyRailsTestControllerMethod  integrate_views
           syn keyword rubyRailsMethod params request response session flash
         endif
       endif
@@ -3175,7 +3271,7 @@ function! s:BufSyntax()
         syn match rubyRailsMethod '\.\zs\%(connect\|resources\=\|root\|named_route\|namespace\)\&gt;'
       endif
       syn keyword rubyRailsMethod debugger
-      syn keyword rubyRailsMethod alias_attribute alias_method_chain attr_accessor_with_default attr_internal attr_internal_accessor attr_internal_reader attr_internal_writer delegate mattr_accessor mattr_reader mattr_writer
+      syn keyword rubyRailsMethod alias_attribute alias_method_chain attr_accessor_with_default attr_internal attr_internal_accessor attr_internal_reader attr_internal_writer delegate mattr_accessor mattr_reader mattr_writer superclass_delegating_accessor superclass_delegating_reader superclass_delegating_writer
       syn keyword rubyRailsMethod cattr_accessor cattr_reader cattr_writer class_inheritable_accessor class_inheritable_array class_inheritable_array_writer class_inheritable_hash class_inheritable_hash_writer class_inheritable_option class_inheritable_reader class_inheritable_writer inheritable_attributes read_inheritable_attribute reset_inheritable_attributes write_inheritable_array write_inheritable_attribute write_inheritable_hash
       syn keyword rubyRailsInclude require_dependency gem
 
@@ -3204,7 +3300,6 @@ function! s:BufSyntax()
       exe &quot;syn sync minlines=&quot; . g:ruby_minlines
       syn case match
       syn region  rubyString   matchgroup=rubyStringDelimiter start=+%Q\=&lt;+ end=+&gt;+ contains=@htmlTop,@rubyStringSpecial
-      &quot;syn region  rubyString   matchgroup=rubyStringDelimiter start=+%q&lt;+ end=+&gt;+ contains=@htmlTop
       syn cluster htmlArgCluster add=@rubyStringSpecial
       syn cluster htmlPreProc    add=@rubyStringSpecial
 
@@ -3314,7 +3409,7 @@ function! s:HiDefaults()
   hi def link railsStringSpecial              Identifier
 endfunction
 
-function! RailslogSyntax()
+function! rails#log_syntax()
   syn match   railslogRender      '^\s*\&lt;\%(Processing\|Rendering\|Rendered\|Redirected\|Completed\)\&gt;'
   syn match   railslogComment     '^\s*# .*'
   syn match   railslogModel       '^\s*\u\%(\w\|:\)* \%(Load\%( Including Associations\| IDs For Limited Eager Loading\)\=\|Columns\|Count\|Update\|Destroy\|Delete all\)\&gt;' skipwhite nextgroup=railslogModelNum
@@ -3354,9 +3449,6 @@ endfunction
 &quot; }}}1
 &quot; Statusline {{{1
 
-&quot; Depends: nothing!
-&quot; Provides: s:BufInitStatusline
-
 function! s:addtostatus(letter,status)
   let status = a:status
   if status !~ 'Rails' &amp;&amp; g:rails_statusline
@@ -3376,7 +3468,7 @@ function! s:BufInitStatusline()
     if &amp;l:statusline == ''
       let &amp;l:statusline='%&lt;%f %h%m%r%='
       if &amp;ruler
-        let &amp;l:statusline = &amp;l:statusline . '%-16( %l,%c-%v %)%P'
+        let &amp;l:statusline .= '%-16( %l,%c-%v %)%P'
       endif
     endif
     let &amp;l:statusline = s:InjectIntoStatusline(&amp;l:statusline)
@@ -3388,7 +3480,7 @@ function! s:InitStatusline()
     if &amp;g:statusline == ''
       let &amp;g:statusline='%&lt;%f %h%m%r%='
       if &amp;ruler
-        let &amp;g:statusline = &amp;g:statusline . '%-16( %l,%c-%v %)%P'
+        let &amp;g:statusline .= '%-16( %l,%c-%v %)%P'
       endif
     endif
     let &amp;g:statusline = s:InjectIntoStatusline(&amp;g:statusline)
@@ -3407,7 +3499,7 @@ function! s:InjectIntoStatusline(status)
       let status=substitute(status,'%=','%{RailsStatusline()}%=','')
     endif
     if status !~ 'Rails' &amp;&amp; status != ''
-      let status=status.'%{RailsStatusline()}'
+      let status .= '%{RailsStatusline()}'
     endif
   endif
   return status
@@ -3442,16 +3534,13 @@ endfunction
 &quot; }}}1
 &quot; Mappings {{{1
 
-&quot; Depends: nothing!
-&quot; Exports: s:BufMappings
-
 function! s:BufMappings()
-  map &lt;buffer&gt; &lt;silent&gt; &lt;Plug&gt;RailsAlternate  :A&lt;CR&gt;
-  map &lt;buffer&gt; &lt;silent&gt; &lt;Plug&gt;RailsRelated    :R&lt;CR&gt;
-  map &lt;buffer&gt; &lt;silent&gt; &lt;Plug&gt;RailsFind       :REfind&lt;CR&gt;
-  map &lt;buffer&gt; &lt;silent&gt; &lt;Plug&gt;RailsSplitFind  :RSfind&lt;CR&gt;
-  map &lt;buffer&gt; &lt;silent&gt; &lt;Plug&gt;RailsVSplitFind :RVfind&lt;CR&gt;
-  map &lt;buffer&gt; &lt;silent&gt; &lt;Plug&gt;RailsTabFind    :RTfind&lt;CR&gt;
+  nnoremap &lt;buffer&gt; &lt;silent&gt; &lt;Plug&gt;RailsAlternate  :&lt;C-U&gt;A&lt;CR&gt;
+  nnoremap &lt;buffer&gt; &lt;silent&gt; &lt;Plug&gt;RailsRelated    :&lt;C-U&gt;R&lt;CR&gt;
+  nnoremap &lt;buffer&gt; &lt;silent&gt; &lt;Plug&gt;RailsFind       :&lt;C-U&gt;REfind&lt;CR&gt;
+  nnoremap &lt;buffer&gt; &lt;silent&gt; &lt;Plug&gt;RailsSplitFind  :&lt;C-U&gt;RSfind&lt;CR&gt;
+  nnoremap &lt;buffer&gt; &lt;silent&gt; &lt;Plug&gt;RailsVSplitFind :&lt;C-U&gt;RVfind&lt;CR&gt;
+  nnoremap &lt;buffer&gt; &lt;silent&gt; &lt;Plug&gt;RailsTabFind    :&lt;C-U&gt;RTfind&lt;CR&gt;
   if g:rails_mappings
     if !hasmapto(&quot;&lt;Plug&gt;RailsFind&quot;)
       nmap &lt;buffer&gt; gf              &lt;Plug&gt;RailsFind
@@ -3470,7 +3559,6 @@ function! s:BufMappings()
     endif
     if exists(&quot;$CREAM&quot;)
       imap &lt;buffer&gt; &lt;C-CR&gt; &lt;C-O&gt;&lt;Plug&gt;RailsFind
-      &quot; Are these a good idea?
       imap &lt;buffer&gt; &lt;M-[&gt;  &lt;C-O&gt;&lt;Plug&gt;RailsAlternate
       imap &lt;buffer&gt; &lt;M-]&gt;  &lt;C-O&gt;&lt;Plug&gt;RailsRelated
     endif
@@ -3482,10 +3570,8 @@ endfunction
 &quot; }}}1
 &quot; Project {{{
 
-&quot; Depends: s:gsub, s:escarg, s:warn, s:sub, s:relglob
-
 function! s:Project(bang,arg)
-  let rr = RailsRoot()
+  let rr = rails#app().path()
   exe &quot;Project &quot;.a:arg
   let line = search('^[^ =]*=&quot;'.s:gsub(rr,'[\/]','[\\/]').'&quot;')
   let projname = s:gsub(fnamemodify(rr,':t'),'\=','-') &quot; .'_on_rails'
@@ -3511,7 +3597,7 @@ function! s:Project(bang,arg)
 .
     endif
     let line = line('.')+1
-    call s:NewProject(projname,rr,a:bang)
+    call s:NewProject(projname,rr)
   endif
   normal! zMzo
   if search(&quot;^ app=app {&quot;,&quot;W&quot;,line+10)
@@ -3521,9 +3607,9 @@ function! s:Project(bang,arg)
   normal! 0zt
 endfunction
 
-function! s:NewProject(proj,rr,fancy)
+function! s:NewProject(proj,rr)
     let line = line('.')+1
-    let template = s:NewProjectTemplate(a:proj,a:rr,a:fancy)
+    let template = s:NewProjectTemplate(a:proj,a:rr)
     silent put =template
     exe line
     &quot; Ugh. how else can I force detecting folds?
@@ -3547,164 +3633,130 @@ function! s:NewProject(proj,rr,fancy)
     endif
 endfunction
 
-function! s:NewProjectTemplate(proj,rr,fancy)
+function! s:NewProjectTemplate(proj,rr)
   let str = a:proj.'=&quot;'.a:rr.&quot;\&quot; CD=. filter=\&quot;*\&quot; {\n&quot;
-  let str = str.&quot; app=app {\n&quot;
-  if isdirectory(a:rr.'/app/apis')
-    let str = str.&quot;  apis=apis {\n  }\n&quot;
-  endif
-  let str = str.&quot;  controllers=controllers filter=\&quot;**\&quot; {\n  }\n&quot;
-  let str = str.&quot;  helpers=helpers filter=\&quot;**\&quot; {\n  }\n&quot;
-  let str = str.&quot;  models=models filter=\&quot;**\&quot; {\n  }\n&quot;
-  if a:fancy
-    let str = str.&quot;  views=views {\n&quot;
-    let views = s:relglob(a:rr.'/app/views/','*').&quot;\n&quot;
-    while views != ''
-      let dir = matchstr(views,'^.\{-\}\ze\n')
-      let views = s:sub(views,'^.{-}\n','')
-      let str = str.&quot;   &quot;.dir.&quot;=&quot;.dir.' filter=&quot;**&quot; {'.&quot;\n   }\n&quot;
-    endwhile
-    let str = str.&quot;  }\n&quot;
-  else
-    let str = str.&quot;  views=views filter=\&quot;**\&quot; {\n  }\n&quot;
-  endif
-  let str = str . &quot; }\n&quot;
-  let str = str . &quot; config=config {\n  environments=environments {\n  }\n }\n&quot;
-  let str = str . &quot; db=db {\n&quot;
-  if isdirectory(a:rr.'/db/migrate')
-    let str = str . &quot;  migrate=migrate {\n  }\n&quot;
-  endif
-  let str = str . &quot; }\n&quot;
-  let str = str . &quot; lib=lib filter=\&quot;* */**/*.rb \&quot; {\n  tasks=tasks filter=\&quot;**/*.rake\&quot; {\n  }\n }\n&quot;
-  let str = str . &quot; public=public {\n  images=images {\n  }\n  javascripts=javascripts {\n  }\n  stylesheets=stylesheets {\n  }\n }\n&quot;
+  let str .= &quot; app=app {\n&quot;
+  for dir in ['apis','controllers','helpers','models','views']
+    let str .= s:addprojectdir(a:rr,'app',dir)
+  endfor
+  let str .= &quot; }\n&quot;
+  let str .= &quot; config=config {\n  environments=environments {\n  }\n }\n&quot;
+  let str .= &quot; db=db {\n&quot;
+  let str .= s:addprojectdir(a:rr,'db','migrate')
+  let str .= &quot; }\n&quot;
+  let str .= &quot; lib=lib filter=\&quot;* */**/*.rb \&quot; {\n  tasks=tasks filter=\&quot;**/*.rake\&quot; {\n  }\n }\n&quot;
+  let str .= &quot; public=public {\n  images=images {\n  }\n  javascripts=javascripts {\n  }\n  stylesheets=stylesheets {\n  }\n }\n&quot;
   if isdirectory(a:rr.'/spec')
-    let str = str . &quot; spec=spec {\n&quot;
-    let str = str . &quot;  controllers=controllers filter=\&quot;**\&quot; {\n  }\n&quot;
-    let str = str . &quot;  fixtures=fixtures filter=\&quot;**\&quot; {\n  }\n&quot;
-    let str = str . &quot;  helpers=helpers filter=\&quot;**\&quot; {\n  }\n&quot;
-    let str = str . &quot;  models=models filter=\&quot;**\&quot; {\n  }\n&quot;
-    let str = str . &quot;  views=views filter=\&quot;**\&quot; {\n  }\n }\n&quot;
-  endif
-  let str = str . &quot; test=test {\n&quot;
-  if isdirectory(a:rr.'/test/fixtures')
-    let str = str . &quot;  fixtures=fixtures filter=\&quot;**\&quot; {\n  }\n&quot;
-  endif
-  if isdirectory(a:rr.'/test/functional')
-    let str = str . &quot;  functional=functional filter=\&quot;**\&quot; {\n  }\n&quot;
-  endif
-  if isdirectory(a:rr.'/test/integration')
-    let str = str . &quot;  integration=integration filter=\&quot;**\&quot; {\n  }\n&quot;
-  endif
-  if isdirectory(a:rr.'/test/mocks')
-    let str = str . &quot;  mocks=mocks filter=\&quot;**\&quot; {\n  }\n&quot;
-  endif
-  if isdirectory(a:rr.'/test/unit')
-    let str = str . &quot;  unit=unit filter=\&quot;**\&quot; {\n  }\n&quot;
-  endif
-  let str = str . &quot; }\n}\n&quot;
+    let str .= &quot; spec=spec {\n&quot;
+    for dir in ['controllers','fixtures','helpers','models','views']
+      let str .= s:addprojectdir(a:rr,'spec',dir)
+    endfor
+    let str .= &quot; }\n&quot;
+  endif
+  if isdirectory(a:rr.'/test')
+    let str .= &quot; test=test {\n&quot;
+    for dir in ['fixtures','functional','integration','mocks','unit']
+      let str .= s:addprojectdir(a:rr,'test',dir)
+    endfor
+    let str .= &quot; }\n&quot;
+  end
+  let str .= &quot;}\n&quot;
   return str
 endfunction
 
+function! s:addprojectdir(rr,parentdir,dir)
+  if isdirectory(a:rr.'/'.a:parentdir.'/'.a:dir)
+    return '  '.a:dir.'='.a:dir.&quot; filter=\&quot;**\&quot; {\n  }\n&quot;
+  else
+    return ''
+  endif
+endfunction
+
 &quot; }}}1
 &quot; Database {{{1
 
-&quot; Depends: s:environment, s:rubyeval, s:rv, reloadability
-
 function! s:extractdbvar(str,arg)
   return matchstr(&quot;\n&quot;.a:str.&quot;\n&quot;,'\n'.a:arg.'=\zs.\{-\}\ze\n')
 endfunction
 
-function! s:BufDatabase(...)
-  if exists(&quot;s:lock_database&quot;)
-    return
+function! s:app_dbext_settings(environment) dict
+  if self.cache.needs('dbext_settings')
+    call self.cache.set('dbext_settings',{})
   endif
-  let s:lock_database = 1
-  let rv = s:rv()
-  if (a:0 &amp;&amp; a:1 &gt; 1)
-    unlet! s:dbext_type_{rv}
-  endif
-  if (a:0 &gt; 1 &amp;&amp; a:2 != '')
-    let env = a:2
-  else
-    let env = s:environment()
-  endif
-  &quot; Crude caching mechanism
-  if !exists(&quot;s:dbext_type_&quot;.rv)
-    if exists(&quot;g:loaded_dbext&quot;) &amp;&amp; (g:rails_dbext + (a:0 ? a:1 : 0)) &gt; 0 &amp;&amp; filereadable(RailsRoot().&quot;/config/database.yml&quot;)
-      &quot; Ideally we would filter this through ERB but that could be insecure.
-      &quot; It might be possible to make use of taint checking.
+  let cache = self.cache.get('dbext_settings')
+  if !has_key(cache,a:environment)
+    let dict = {}
+    if self.has_file(&quot;config/database.yml&quot;)
       let out = &quot;&quot;
       if has(&quot;ruby&quot;)
-        ruby require 'yaml'; VIM::command('let out = %s' % File.open(VIM::evaluate(&quot;RailsRoot()&quot;)+&quot;/config/database.yml&quot;) {|f| y = YAML::load(f); e = y[VIM::evaluate(&quot;env&quot;)]; i=0; e=y[e] while e.respond_to?(:to_str) &amp;&amp; (i+=1)&lt;16; e.map {|k,v| &quot;#{k}=#{v}\n&quot; if v}.compact.join }.inspect) rescue nil
+        ruby require 'yaml'; VIM::command('let out = %s' % File.open(VIM::evaluate(&quot;self.path()&quot;)+&quot;/config/database.yml&quot;) {|f| y = YAML::load(f); e = y[VIM::evaluate(&quot;a:environment&quot;)]; i=0; e=y[e] while e.respond_to?(:to_str) &amp;&amp; (i+=1)&lt;16; e.map {|k,v| &quot;#{k}=#{v}\n&quot; if v}.compact.join }.inspect) rescue nil
       endif
       if out == &quot;&quot;
-        let cmdb = 'require %{yaml}; File.open(%q{'.RailsRoot().'/config/database.yml}) {|f| y = YAML::load(f); e = y[%{'
+        let cmdb = 'require %{yaml}; File.open(%q{'.self.path().'/config/database.yml}) {|f| y = YAML::load(f); e = y[%{'
         let cmde = '}]; i=0; e=y[e] while e.respond_to?(:to_str) &amp;&amp; (i+=1)&lt;16; e.each{|k,v|puts k.to_s+%{=}+v.to_s}}'
-        if a:0 ? a:1 : g:rails_expensive
-          let out = s:rubyeval(cmdb.env.cmde,'')
-        else
-          unlet! s:lock_database
-          return
-        endif
+        let out = self.lightweight_ruby_eval(cmdb.a:environment.cmde)
       endif
       let adapter = s:extractdbvar(out,'adapter')
-      let s:dbext_bin_{rv} = ''
-      let s:dbext_integratedlogin_{rv} = ''
-      if adapter == 'postgresql'
-        let adapter = 'pgsql'
-      elseif adapter == 'sqlite3'
-        let adapter = 'sqlite'
-        &quot; Does not appear to work
-        let s:dbext_bin = 'sqlite3'
-      elseif adapter == 'sqlserver'
-        let adapter = 'sqlsrv'
-      elseif adapter == 'sybase'
-        let adapter = 'asa'
-      elseif adapter == 'oci'
-        let adapter = 'ora'
-      endif
-      let s:dbext_type_{rv} = toupper(adapter)
-      let s:dbext_user_{rv} = s:extractdbvar(out,'username')
-      let s:dbext_passwd_{rv} = s:extractdbvar(out,'password')
-      if s:dbext_passwd_{rv} == '' &amp;&amp; adapter == 'mysql'
+      let adapter = get({'postgresql': 'pgsql', 'sqlite3': 'sqlite', 'sqlserver': 'sqlsrv', 'sybase': 'asa', 'oci': 'ora'},adapter,adapter)
+      let dict['type'] = toupper(adapter)
+      let dict['user'] = s:extractdbvar(out,'username')
+      let dict['passwd'] = s:extractdbvar(out,'password')
+      if dict['passwd'] == '' &amp;&amp; adapter == 'mysql'
         &quot; Hack to override password from .my.cnf
-        let s:dbext_extra_{rv} = ' --password='
+        let dict['extra'] = ' --password='
       else
-        let s:dbext_extra_{rv} = ''
+        let dict['extra'] = ''
+      endif
+      let dict['dbname'] = s:extractdbvar(out,'database')
+      if dict['dbname'] == ''
+        let dict['dbname'] = s:extractdbvar(out,'dbfile')
       endif
-      let s:dbext_dbname_{rv} = s:extractdbvar(out,'database')
-      if s:dbext_dbname_{rv} != '' &amp;&amp; s:dbext_dbname_{rv} !~ '^:' &amp;&amp; adapter =~? '^sqlite'
-        let s:dbext_dbname_{rv} = RailsRoot().'/'.s:dbext_dbname_{rv}
+      if dict['dbname'] != '' &amp;&amp; dict['dbname'] !~ '^:' &amp;&amp; adapter =~? '^sqlite'
+        let dict['dbname'] = self.path(dict['dbname'])
       endif
-      let s:dbext_profile_{rv} = ''
-      let s:dbext_host_{rv} = s:extractdbvar(out,'host')
-      let s:dbext_port_{rv} = s:extractdbvar(out,'port')
-      let s:dbext_dsnname_{rv} = s:extractdbvar(out,'dsn')
-      if s:dbext_host_{rv} =~? '^\cDBI:'
-        if s:dbext_host_{rv} =~? '\c\&lt;Trusted[_ ]Connection\s*=\s*yes\&gt;'
-          let s:dbext_integratedlogin_{rv} = 1
+      let dict['profile'] = ''
+      let dict['host'] = s:extractdbvar(out,'host')
+      let dict['port'] = s:extractdbvar(out,'port')
+      let dict['dsnname'] = s:extractdbvar(out,'dsn')
+      if dict['host'] =~? '^\cDBI:'
+        if dict['host'] =~? '\c\&lt;Trusted[_ ]Connection\s*=\s*yes\&gt;'
+          let dict['integratedlogin'] = 1
         endif
-        let s:dbext_host_{rv} = matchstr(s:dbext_host_{rv},'\c\&lt;\%(Server\|Data Source\)\s*=\s*\zs[^;]*')
+        let dict['host'] = matchstr(dict['host'],'\c\&lt;\%(Server\|Data Source\)\s*=\s*\zs[^;]*')
       endif
+      call filter(dict,'v:val != &quot;&quot;')
     endif
+    let cache[a:environment] = dict
   endif
-  if exists(&quot;s:dbext_type_&quot;.rv)
-    silent! let b:dbext_type    = s:dbext_type_{rv}
-    silent! let b:dbext_profile = s:dbext_profile_{rv}
-    silent! let b:dbext_bin     = s:dbext_bin_{rv}
-    silent! let b:dbext_user    = s:dbext_user_{rv}
-    silent! let b:dbext_passwd  = s:dbext_passwd_{rv}
-    silent! let b:dbext_dbname  = s:dbext_dbname_{rv}
-    silent! let b:dbext_host    = s:dbext_host_{rv}
-    silent! let b:dbext_port    = s:dbext_port_{rv}
-    silent! let b:dbext_dsnname = s:dbext_dsnname_{rv}
-    silent! let b:dbext_extra   = s:dbext_extra_{rv}
-    silent! let b:dbext_integratedlogin = s:dbext_integratedlogin_{rv}
-    if b:dbext_type == 'PGSQL'
-      let $PGPASSWORD = b:dbext_passwd
-    elseif exists('$PGPASSWORD')
-      let $PGPASSWORD = ''
-    endif
+  return cache[a:environment]
+endfunction
+
+function! s:BufDatabase(...)
+  if exists(&quot;s:lock_database&quot;) || !exists('g:loaded_dbext') || !exists('b:rails_root')
+    return
+  endif
+  let self = rails#app()
+  let s:lock_database = 1
+  if (a:0 &amp;&amp; a:1 &gt; 1)
+    call self.cache.clear('dbext_settings')
+  endif
+  if (a:0 &gt; 1 &amp;&amp; a:2 != '')
+    let env = a:2
+  else
+    let env = s:environment()
+  endif
+  if (!self.cache.has('dbext_settings') || !has_key(self.cache.get('dbext_settings'),env)) &amp;&amp; (g:rails_dbext + (a:0 ? a:1 : 0)) &lt;= 0
+    unlet! s:lock_database
+    return
+  endif
+  let dict = self.dbext_settings(env)
+  for key in ['type', 'profile', 'bin', 'user', 'passwd', 'dbname', 'host', 'port', 'dsnname', 'extra', 'integratedlogin']
+    let b:dbext_{key} = get(dict,key,'')
+  endfor
+  if b:dbext_type == 'PGSQL'
+    let $PGPASSWORD = b:dbext_passwd
+  elseif exists('$PGPASSWORD')
+    let $PGPASSWORD = ''
   endif
   if a:0 &gt;= 3 &amp;&amp; a:3 &amp;&amp; exists(&quot;:Create&quot;)
     if exists(&quot;b:dbext_dbname&quot;) &amp;&amp; exists(&quot;b:dbext_type&quot;) &amp;&amp; b:dbext_type !~? 'sqlite'
@@ -3724,11 +3776,11 @@ function! s:BufDatabase(...)
   unlet! s:lock_database
 endfunction
 
+call s:add_methods('app', ['dbext_settings'])
+
 &quot; }}}1
 &quot; Abbreviations {{{1
 
-&quot; Depends: s:sub, s:gsub, s:string, s:linepeak, s:error
-
 function! s:selectiveexpand(pat,good,default,...)
   if a:0 &gt; 0
     let nd = a:1
@@ -3748,16 +3800,16 @@ function! s:selectiveexpand(pat,good,default,...)
   endif
 endfunction
 
-function! s:TheMagicC()
+function! s:TheCWord()
   let l = s:linepeak()
-  if l =~ '\&lt;find\s*\((\|:first,\|:all,\)' || l =~ '\&lt;paginate\&gt;'
+  if l =~ '\&lt;\%(find\|first\|last\|all\|paginate\)\&gt;'
     return s:selectiveexpand('..',':conditions =&gt; ',':c')
-  elseif l =~ '\&lt;render\s*(\=\s*:partial\s\*=&gt;\s*'
+  elseif l =~ '\&lt;render\s*(\=\s*:partial\s*=&gt;\s*'
     return s:selectiveexpand('..',':collection =&gt; ',':c')
-  elseif RailsFileType() =~ '^model\&gt;'
-    return s:selectiveexpand('..',':conditions =&gt; ',':c')
-  else
+  elseif l =~ '\&lt;\%(url_for\|link_to\|form_tag\)\&gt;' || l =~ ':url\s*=&gt;\s*{\s*'
     return s:selectiveexpand('..',':controller =&gt; ',':c')
+  else
+    return s:selectiveexpand('..',':conditions =&gt; ',':c')
   endif
 endfunction
 
@@ -3765,9 +3817,9 @@ function! s:AddSelectiveExpand(abbr,pat,expn,...)
   let expn  = s:gsub(s:gsub(a:expn        ,'[\&quot;|]','\\&amp;'),'\&lt;','\\&lt;Lt&gt;')
   let expn2 = s:gsub(s:gsub(a:0 ? a:1 : '','[\&quot;|]','\\&amp;'),'\&lt;','\\&lt;Lt&gt;')
   if a:0
-    exe &quot;inoreabbrev &lt;buffer&gt; &lt;silent&gt; &quot;.a:abbr.&quot; &lt;C-R&gt;=&lt;SID&gt;selectiveexpand(&quot;.s:string(a:pat).&quot;,\&quot;&quot;.expn.&quot;\&quot;,&quot;.s:string(a:abbr).&quot;,\&quot;&quot;.expn2.&quot;\&quot;)&lt;CR&gt;&quot;
+    exe &quot;inoreabbrev &lt;buffer&gt; &lt;silent&gt; &quot;.a:abbr.&quot; &lt;C-R&gt;=&lt;SID&gt;selectiveexpand(&quot;.string(a:pat).&quot;,\&quot;&quot;.expn.&quot;\&quot;,&quot;.string(a:abbr).&quot;,\&quot;&quot;.expn2.&quot;\&quot;)&lt;CR&gt;&quot;
   else
-    exe &quot;inoreabbrev &lt;buffer&gt; &lt;silent&gt; &quot;.a:abbr.&quot; &lt;C-R&gt;=&lt;SID&gt;selectiveexpand(&quot;.s:string(a:pat).&quot;,\&quot;&quot;.expn.&quot;\&quot;,&quot;.s:string(a:abbr).&quot;)&lt;CR&gt;&quot;
+    exe &quot;inoreabbrev &lt;buffer&gt; &lt;silent&gt; &quot;.a:abbr.&quot; &lt;C-R&gt;=&lt;SID&gt;selectiveexpand(&quot;.string(a:pat).&quot;,\&quot;&quot;.expn.&quot;\&quot;,&quot;.string(a:abbr).&quot;)&lt;CR&gt;&quot;
   endif
 endfunction
 
@@ -3780,7 +3832,7 @@ function! s:AddBracketExpand(abbr,expn)
 endfunction
 
 function! s:AddColonExpand(abbr,expn)
-  call s:AddSelectiveExpand(a:abbr,':',a:expn)
+  call s:AddSelectiveExpand(a:abbr,'[:.]',a:expn)
 endfunction
 
 function! s:AddParenExpand(abbr,expn,...)
@@ -3821,8 +3873,6 @@ function! s:BufAbbreviations()
       Rabbrev taiw  time_ago_in_words
     endif
     if t =~ '^controller\&gt;'
-      &quot;call s:AddSelectiveExpand('rn','[,\r]','render :nothing =&gt; true')
-      &quot;let b:rails_abbreviations = b:rails_abbreviations . &quot;rn\trender :nothing =&gt; true\n&quot;
       Rabbrev re(  redirect_to
       Rabbrev rea( redirect_to :action\ =&gt;\ 
       Rabbrev rec( redirect_to :controller\ =&gt;\ 
@@ -3849,35 +3899,24 @@ function! s:BufAbbreviations()
       Rabbrev mac(  add_column
       Rabbrev mrnc( rename_column
       Rabbrev mrc(  remove_column
-      Rabbrev mct( create_table
-      &quot;Rabbrev mct   create_table\ :\ do\ &lt;Bar&gt;t&lt;Bar&gt;&lt;CR&gt;end&lt;Esc&gt;k$6hi
+      Rabbrev mct(  create_table
+      Rabbrev mcht( change_table
       Rabbrev mrnt( rename_table
       Rabbrev mdt(  drop_table
       Rabbrev mcc(  t.column
     endif
     if t =~ '^test\&gt;'
-      &quot;Rabbrev ae(   assert_equal
       Rabbrev ase(  assert_equal
-      &quot;Rabbrev ako(  assert_kind_of
       Rabbrev asko( assert_kind_of
-      &quot;Rabbrev ann(  assert_not_nil
       Rabbrev asnn( assert_not_nil
-      &quot;Rabbrev ar(   assert_raise
       Rabbrev asr(  assert_raise
-      &quot;Rabbrev are(  assert_response
       Rabbrev asre( assert_response
       Rabbrev art(  assert_redirected_to
     endif
     Rabbrev :a    :action\ =&gt;\ 
-    inoreabbrev &lt;buffer&gt; &lt;silent&gt; :c &lt;C-R&gt;=&lt;SID&gt;TheMagicC()&lt;CR&gt;
-    &quot; Lie a little
-    if t =~ '^view\&gt;'
-      let b:rails_abbreviations = b:rails_abbreviations . &quot;:c\t:collection =&gt; \n&quot;
-    elseif s:controller() != ''
-      let b:rails_abbreviations = b:rails_abbreviations . &quot;:c\t:controller =&gt; \n&quot;
-    else
-      let b:rails_abbreviations = b:rails_abbreviations . &quot;:c\t:conditions =&gt; \n&quot;
-    endif
+    &quot; hax
+    Rabbrev :c    :co________\ =&gt;\ 
+    inoreabbrev &lt;buffer&gt; &lt;silent&gt; :c &lt;C-R&gt;=&lt;SID&gt;TheCWord()&lt;CR&gt;
     Rabbrev :i    :id\ =&gt;\ 
     Rabbrev :o    :object\ =&gt;\ 
     Rabbrev :p    :partial\ =&gt;\ 
@@ -3899,31 +3938,39 @@ endfunction
 
 function! s:Abbrev(bang,...) abort
   if !exists(&quot;b:rails_abbreviations&quot;)
-    let b:rails_abbreviations = &quot;\n&quot;
+    let b:rails_abbreviations = {}
   endif
   if a:0 &gt; 3 || (a:bang &amp;&amp; (a:0 != 1))
     return s:error(&quot;Rabbrev: invalid arguments&quot;)
   endif
-  if a:bang
-    return s:unabbrev(a:1)
-  endif
   if a:0 == 0
-    echo s:sub(b:rails_abbreviations,'^\n','')
+    for key in sort(keys(b:rails_abbreviations))
+      echo key . join(b:rails_abbreviations[key],&quot;\t&quot;)
+    endfor
     return
   endif
   let lhs = a:1
+  let root = s:sub(lhs,'%(::|\(|\[)$','')
+  if a:bang
+    if has_key(b:rails_abbreviations,root)
+      call remove(b:rails_abbreviations,root)
+    endif
+    exe &quot;iunabbrev &lt;buffer&gt; &quot;.root
+    return
+  endif
   if a:0 &gt; 3 || a:0 &lt; 2
     return s:error(&quot;Rabbrev: invalid arguments&quot;)
   endif
   let rhs = a:2
-  call s:unabbrev(lhs,1)
+  if has_key(b:rails_abbreviations,root)
+    call remove(b:rails_abbreviations,root)
+  endif
   if lhs =~ '($'
-    let b:rails_abbreviations = b:rails_abbreviations . lhs . &quot;\t&quot; . rhs . &quot;&quot; . (a:0 &gt; 2 ? &quot;\t&quot;.a:3 : &quot;&quot;). &quot;\n&quot;
-    let llhs = s:sub(lhs,'\($','')
+    let b:rails_abbreviations[root] = [&quot;(&quot;, rhs . (a:0 &gt; 2 ? &quot;\t&quot;.a:3 : &quot;&quot;)]
     if a:0 &gt; 2
-      call s:AddParenExpand(llhs,rhs,a:3)
+      call s:AddParenExpand(root,rhs,a:3)
     else
-      call s:AddParenExpand(llhs,rhs)
+      call s:AddParenExpand(root,rhs)
     endif
     return
   endif
@@ -3931,42 +3978,24 @@ function! s:Abbrev(bang,...) abort
     return s:error(&quot;Rabbrev: invalid arguments&quot;)
   endif
   if lhs =~ ':$'
-    let llhs = s:sub(lhs,':=:$','')
-    call s:AddColonExpand(llhs,rhs)
+    call s:AddColonExpand(root,rhs)
   elseif lhs =~ '\[$'
-    let llhs = s:sub(lhs,'\[$','')
-    call s:AddBracketExpand(llhs,rhs)
+    call s:AddBracketExpand(root,rhs)
   elseif lhs =~ '\w$'
     call s:AddTabExpand(lhs,rhs)
   else
     return s:error(&quot;Rabbrev: unimplemented&quot;)
   endif
-  let b:rails_abbreviations = b:rails_abbreviations . lhs . &quot;\t&quot; . rhs . &quot;\n&quot;
-endfunction
-
-function! s:unabbrev(abbr,...)
-  let abbr = s:sub(a:abbr,'%(::|\(|\[)$','')
-  let pat  = s:sub(abbr,'\\','\\\\')
-  if !exists(&quot;b:rails_abbreviations&quot;)
-    let b:rails_abbreviations = &quot;\n&quot;
-  endif
-  let b:rails_abbreviations = substitute(b:rails_abbreviations,'\V\C\n'.pat.'\(\t\|::\t\|(\t\|[\t\)\.\{-\}\n','\n','')
-  if a:0 == 0 || a:1 == 0
-    exe &quot;iunabbrev &lt;buffer&gt; &quot;.abbr
-  endif
+  let b:rails_abbreviations[root] = [matchstr(lhs,'\W*$'),rhs]
 endfunction
 
 &quot; }}}1
 &quot; Settings {{{1
 
-&quot; Depends: s:error, s:sub, s:sname, s:escvar, s:lastmethod, s:environment, s:gsub, s:lastmethodlib, s:gsub
-
 function! s:Set(bang,...)
   let c = 1
   let defscope = ''
-  while c &lt;= a:0
-    let arg = a:{c}
-    let c = c + 1
+  for arg in a:000
     if arg =~? '^&lt;[abgl]\=&gt;$'
       let defscope = (matchstr(arg,'&lt;\zs.*\ze&gt;'))
     elseif arg !~ '='
@@ -3974,7 +4003,7 @@ function! s:Set(bang,...)
         let arg = defscope.':'.opt
       endif
       let val = s:getopt(arg)
-      if val == '' &amp;&amp; s:opts() !~ '\&lt;'.arg.'\n'
+      if val == '' &amp;&amp; !has_key(s:opts(),arg)
         call s:error(&quot;No such rails.vim option: &quot;.arg)
       else
         echo arg.&quot;=&quot;.val
@@ -3987,10 +4016,11 @@ function! s:Set(bang,...)
       endif
       call s:setopt(opt,val)
     endif
-  endwhile
+  endfor
 endfunction
 
 function! s:getopt(opt,...)
+  let app = rails#app()
   let opt = a:opt
   if a:0
     let scope = a:1
@@ -4000,28 +4030,32 @@ function! s:getopt(opt,...)
   else
     let scope = 'abgl'
   endif
+  let lnum = a:0 &gt; 1 ? a:2 : line('.')
   if scope =~ 'l' &amp;&amp; &amp;filetype != 'ruby'
     let scope = s:sub(scope,'l','b')
   endif
   if scope =~ 'l'
-    call s:LocalModelines()
+    call s:LocalModelines(lnum)
   endif
-  let opt = s:sub(opt,'&lt;%(rake|rake_task|rake_target)$','task')
+  let var = s:sname().'_'.opt
+  let lastmethod = s:lastmethod(lnum)
+  if lastmethod == '' | let lastmethod = ' ' | endif
   &quot; Get buffer option
-  if scope =~ 'l' &amp;&amp; exists(&quot;b:_&quot;.s:sname().&quot;_&quot;.s:escvar(s:lastmethod()).&quot;_&quot;.opt)
-    return b:_{s:sname()}_{s:escvar(s:lastmethod())}_{opt}
-  elseif exists(&quot;b:&quot;.s:sname().&quot;_&quot;.opt) &amp;&amp; (scope =~ 'b' || (scope =~ 'l' &amp;&amp; s:lastmethod() == ''))
-    return b:{s:sname()}_{opt}
-  elseif scope =~ 'a' &amp;&amp; exists(&quot;s:_&quot;.s:rv().&quot;_&quot;.s:environment().&quot;_&quot;.opt)
-    return s:_{s:rv()}_{s:environment()}_{opt}
+  if scope =~ 'l' &amp;&amp; exists('b:_'.var) &amp;&amp; has_key(b:_{var},lastmethod)
+    return b:_{var}[lastmethod]
+  elseif exists('b:'.var) &amp;&amp; (scope =~ 'b' || (scope =~ 'l' &amp;&amp; lastmethod == ' '))
+    return b:{var}
+  elseif scope =~ 'a' &amp;&amp; has_key(app,'options') &amp;&amp; has_key(app.options,opt)
+    return app.options[opt]
   elseif scope =~ 'g' &amp;&amp; exists(&quot;g:&quot;.s:sname().&quot;_&quot;.opt)
-    return g:{s:sname()}_{opt}
+    return g:{var}
   else
     return &quot;&quot;
   endif
 endfunction
 
 function! s:setopt(opt,val)
+  let app = rails#app()
   if a:opt =~? '[abgl]:'
     let scope = matchstr(a:opt,'^\w')
     let opt = s:sub(a:opt,'^\w:','')
@@ -4029,48 +4063,48 @@ function! s:setopt(opt,val)
     let scope = ''
     let opt = a:opt
   endif
-  let opt = s:sub(opt,'&lt;%(rake|rake_task|rake_target)$','task')
-  let defscope = matchstr(s:opts(),'\n\zs\w\ze:'.opt,'\n')
-  if defscope == ''
-    let defscope = 'a'
-  endif
+  let defscope = get(s:opts(),opt,'a')
   if scope == ''
     let scope = defscope
   endif
-  if &amp;filetype == 'ruby' &amp;&amp; (scope == 'B' || scope == 'l')
+  if &amp;filetype != 'ruby' &amp;&amp; (scope ==# 'B' || scope ==# 'l')
     let scope = 'b'
   endif
+  let var = s:sname().'_'.opt
   if opt =~ '\W'
     return s:error(&quot;Invalid option &quot;.a:opt)
-  elseif scope =~? 'a'
-    let s:_{s:rv()}_{s:environment()}_{opt} = a:val
-  elseif scope == 'B' &amp;&amp; defscope == 'l'
-    let b:_{s:sname()}_{s:escvar('')}_{opt} = a:val
+  elseif scope ==# 'B' &amp;&amp; defscope == 'l'
+    if !exists('b:_'.var) | let b:_{var} = {} | endif
+    let b:_{var}[' '] = a:val
   elseif scope =~? 'b'
-    let b:{s:sname()}_{opt} = a:val
+    let b:{var} = a:val
+  elseif scope =~? 'a'
+    if !has_key(app,'options') | let app.options = {} | endif
+    let app.options[opt] = a:val
   elseif scope =~? 'g'
-    let g:{s:sname()}_{opt} = a:val
+    let g:{var} = a:val
   elseif scope =~? 'l'
-    let b:_{s:sname()}_{s:escvar(s:lastmethod())}_{opt} = a:val
+    if !exists('b:_'.var) | let b:_{var} = {} | endif
+    let lastmethod = s:lastmethod(lnum)
+    let b:_{var}[lastmethod == '' ? ' ' : lastmethod] = a:val
   else
     return s:error(&quot;Invalid scope for &quot;.a:opt)
   endif
 endfunction
 
 function! s:opts()
-  return &quot;\nb:alternate\nb:controller\na:gnu_screen\nb:model\nl:preview\nb:task\nl:related\na:root_url\na:ruby_fork_port\n&quot;
+  return {'alternate': 'b', 'controller': 'b', 'gnu_screen': 'a', 'model': 'b', 'preview': 'l', 'task': 'b', 'related': 'l', 'root_url': 'a'}
 endfunction
 
-function! s:SetComplete(A,L,P)
+function! s:Complete_set(A,L,P)
   if a:A =~ '='
     let opt = matchstr(a:A,'[^=]*')
-    return opt.&quot;=&quot;.s:getopt(opt)
+    return [opt.&quot;=&quot;.s:getopt(opt)]
   else
     let extra = matchstr(a:A,'^[abgl]:')
-    let opts = s:gsub(s:sub(s:gsub(s:opts(),'\n\w:','\n'.extra),'^\n',''),'\n','=\n')
-    return opts
+    return filter(sort(map(keys(s:opts()),'extra.v:val')),'s:startswith(v:val,a:A)')
   endif
-  return &quot;&quot;
+  return []
 endfunction
 
 function! s:BufModelines()
@@ -4090,15 +4124,15 @@ function! s:BufModelines()
     endif
     let mat    = matchstr(lines,'\C\&lt;Rset'.pat,matend)
     let matend = matchend(lines,'\C\&lt;Rset'.pat,matend)
-    let cnt = cnt + 1
+    let cnt += 1
   endwhile
 endfunction
 
-function! s:LocalModelines()
+function! s:LocalModelines(lnum)
   if !g:rails_modelines
     return
   endif
-  let lbeg = s:lastmethodline()
+  let lbeg = s:lastmethodline(a:lnum)
   let lend = s:endof(lbeg)
   if lbeg == 0 || lend == 0
     return
@@ -4106,8 +4140,8 @@ function! s:LocalModelines()
   let lines = &quot;\n&quot;
   let lnum = lbeg
   while lnum &lt; lend &amp;&amp; lnum &lt; lbeg + 5
-    let lines = lines . getline(lnum) . &quot;\n&quot;
-    let lnum = lnum + 1
+    let lines .= getline(lnum) . &quot;\n&quot;
+    let lnum += 1
   endwhile
   let pat = '\s\+\zs.\{-\}\ze\%(\n\|\s\s\|#{\@!\|%&gt;\|--&gt;\|$\)'
   let cnt = 1
@@ -4121,40 +4155,43 @@ function! s:LocalModelines()
     endif
     let mat    = matchstr(lines,'\C\&lt;rset'.pat,matend)
     let matend = matchend(lines,'\C\&lt;rset'.pat,matend)
-    let cnt = cnt + 1
+    let cnt += 1
   endwhile
 endfunction
 
 &quot; }}}1
 &quot; Detection {{{1
 
-function! s:callback(file)
-  if RailsRoot() != &quot;&quot;
-    let var = &quot;callback_&quot;.s:rv().&quot;_&quot;.s:escvar(a:file)
-    if !exists(&quot;s:&quot;.var) || exists(&quot;b:rails_refresh&quot;)
-      let s:{var} = s:hasfile(a:file)
-    endif
-    if s:{var}
-      if exists(&quot;:sandbox&quot;)
-        sandbox source `=RailsRoot().'/'.a:file`
-      elseif g:rails_modelines
-        source `=RailsRoot().'/'.a:file`
-      endif
-    endif
+function! s:app_source_callback(file) dict
+  if self.cache.needs('existence')
+    call self.cache.set('existence',{})
+  endif
+  let cache = self.cache.get('existence')
+  if !has_key(cache,a:file)
+    let cache[a:file] = self.has_file(a:file)
+  endif
+  if cache[a:file]
+    sandbox source `=self.path(a:file)`
   endif
 endfunction
 
+call s:add_methods('app',['source_callback'])
+
 function! RailsBufInit(path)
   let cpo_save = &amp;cpo
   set cpo&amp;vim
   let firsttime = !(exists(&quot;b:rails_root&quot;) &amp;&amp; b:rails_root == a:path)
   let b:rails_root = a:path
+  if !has_key(s:apps,a:path)
+    let s:apps[a:path] = deepcopy(s:app_prototype)
+    let s:apps[a:path].root = a:path
+  endif
+  let app = s:apps[a:path]
   &quot; Apparently RailsFileType() can be slow if the underlying file system is
   &quot; slow (even though it doesn't really do anything IO related).  This caching
   &quot; is a temporary hack; if it doesn't cause problems it should probably be
   &quot; refactored.
-  unlet! b:rails_cached_file_type
-  let b:rails_cached_file_type = RailsFileType()
+  let b:rails_cached_file_type = app.calculate_file_type(RailsFilePath())
   if g:rails_history_size &gt; 0
     if !exists(&quot;g:RAILS_HISTORY&quot;)
       let g:RAILS_HISTORY = &quot;&quot;
@@ -4172,7 +4209,7 @@ function! RailsBufInit(path)
     let g:RAILS_HISTORY = path.&quot;\n&quot;.g:RAILS_HISTORY
     let g:RAILS_HISTORY = s:sub(g:RAILS_HISTORY,'%(.{-}\n){,'.g:rails_history_size.'}\zs.*','')
   endif
-  call s:callback(&quot;config/syntax.vim&quot;)
+  call app.source_callback(&quot;config/syntax.vim&quot;)
   if &amp;ft == &quot;mason&quot;
     setlocal filetype=eruby
   elseif &amp;ft =~ '^\%(conf\|ruby\)\=$' &amp;&amp; expand(&quot;%:e&quot;) =~ '^\%(rjs\|rxml\|builder\|rake\|mab\)$'
@@ -4210,7 +4247,9 @@ function! RailsBufInit(path)
   call s:BufSettings()
   call s:BufCommands()
   call s:BufAbbreviations()
-  call s:BufDatabase()
+  if exists(&quot;g:loaded_dbext&quot;) &amp;&amp; g:loaded_dbext &lt; 800
+    call s:BufDatabase()
+  endif
   &quot; snippetsEmu.vim
   if exists('g:loaded_snippet')
     silent! runtime! ftplugin/rails_snippets.vim
@@ -4231,32 +4270,34 @@ function! RailsBufInit(path)
   if f != ''
     exe &quot;silent doautocmd User Rails&quot;.f
   endif
-  call s:callback(&quot;config/rails.vim&quot;)
+  call app.source_callback(&quot;config/rails.vim&quot;)
   call s:BufModelines()
   call s:BufMappings()
-  &quot;unlet! b:rails_cached_file_type
   let &amp;cpo = cpo_save
   return b:rails_root
 endfunction
 
 function! s:SetBasePath()
-  let rp = s:gsub(RailsRoot(),'[ ,]','\\&amp;')
-  let t = RailsFileType()
-  let oldpath = s:sub(&amp;l:path,'^\.,','')
-  if stridx(oldpath,rp) == 2
-    let oldpath = ''
+  if rails#app().path() =~ '://'
+    return
   endif
-  let &amp;l:path = '.,'.rp.&quot;,&quot;.rp.&quot;/app/controllers,&quot;.rp.&quot;/app,&quot;.rp.&quot;/app/models,&quot;.rp.&quot;/app/helpers,&quot;.rp.&quot;/config,&quot;.rp.&quot;/lib,&quot;.rp.&quot;/vendor,&quot;.rp.&quot;/vendor/plugins/*/lib,&quot;.rp.&quot;/test/unit,&quot;.rp.&quot;/test/functional,&quot;.rp.&quot;/test/integration,&quot;.rp.&quot;/app/apis,&quot;.rp.&quot;/app/services,&quot;.rp.&quot;/test,&quot;.&quot;/vendor/plugins/*/test,&quot;.rp.&quot;/vendor/rails/*/lib,&quot;.rp.&quot;/vendor/rails/*/test,&quot;.rp.&quot;/spec,&quot;.rp.&quot;/spec/*,&quot;
+  let transformed_path = s:pathsplit(s:pathjoin([rails#app().path()]))[0]
+  let old_path = s:pathsplit(s:sub(&amp;l:path,'^\.,=',''))
+  call filter(old_path,'!s:startswith(v:val,transformed_path)')
+
+  let path = ['app', 'app/models', 'app/controllers', 'app/helpers', 'config', 'lib', 'app/views']
   if s:controller() != ''
-    let &amp;l:path = &amp;l:path . rp . '/app/views/' . s:controller() . ',' . rp . '/app/views,' . rp . '/public,'
+    let path += ['app/views/'.s:controller(), 'public']
   endif
-  if t =~ '^log\&gt;'
-    let &amp;l:path = &amp;l:path . rp . '/app/views,'
+  if rails#app().test_suites('test')
+    let path += ['test', 'test/unit', 'test/functional', 'test/integration']
   endif
-  if &amp;l:path =~ '://'
-    let &amp;l:path = &quot;.,&quot;
+  if rails#app().test_suites('spec')
+    let path += ['spec', 'spec/models', 'spec/controllers', 'spec/helpers', 'spec/views', 'spec/lib']
   endif
-  let &amp;l:path = &amp;l:path . oldpath
+  let path += ['app/*', 'vendor', 'vendor/plugins/*/lib', 'vendor/plugins/*/test', 'vendor/rails/*/lib', 'vendor/rails/*/test']
+  call map(path,'rails#app().path(v:val)')
+  let &amp;l:path = s:pathjoin('.',rails#app().path(),path,old_path)
 endfunction
 
 function! s:BufSettings()
@@ -4264,11 +4305,11 @@ function! s:BufSettings()
     return ''
   endif
   call s:SetBasePath()
-  let rp = s:gsub(RailsRoot(),'[ ,]','\\&amp;')
+  let rp = s:gsub(rails#app().path(),'[ ,]','\\&amp;')
   let &amp;l:errorformat = s:efm
   setlocal makeprg=rake
   if stridx(&amp;tags,rp) == -1
-    let &amp;l:tags = &amp;tags . &quot;,&quot; . rp . &quot;/tags,&quot; . rp . &quot;/.tags&quot;
+    let &amp;l:tags = rp . &quot;/tmp/tags,&quot; . &amp;tags . &quot;,&quot; . rp . &quot;/tags&quot;
   endif
   if has(&quot;gui_win32&quot;) || has(&quot;gui_running&quot;)
     let code      = '*.rb;*.rake;Rakefile'
@@ -4287,7 +4328,6 @@ function! s:BufSettings()
   let &amp;l:suffixesadd=&quot;.rb,.&quot;.s:gsub(s:view_types,',',',.').&quot;,.css,.js,.yml,.csv,.rake,.sql,.html,.xml&quot;
   if &amp;ft =~ '^\%(e\=ruby\|[yh]aml\|javascript\|css\|sass\)$'
     setlocal sw=2 sts=2 et
-    &quot;set include=\\&lt;\\zsAct\\f*::Base\\ze\\&gt;\\\|^\\s*\\(require\\\|load\\)\\s\\+['\&quot;]\\zs\\f\\+\\ze
     if exists('+completefunc')
       if &amp;completefunc == ''
         set completefunc=syntaxcomplete#Complete
@@ -4354,13 +4394,17 @@ augroup railsPluginAuto
   autocmd User BufEnterRails call s:RefreshBuffer()
   autocmd User BufEnterRails call s:resetomnicomplete()
   autocmd User BufEnterRails call s:BufDatabase(-1)
-  autocmd BufWritePost */config/database.yml unlet! s:dbext_type_{s:rv()} &quot; Force reload
-  autocmd BufWritePost */test/test_helper.rb call s:cacheclear(&quot;user_asserts&quot;)
-  autocmd BufWritePost */config/routes.rb    call s:cacheclear(&quot;named_routes&quot;)
+  autocmd User dbextPreConnection call s:BufDatabase(1)
+  autocmd BufWritePost */config/database.yml      call rails#cache_clear(&quot;dbext_settings&quot;)
+  autocmd BufWritePost */test/test_helper.rb      call rails#cache_clear(&quot;user_assertions&quot;)
+  autocmd BufWritePost */config/routes.rb         call rails#cache_clear(&quot;named_routes&quot;)
+  autocmd BufWritePost */config/environments/*.rb call rails#cache_clear(&quot;environments&quot;)
+  autocmd BufWritePost */tasks/**.rake            call rails#cache_clear(&quot;rake_tasks&quot;)
+  autocmd BufWritePost */generators/**            call rails#cache_clear(&quot;generators&quot;)
   autocmd FileType * if exists(&quot;b:rails_root&quot;) | call s:BufSettings() | endif
   autocmd Syntax ruby,eruby,yaml,haml,javascript,railslog if exists(&quot;b:rails_root&quot;) | call s:BufSyntax() | endif
-  silent! autocmd QuickFixCmdPre  make* call s:QuickFixCmdPre()
-  silent! autocmd QuickFixCmdPost make* call s:QuickFixCmdPost()
+  autocmd QuickFixCmdPre  make* call s:push_chdir()
+  autocmd QuickFixCmdPost make* call s:pop_command()
 augroup END
 
 &quot; }}}1
@@ -4371,6 +4415,10 @@ let s:sid = s:sub(maparg(&quot;&lt;SID&gt;xx&quot;),'xx$','')
 unmap &lt;SID&gt;xx
 let s:file = expand('&lt;sfile&gt;:p')
 
+if !exists('s:apps')
+  let s:apps = {}
+endif
+
 &quot; }}}1
 
 let &amp;cpo = s:cpo_save</diff>
      <filename>autoload/rails.vim</filename>
    </modified>
    <modified>
      <diff>@@ -21,22 +21,21 @@ 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 filesystem menu...............|NERDTreeFilesysMenu|
+        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 filesystem menu...............|NERDTreeFilesysMenu|
     3.Options.................................|NERDTreeOptions|
-        3.1 Option summary....................|NERDTreeOptionSummary|
-        3.2 Option details....................|NERDTreeOptionDetails|
-    4.Public functions........................|NERDTreePublicFunctions|
-    5.TODO list...............................|NERDTreeTodo|
-    6.The Author..............................|NERDTreeAuthor|
-    7.Changelog...............................|NERDTreeChangelog|
-    8.Credits.................................|NERDTreeCredits|
-    9.License.................................|NERDTreeLicense|
+        3.1.Option summary....................|NERDTreeOptionSummary|
+        3.2.Option details....................|NERDTreeOptionDetails|
+    4.Hacking the NERD tree...................|NERDTreeHacking|
+    5.About...................................|NERDTreeAbout|
+    6.Changelog...............................|NERDTreeChangelog|
+    7.Credits.................................|NERDTreeCredits|
+    8.License.................................|NERDTreeLicense|
 
 ==============================================================================
 1. Intro                                                            *NERDTree*
@@ -65,7 +64,7 @@ The following features and functionality are provided by the NERD tree:
         * ...
     * Directories and files can be bookmarked.
     * Most NERD tree navigation can also be done with the mouse
-    * Dynamic customisation of tree content
+    * 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 &quot;turned off&quot; so that only directories are displayed
@@ -81,9 +80,13 @@ The following features and functionality are provided by the NERD tree:
           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
+      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
+    * 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
 
 ==============================================================================
 2. Functionality provided                              *NERDTreeFunctionality*
@@ -109,7 +112,15 @@ The following features and functionality are provided by the NERD tree:
     again.  If no NERD tree exists for this tab then this command acts the
     same as the |:NERDTree| command.
 
-:NERDTreeClose
+: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.
 
 ------------------------------------------------------------------------------
@@ -122,7 +133,7 @@ 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
+|'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|
 
@@ -160,7 +171,7 @@ Note that the following commands are only available in the NERD tree buffer.
     Remove all bookmarks.
 
 :ReadBookmarks
-    Re-read the bookmarks in the |NERDTreeBookmarksFile|.
+    Re-read the bookmarks in the |'NERDTreeBookmarksFile'|.
 
 See also |:NERDTree| and |:NERDTreeFromBookmark|.
 
@@ -171,7 +182,7 @@ 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
+|'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:
@@ -190,16 +201,18 @@ 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|
-&lt;tab&gt;...Open selected file in a split window.....................|NERDTree-tab|
-g&lt;tab&gt;..Same as &lt;tab&gt;, but leave the cursor on the NERDTree......|NERDTree-gtab|
+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|
 !.......Execute the current file.................................|NERDTree-!|
 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.......Open a netrw for the current dir.........................|NERDTree-e|
+e.......Edit the current dif.....................................|NERDTree-e|
 
 double-click.......same as the |NERDTree-o| map.
-middle-click.......same as |NERDTree-tab| for files, same as
+middle-click.......same as |NERDTree-i| for files, same as
                    |NERDTree-e| for dirs.
 
 D.......Delete the current bookmark .............................|NERDTree-D|
@@ -219,7 +232,7 @@ R.......Recursively refresh the current root.....................|NERDTree-R|
 m.......Display the filesystem menu..............................|NERDTree-m|
 cd......Change the CWD to the dir of the selected node...........|NERDTree-cd|
 
-H.......Toggle whether hidden files displayed....................|NERDTree-H|
+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|
@@ -278,8 +291,8 @@ Applies to: files and directories.
 The same as |NERDTree-t| except that the focus is kept in the current tab.
 
 ------------------------------------------------------------------------------
-                                                                *NERDTree-tab*
-Default key: &lt;tab&gt;
+                                                                  *NERDTree-i*
+Default key: i
 Map option: NERDTreeMapOpenSplit
 Applies to: files.
 
@@ -287,15 +300,35 @@ Opens the selected file in a new split window and puts the cursor in the new
 window.
 
 ------------------------------------------------------------------------------
-                                                               *NERDTree-gtab*
-Default key: g&lt;tab&gt;
+                                                                 *NERDTree-gi*
+Default key: gi
 Map option: None
 Applies to: files.
 
-The same as |NERDTree-tab| except that the cursor is not moved.
+The same as |NERDTree-i| except that the cursor is not moved.
 
 The key combo for this mapping is always &quot;g&quot; + NERDTreeMapOpenSplit (see
-|NERDTree-tab|).
+|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 &quot;g&quot; + NERDTreeMapOpenVSplit (see
+|NERDTree-s|).
 
 ------------------------------------------------------------------------------
                                                                   *NERDTree-!*
@@ -314,8 +347,8 @@ 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
+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.
 
 ------------------------------------------------------------------------------
@@ -342,7 +375,9 @@ Default key: e
 Map option: NERDTreeMapOpenExpl
 Applies to: files and directories.
 
-Opens a netrw on the selected directory, or the selected file's directory.
+|: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*
@@ -463,13 +498,12 @@ Applies to: files and directories.
 Display the filesystem menu. See |NERDTreeFilesysMenu| for details.
 
 ------------------------------------------------------------------------------
-                                                                  *NERDTree-H*
-Default key: H
+                                                                  *NERDTree-I*
+Default key: I
 Map option: NERDTreeMapToggleHidden
 Applies to: no restrictions.
 
-Toggles whether hidden files are displayed. Hidden files are any
-file/directory that starts with a &quot;.&quot;
+Toggles whether hidden files (i.e. &quot;dot files&quot;) are displayed.
 
 ------------------------------------------------------------------------------
                                                                   *NERDTree-f*
@@ -477,7 +511,7 @@ Default key: f
 Map option: NERDTreeMapToggleFilters
 Applies to: no restrictions.
 
-Toggles whether file filters are used. See |NERDTreeIgnore| for details.
+Toggles whether file filters are used. See |'NERDTreeIgnore'| for details.
 
 ------------------------------------------------------------------------------
                                                                   *NERDTree-F*
@@ -561,53 +595,58 @@ then id be grateful if you'd email me.
 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.
+|'loaded_nerd_tree'|            Turns off the script.
 
-|NERDChristmasTree|             Tells the NERD tree to make itself colourful
+|'NERDChristmasTree'|           Tells the NERD tree to make itself colourful
                                 and pretty.
 
-|NERDTreeAutoCenter|            Controls whether the NERD tree window centers
+|'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.
+|'NERDTreeAutoCenterThreshold'| Controls the sensitivity of autocentering.
 
-|NERDTreeCaseSensitiveSort|     Tells the NERD tree whether to be case
+|'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
+|'NERDTreeChDirMode'|           Tells the NERD tree if/when it should change
                                 vim's current working directory.
 
-|NERDTreeHighlightCursorline|   Tell the NERD tree whether to highlight the
+|'NERDTreeHighlightCursorline'| Tell the NERD tree whether to highlight the
                                 current cursor line.
 
-|NERDTreeIgnore|                Tells the NERD tree which files to ignore.
+|'NERDTreeHijackNetrw'|         Tell the NERD tree whether to replace the netrw
+                                autocommands for exploring local directories.
 
-|NERDTreeBookmarksFile|         Where the bookmarks are stored.
+|'NERDTreeIgnore'|              Tells the NERD tree which files to ignore.
 
-|NERDTreeMouseMode|             Tells the NERD tree how to handle mouse
+|'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.
+|'NERDTreeQuitOnOpen'|          Closes the tree window after opening a file.
 
-|NERDTreeShowBookmarks|         Tells the NERD tree whether to display the
+|'NERDTreeShowBookmarks'|       Tells the NERD tree whether to display the
                                 bookmarks table on startup.
 
-|NERDTreeShowFiles|             Tells the NERD tree whether to display files
+|'NERDTreeShowFiles'|           Tells the NERD tree whether to display files
                                 in the tree on startup.
 
-|NERDTreeShowHidden|            Tells the NERD tree whether to display hidden
+|'NERDTreeShowHidden'|          Tells the NERD tree whether to display hidden
                                 files on startup.
 
-|NERDTreeShowLineNumbers|       Tells the NERD tree whether to display line
+|'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
+|'NERDTreeSortOrder'|           Tell the NERD tree how to sort the nodes in
                                 the tree.
 
-|NERDTreeWinPos|                Tells the script where to put the NERD 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
+|'NERDTreeWinSize'|             Sets the window size when the NERD tree is
                                 opened.
 
 ------------------------------------------------------------------------------
@@ -616,13 +655,13 @@ NERD tree. These options should be set in your vimrc.
 To enable any of the below options you should put the given line in your
 ~/.vimrc
 
-                                                            *loaded_nerd_tree*
+                                                          *'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: &gt;
     let loaded_nerd_tree=1
 &lt;
 ------------------------------------------------------------------------------
-                                                           *NERDChristmasTree*
+                                                         *'NERDChristmasTree'*
 Values: 0 or 1.
 Default: 1.
 
@@ -632,12 +671,12 @@ added to the nerd tree to make it more colourful.
 Set it to 0 for a more vanilla looking tree.
 
 ------------------------------------------------------------------------------
-                                                          *NERDTreeAutoCenter*
+                                                        *'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.
+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|
@@ -646,15 +685,15 @@ i.e. |NERDTree-J| |NERDTree-K| |NERDTree-C-J| |NERDTree-c-K| |NERDTree-p|
 The centering is done with a |zz| operation.
 
 ------------------------------------------------------------------------------
-                                                 *NERDTreeAutoCenterThreshold*
+                                               *'NERDTreeAutoCenterThreshold'*
 Values: Any natural number.
 Default: 3
 
 This option controls the &quot;sensitivity&quot; of the NERD tree auto centering. See
-|NERDTreeAutoCenter| for details.
+|'NERDTreeAutoCenter'| for details.
 
 ------------------------------------------------------------------------------
-                                                   *NERDTreeCaseSensitiveSort*
+                                                 *'NERDTreeCaseSensitiveSort'*
 Values: 0 or 1.
 Default: 0.
 
@@ -675,7 +714,7 @@ account. The above nodes would then be sorted like this: &gt;
     boner.c
 &lt;
 ------------------------------------------------------------------------------
-                                                           *NERDTreeChDirMode*
+                                                         *'NERDTreeChDirMode'*
 
 Values: 0, 1 or 2.
 Default: 0.
@@ -698,7 +737,7 @@ 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*
+                                               *'NERDTreeHighlightCursorline'*
 Values: 0 or 1.
 Default: 1.
 
@@ -706,13 +745,30 @@ If set to 1, the current cursor line in the NERD tree buffer will be
 highlighted. This is done using the |cursorline| option.
 
 ------------------------------------------------------------------------------
-                                                              *NERDTreeIgnore*
+                                                       *'NERDTreeHijackNetrw'*
+Values: 0 or 1.
+Default: 1.
+
+If set to 1, doing a &gt;
+    :edit &lt;some directory&gt;
+&lt;
+will open up a &quot;secondary&quot; 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.
+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: &gt;
     let NERDTreeIgnore=['\.vim$', '\~$']
@@ -728,14 +784,14 @@ The file filters can be turned on and off dynamically with the |NERDTree-f|
 mapping.
 
 ------------------------------------------------------------------------------
-                                                       *NERDTreeBookmarksFile*
+                                                     *'NERDTreeBookmarksFile'*
 Values: a path
 Default: $HOME/.NERDTreeBookmarks
 
 This is where bookmarks are saved. See |NERDTreeBookmarkCommands|.
 
 ------------------------------------------------------------------------------
-                                                           *NERDTreeMouseMode*
+                                                       *'NERDTreeMouseMode'*
 Values: 1, 2 or 3.
 Default: 1.
 
@@ -753,25 +809,26 @@ then (to single click activate it) you must click somewhere in
 'application.rb'.
 
 ------------------------------------------------------------------------------
-                                                          *NERDTreeQuitOnOpen*
+                                                        *'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| or |NERDTree-tab| mappings.
+|NERDTree-o| or |NERDTree-i| mappings.
 
 ------------------------------------------------------------------------------
-                                                       *NERDTreeShowBookmarks*
+                                                     *'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.
+This option can be toggled dynamically, per tree, with the |NERDTree-B|
+mapping.
 
 ------------------------------------------------------------------------------
-                                                           *NERDTreeShowFiles*
+                                                         *'NERDTreeShowFiles'*
 Values: 0 or 1.
 Default: 1.
 
@@ -783,19 +840,19 @@ mapping and is useful for drastically shrinking the tree when you are
 navigating to a different part of the tree.
 
 ------------------------------------------------------------------------------
-                                                          *NERDTreeShowHidden*
+                                                        *'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-H| mapping.  Use one
+can be dynamically toggled, per tree, with the |NERDTree-I| mapping.  Use one
 of the follow lines to set this option: &gt;
     let NERDTreeShowHidden=0
     let NERDTreeShowHidden=1
 &lt;
 
 ------------------------------------------------------------------------------
-                                                     *NERDTreeShowLineNumbers*
+                                                   *'NERDTreeShowLineNumbers'*
 Values: 0 or 1.
 Default: 0.
 
@@ -806,7 +863,7 @@ window.  Use one of the follow lines to set this option: &gt;
 &lt;
 
 ------------------------------------------------------------------------------
-                                                           *NERDTreeSortOrder*
+                                                         *'NERDTreeSortOrder'*
 Values: a list of regular expressions.
 Default: ['\/$', '*', '\.swp$',  '\.bak$', '\~$']
 
@@ -821,8 +878,8 @@ 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.
+If no star is present in 'NERDTreeSortOrder' then one is automatically
+appended to the array.
 
 The regex '\/$' should be used to match directory nodes.
 
@@ -839,30 +896,42 @@ Other examples: &gt;
    backup files will appear last with everything else preceding them.
 
 ------------------------------------------------------------------------------
-                                                              *NERDTreeWinPos*
-Values: &quot;left&quot;, &quot;right&quot;, &quot;top&quot; or &quot;bottom&quot;
+                                                        *'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-&amp;| 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: &quot;left&quot; or &quot;right&quot;
 Default: &quot;left&quot;.
 
 This option is used to determine where NERD tree window is placed on the
 screen.
 
-&quot;top&quot; or &quot;bottom&quot;, will cause a horizontal split to be created for the tree,
-while &quot;left&quot; and &quot;right&quot; will cause a vertical split.
-
-This option is makes it possible to use two different explorer type
-plugins simultaneously. For example, you could have the taglist plugin on the
-left of the window and the NERD tree on the right.
+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*
+                                                           *'NERDTreeWinSize'*
 Values: a positive integer.
 Default: 31.
 
 This option is used to change the size of the NERD tree when it is loaded.
 
 ==============================================================================
-                                                     *NERDTreePublicFunctions*
-5. Public functions      ~
+4. Hacking the NERD tree                                     *NERDTreeHacking*
+
+Public functions ~
 
 The script provides 2 public functions for your hacking pleasure. Their
 signatures are: &gt;
@@ -877,16 +946,29 @@ style OO. To see the functions that each class provides you can read look at
 the code.
 
 Use the node objects to manipulate the structure of the tree. Use the path
-objects to access the data the tree represents and to make changes to the
-filesystem.
+objects to access the files/directories the tree nodes represent.
 
-==============================================================================
-5. TODO list                                                    *NERDTreeTodo*
+The NERD tree filetype ~
 
-Window manager integration?
+NERD tree buffers have a filetype of &quot;nerdtree&quot;. You can use this to hack the
+NERD tree via autocommands (on |FileType|) or via an ftplugin.
+
+For example, putting this code in ~/.vim/ftplugin/nerdtree.vim would override
+the o mapping, making it open the selected node in a new gvim instance. &gt;
+
+    nnoremap &lt;silent&gt; &lt;buffer&gt; o :call &lt;sid&gt;openInNewVimInstance()&lt;cr&gt;
+    function! s:openInNewVimInstance()
+        let p = NERDTreeGetCurrentPath()
+        if p != {}
+            silent exec &quot;!gvim &quot; . p.strForOS(1) . &quot;&amp;&quot;
+        endif
+    endfunction
+&lt;
+This way you can add new mappings or :commands or override any existing
+mapping.
 
 ==============================================================================
-6. The Author                                                 *NERDTreeAuthor*
+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.
@@ -896,332 +978,100 @@ 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 ;)
 
-==============================================================================
-7. Changelog                                               *NERDTreeChangelog*
+The latest stable versions can be found at
+    http://www.vim.org/scripts/script.php?script_id=1658
 
-2.14.0
-    - fix a bug where the &lt;c-w&gt;o mapping  would cause the tree window to be
-      incorrectly sized when reopened.
-    - add keymapping to delete bookmarks from the bookmarks table, see
-      :help NERDTree-D
-    - lots of refactoring
-2.13.0
-    - make NERDTreeChDir option default to 0 (i.e. never change vims current
-      working dir by default)
-    - when moving/deleting nodes with the filesystem menu, move/delete any
-      associated bookmarks
-    - make the t/T on directory nodes open a fresh NERD tree for the selected
-      dir in a new tab, rather than a netrw.
-    - place the cursor at the top of the bookmarks table when opening it with B
-    - make NERDTreeQuitOnOpen option work with the g&lt;tab&gt; and go mappings,
-      thanks to Maxim Kim for the bug report
-    - change how invalid bookmarks are handled. Now they are not deleted. If a
-      bookmark is malformed (in the bookmarks file) or points to an
-      invalid/nonexisting location then print an error and place the offending
-      bookmarks at the bottom of the bookmarks file. See :help
-      |NERDTreeInvalidBookmarks| for info. Thanks to Zhang Shuhan for the
-      suggestion and the testing.
-    - fix a bug with the 'o' mapping that occurred when opening a new buffer
-      for a file whose name was a substring of an already open file. Thanks to
-      Charlton Wang for the report.
-    - stop the script from going into an infinite loop when it tries to cache
-      a named pipe. Thanks to Charlton Wang for the report.
-
-2.12.0
-    - added a UI for bookmarks. See :help NERDTreeBookmarkTable for details.
-      Thanks to Zhang Shuhan for testing and bug reports.
-    - relaxed the restrictions on bookmark names, now the only restriction is
-      that they cant contain spaces. This allows for e.g. Chinese bookmark
-      names. Thanks to Zhang Shuhan for the suggestion.
-    - combined the NERDTreeWinPos and NERDTreeSplitVertical options. See :help
-      NERDTreeWinPos.
-    - applied a patch from Matan Nassau to add the NERDTreeQuitOnOpen option
-      which closes the tree window after opening a file. See :help
-      NERDTreeQuitOnOpen.
-    - optimised the nerd tree rendering. Now it takes just over 1/3 of the time
-      it previously took to render.
-    - now the tree filter mappings toggle the filters &quot;per tree&quot; rather than
-      globally. The global filter variables are used to set the initial filter
-      settings for each new NERD tree.
-    - fix to window resizing when opening a file when NERD tree is the only
-      window open
-    - other fixes
-
-2.11.0
-    - changes to the 'o' mapping when opening files:
-      - dont clobber &quot;special&quot; windows (eg taglist/quickfix/etc). This should
-        make the NERD tree play nicer with other explorer plugins. Thanks to
-        Yuan Jiang for the suggestion.
-      - if the file is already open in the current tab, just move the cursor
-        to that window
-    - highlight executable files, made some slight changes to other
-      highlighting
-    - if the user resizes the tree window, keep that new size. Dont reset to
-      the default during the &lt;tab&gt; mapping, or :NERDTreeToggle command. Only
-      reset the size if a fresh tree is started with :NERDTree.
-    - remove the &quot;magic&quot; functionality from the &lt;c-j&gt;/&lt;c-k&gt; mappings (it was
-      more confusing than helpful)
-    - other minor fixes
-
-2.10.0
-    - added bookmarks, see :help NERDTreeBookmarkCommands for details. Thanks
-      to Piotr Czachur for all his testing and suggestions.
-    - fixed screen jumping bug with when &amp;scrolloff != 0
-    - fixed some bugs with copying nodes
-    - other random fixes
-    - change license to wtfpl
-
-2.9.0
-
-    - path handling improvements, thanks to Zhang Shuhan for heaps of
-      testing/bug reports
-        * improved how paths are stored, now the script will no longer get
-          confused about drives on MF Windows
-        * made the script way better at handling paths with strange characters
-          in them (eg '$@; etc)
-    - applied a patch from Cory Echols
-        * add the command :NERDTreeClose to close the tree for the current tab
-        * set the filetype for the NERD tree buffer to &quot;nerdtree&quot;
-
-2.8.0
-    - added an option to enable/disable line numbers in the NERD tree window,
-      thanks to Olivier Yiptong for the email.
-
-2.7.1
-    - Changed the keys for the filesystem menu to be mnemonic rather than
-      arbitrary integers
-    - Documented the copying functionality in the filesystem menu
-
-2.7.0
-    - Bug fix: Now when you have the tree on the right and you open it with
-      multiple windows stacked, it will take up the full height of the vim
-      window.
-    - Now line numbers always turned off in the tree by default
-    - Implemented copying of nodes (via the filesystem menu) for *nix/macosx
-    - took the help doc out of the script and repackaged the whole thing as a
-      zip
-
-2.6.2
-    - Now when you try to open a file node into a window that is modified, the
-      window is not split if the &amp;hidden option is set. Thanks to  Niels Aan
-      de Brugh for this suggestion.
-
-2.6.1
-    - Fixed a major bug with the &lt;tab&gt; mapping. Thanks to Zhang Weiwu for
-      emailing me.
-
-2.6.0
-    - Extended the behaviour of &lt;c-j/k&gt;. Now if the cursor is on a file node
-      and you use &lt;c-j/k&gt; the cursor will jump to its PARENTS next/previous
-      sibling. Go :help NERDTree-c-j and :help NERDTree-c-k for info.
-    - Extended the behaviour of the J/K mappings. Now if the cursor is on the
-      last child of a node and you push J/K it will jump down to the last child
-      of the next/prev of its parents siblings that is open and has children.
-      Go :help NERDTree-J and :help NERDTree-K for info.
-    - The goal of these changes is to make tree navigation faster.
-    - Reorganised the help page a bit.
-    - Removed the E mapping.
-    - bugfixes
-
-2.5.0
-    - Added an option to enforce case sensitivity when sorting tree nodes.
-      Read :help NERDTreeCaseSensitiveSort for details.  (thanks to Michael
-      Madsen for emailing me about this). Case sensitivity defaults to off.
-    - Made the script echo a &quot;please wait&quot; style message when opening large
-      directories. Thanks to AOYAMA Shotaro for this suggestion.
-    - Added 2 public functions that can be used to retrieve the treenode and
-      path that the cursor is on. Read :help NERDTreePublicFunctions for
-      details (thanks again to AOYAMA Shotaro for the idea :).
-    - added 2 new mappings for file nodes: &quot;g&lt;tab&gt;&quot; and &quot;go&quot;. These are the
-      same as the &quot;&lt;tab&gt;&quot; and &quot;o&quot; maps except that the cursor stays in the
-      NERDTree. Note: these maps are slaved to the o and &lt;tab&gt; mappings, so if
-      eg you remap &quot;&lt;tab&gt;&quot; to &quot;i&quot; then the &quot;g&lt;tab&gt;&quot; map will also be changed
-      to &quot;gi&quot;.
-    - Renamed many of the help tags to be simpler.
-    - Simplified the ascii &quot;graphics&quot; for the filesystem menu
-    - Fixed bugs.
-    - Probably created bugs.
-    - Refactoring.
-
-2.4.0
-    - Added the P mapping to jump to the tree root.
-    - Added window centering functionality that can be triggered when doing
-      using any of the tree nav mappings. Essentially, if the cursor comes
-      within a certain distance of the top/bottom of the window then a zz is
-      done in the window. Two related options were added: NERDTreeAutoCenter
-      to turn this functionality on/off, and NERDTreeAutoCenterThreshold to
-      control how close the cursor has to be to the window edge to trigger the
-      centering.
-
-2.3.0
-    - Tree navigation changes:
-      - Added J and K mappings to jump to last/first child of the current dir.
-        Options to customise these mappings have also been added.
-      - Remapped the jump to next/prev sibling commands to be &lt;C-j&gt; and &lt;C-k&gt; by
-        default.
-      These changes should hopefully make tree navigation mappings easier to
-      remember and use as the j and k keys are simply reused 3 times (twice
-      with modifier keys).
-
-    - Made it so that, when any of the tree filters are toggled, the cursor
-      stays with the selected node (or goes to its parent/grandparent/... if
-      that node is no longer visible)
-    - Fixed an error in the doc for the mouse mode option.
-    - Made the quickhelp correctly display the current single/double click
-      mappings for opening nodes as specified by the NERDTreeMouseMode option.
-    - Fixed a bug where the script was spazzing after prompting you to delete
-      a modified buffer when using the filesystem menu.
-    - Refactoring
-2.2.3
-    - Refactored the :echo output from the script.
-    - Fixed some minor typos in the doc.
-    - Made some minor changes to the output of the 'Tree filtering mappings'
-      part of the quickhelp
-
-2.2.2
-    - More bugfixes... doh.
-
-2.2.1
-    - Bug fix that was causing an exception when closing the nerd tree. Thanks
-      to Tim carey-smith and Yu Jun for pointing this out.
-
-2.2.0
-    - Now 'cursorline' is set in the NERD tree buffer by default. See :help
-      NERDTreeHighlightCursorline for how to disable it.
-
-2.1.2
-    - Stopped the script from clobbering the 1,2,3 .. 9 registers.
-    - Made it &quot;silent!&quot;ly delete buffers when renaming/deleting file nodes.
-    - Minor correction to the doc
-    - Fixed a bug when refreshing that was occurring when the node you
-      refreshed had been deleted externally.
-    - Fixed a bug that was occurring when you open a file that is already open
-      and modified.
-
-2.1.1
-    - Added a bit more info about the buffers you are prompted to delete when
-      renaming/deleting nodes from the filesystem menu that are already loaded
-      into buffers.
-    - Refactoring and bugfixes
-
-2.1.0
-    - Finally removed the blank line that always appears at the top of the
-      NERDTree buffer
-    - Added NERDTreeMouseMode option. If set to 1, then a double click is
-      required to activate all nodes, if set to 2 then a single click will
-      activate directory nodes, if set to 3 then a single click will activate
-      all nodes.
-    - Now if you delete a file node and have it open in a buffer you are given
-      the option to delete that buffer as well. Similarly if you rename a file
-      you are given the option to delete any buffers containing the old file
-      (if any exist)
-    - When you rename or create a node, the cursor is now put on the new node,
-      this makes it easy immediately edit  the new file.
-    - Fixed a bug with the ! mapping that was occurring on windows with paths
-      containing spaces.
-    - Made all the mappings customisable. See |NERD_tree-mappings| for
-      details. A side effect is that a lot of the &quot;double mappings&quot; have
-      disappeared. E.g 'o' is now the key that is used to activate a node,
-      &lt;CR&gt; is no longer mapped to the same.
-    - Made the script echo warnings in some places rather than standard echos
-    - Insane amounts of refactoring all over the place.
-
-2.0.0
-    - Added two new NERDChristmasTree decorations. First person to spot them
-      and email me gets a free copy of the NERDTree.
-    - Made it so that when you jump around the tree (with the p, s and S
-      mappings) it is counted as a jump by vim. This means if you, eg, push
-      'p' one too many times then you can go `` or ctrl-o.
-    - Added a new option called NERDTreeSortOrder which takes an array of
-      regexs and is used to determine the order that the treenodes are listed
-      in. Go :help NERDTreeSortOrder for details.
-    - Removed the NERDTreeSortDirs option because it is consumed by
-      NERDTreeSortOrder
-    - Added the 'i' mapping which is the same as &lt;tab&gt; but requires less
-      effort to reach.
-    - Added the ! mapping which is  used to execute file in the tree (after it
-      prompts you for arguments etc)
+The latest dev versions are on github
+    http://github.com/scrooloose/nerdtree
 
 
 ==============================================================================
-8. Credits                                                   *NERDTreeCredits*
-
-Thanks to Tim Carey-Smith for testing/using the NERD tree from the first
-pre-beta version, for his many suggestions and for his constant stream of bug
-complaints.
-
-Thanks to Vigil for trying it out before the first release :) and suggesting
-that mappings to open files in new tabs should be implemented.
-
-Thanks to Nick Brettell for testing, fixing my spelling and suggesting i put a
-    .. (up a directory)
-line in the gui.
-
-Thanks to Thomas Scott Urban - the author of the vtreeexplorer plugin - whose
-gui code i borrowed from.
-
-Thanks to Terrance Cohen for pointing out a bug where the script was changing
-vims CWD all over the show.
-
-Thanks to Yegappan Lakshmanan (author of Taglist and other orgasmically
-wonderful plugins) for telling me how to fix a bug that was causing vim to go
-into visual mode everytime you double clicked a node :)
-
-Thanks to Jason Mills for sending me a fix that allows windows paths to use
-forward slashes as well as backward.
-
-Thanks to Michael Geddes (frogonwheels on #vim at freenode) for giving me some
-tips about syntax highlighting when i was doing highlighting for the
-quickhelp.
-
-Thanks to Yu Jun for emailing me about a bug that was occurring when closing
-the tree.
-
-Thanks to Michael Madsen for emailing me about making case sensitivity
-optional when sorting nodes.
-
-Thanks to AOYAMA Shotaro for suggesting that i echo a &quot;please wait&quot; message
-when opening large directories.
-
-Thanks to Michael Madsen for requesting the NERDTreeCaseSensitiveSort option.
-
-Thanks to AOYAMA Shotaro for suggesting that a &quot;please wait&quot; style message be
-echoed when opening large directories. Also, thanks for the suggestion of
-having public functions in the script to access the internal data :D
-
-Thanks to Zhang Weiwu for emailing me about a bug with the the &lt;tab&gt; mapping
-in 2.6.0
-
-Thanks to  Niels Aan de Brugh for the suggestion that the script now split the
-window if you try to open a file in a window containing a modified buffer when
-the &amp;hidden option is set.
-
-Thanks to Olivier Yiptong for prompting me to make line numbers in the
-NERD tree window optional.
-
-Thanks to Zhang Shuhan for all of his emails and testing to help improve the
-NERD tree path handling. Thanks also for suggesting the bookmarks gui, and for
-testing and his many suggestions and bugreports about bookmarks.
-
-Thanks to Cory Echols for sending a patch to add the :NERDTreeClose command and
-set the NERD tree buffers filetype to 'nerdtree'
-
-Thanks to Piotr Czachur for all his suggestions and testing for the bookmarks
-feature.
-
-Thanks to Yuan Jiang for suggesting the &quot;o&quot; mapping shouldnt clobber &quot;special&quot;
-windows, like taglist.
-
-Thanks to Matan Nassau for the patch to add the NERDTreeQuitOnOpen option.
-
-Thanks to Maxim Kim for reporting a bug with g&lt;tab&gt; and go mappings when
-NERDTreeQuitOnOpen was set.
+6. Changelog                                               *NERDTreeChangelog*
+
+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 &quot;vim
+      &lt;some dir&gt;&quot; (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 &lt;path&gt; would fail if &lt;path&gt; was relative and
+      didnt start with a ./ or ../  Thanks to James Kanze.
+    - make the q mapping work with secondary (:e &lt;dir&gt;  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 &lt;directory&gt;  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 &quot;top&quot; and &quot;bottom&quot; as valid settings for NERDTreeWinPos
+    - change the '&lt;tab&gt;' mapping to 'i'
+    - change the 'H' mapping to 'I'
+    - lots of refactoring
 
-Thanks to Charlton Wang for reporting bugs with the 'o' mapping and with
-handling named pipes.
+==============================================================================
+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
 
 ==============================================================================
-9. License                                                   *NERDTreeLicense*
+8. License                                                   *NERDTreeLicense*
 
 The NERD tree is released under the wtfpl.
 See http://sam.zoy.org/wtfpl/COPYING.</diff>
      <filename>doc/NERD_tree.txt</filename>
    </modified>
    <modified>
      <diff>@@ -63,9 +63,9 @@ Rails application development.
    and several other commands are provided.  |rails-navigation|
 
 5. Enhanced syntax highlighting.  From has_and_belongs_to_many to
-   distance_of_time_in_words, it's here.  For Vim 7 users, 'completefunc' is
-   set to enable syntax based completion on |i_CTRL-X_CTRL-U|, making it easy
-   to complete such long method names. |rails-syntax|
+   distance_of_time_in_words, it's here.  For easy completion of these long
+   method names, 'completefunc' is set to enable syntax based completion on
+   |i_CTRL-X_CTRL-U|. |rails-syntax|
 
 6. Interface to script/*.  Generally, use &quot;:Rscript about&quot; to call
    &quot;script/about&quot;.  Most commands have wrappers with additional features:
@@ -92,21 +92,17 @@ directly to |rails-install-plugin| below.
 
 Installing and Configuring Vim ~
 						*rails-install-vim*
-Because it is common for users to utilize an older version of Vim that came
-installed on a system, rails.vim has a design goal of remaining compatible
-with versions of Vim 6.2 and newer.  However, if you have a choice in the
-matter, you are strongly encouraged to install the latest version available.
-Older versions of Vim should work, but increasingly, new plugin features will
-require Vim 7 or newer.  If possible, install a version of Vim with the |Ruby|
-interface compiled in, as a few features will make use of it when available.
+Vim 7 or newer is required.  If possible, install a version of Vim with the
+|Ruby| interface compiled in, as a few features will make use of it when
+available.
 
 If you are new to Vim, you need to create a vimrc.  For Windows, this file
 goes in ~\_vimrc (try :e ~\_vimrc if you don't know where this is).  On other
 platforms, use ~/.vimrc.  A very minimal example file is shown below.
 &gt;
-  set nocompatible
-  syntax on
-  filetype plugin indent on
+	set nocompatible
+	syntax on
+	filetype plugin indent on
 &gt;
 Installing and Using the Plugin ~
 						*rails-install-plugin*
@@ -139,13 +135,19 @@ actually edit a file from a Rails application.
 			application in {directory}, and loads the README.
 
 						*rails-:Rake*
-:Rake {targets}		Like calling |:make| {targets} (with 'makeprg' being
+:[range]Rake {targets}	Like calling |:make| {targets} (with 'makeprg' being
 			rake).  However, in some contexts, if {targets} are
 			omitted, :Rake defaults to something sensible (like
 			db:migrate in a migration, or your current test).
+			In tests (and specs), giving a line argument runs only
+			the test method (or example) at that line.  Use :.Rake
+			to run the test method at the cursor position.  Use
+			:.Rake inside a self.up or self.down method in a
+			migration to run the db:migrate:up or db:migrate:down
+			task for that particular migration.
 
 						*rails-:Rake!*
-:Rake! {targets}	Called with a bang, :Rake will use an alternate
+:[range]Rake! {targets}	Called with a bang, :Rake will use an alternate
 			'errorformat' which attempts to parse the full stack
 			backtrace.
 
@@ -166,7 +168,10 @@ actually edit a file from a Rails application.
 			|:help| rails.
 
 						*rails-:Redit*
-:Redit {file}		Edit {file}, relative to the application root.
+:Redit {file}		Edit {file}, relative to the application root.  Append
+			:line or #method to jump within the file, as in
+			:Redit app/controllers/users_controller.rb:12 or
+			:Redit app/models/user.rb#activate .
 
 						*rails-:Rlog*
 :Rlog [{logfile}]	Split window and open {logfile} ($RAILS_ENV or
@@ -195,8 +200,9 @@ actually edit a file from a Rails application.
 :Rpreview! [{path}]	As with :Rpreview, except :OpenURL is never used.
 
 						*rails-:Rtags*
-:Rtags			Calls ctags -R on the current application root.
-			Exuberant ctags must be installed.
+:Rtags			Calls ctags -R on the current application root and
+			writes the result to tmp/tags.  Exuberant ctags must
+			be installed.
 
 						*rails-:Rrefresh*
 :Rrefresh		Refreshes certain cached settings.  Most noticeably,
@@ -237,8 +243,7 @@ The 'path' has been modified to include all the best places to be.
 &lt;
 						*rails-:Rfind*
 :Rfind [{file}]		Find {file}.  Very similar to :find, but things like
-			BlogController are properly handled, and if
-			genutils.vim is installed (1.x not 2.x), tab complete
+			BlogController are properly handled, and tab complete
 			works.  The default filename is taken from under the
 			cursor in a manner quite similar to gf, described
 			below.
@@ -262,7 +267,7 @@ Example uses of |gf|, and where they might lead.
 &lt;	app/controllers/blog_controller.rb ~
 &gt;
 	&lt;%= render :partial =&gt; 'sh*ared/sidebar' %&gt;
-&lt;	app/views/shared/_sidebar.rhtml ~
+&lt;	app/views/shared/_sidebar.html.erb ~
 &gt;
 	&lt;%= stylesheet_link_tag :scaf*fold %&gt;
 &lt;	public/stylesheets/scaffold.css ~
@@ -277,7 +282,7 @@ Example uses of |gf|, and where they might lead.
 &lt;	test/fixtures/posts.yml ~
 &gt;
 	layout :pri*nt
-&lt;	app/views/layouts/print.rhtml ~
+&lt;	app/views/layouts/print.html.erb ~
 &gt;
 	&lt;%= link_to &quot;New&quot;, new_comme*nt_path %&gt;
 &lt;	app/controllers/comments_controller.rb (jumps to def new) ~
@@ -309,8 +314,8 @@ Two commands, :A and :R, are used quickly jump to an &quot;alternate&quot; and a
 					*rails-alternate* *rails-related*
 The alternate file is most frequently the test file, though there are
 exceptions.  The related file varies, and is sometimes dependent on current
-current location in the file.  For example, when editing a controller, the
-related file is template for the method currently being edited.
+location in the file.  For example, when editing a controller, the related
+file is template for the method currently being edited.
 
 The easiest way to learn these commands is to experiment.  A few examples of
 alternate and related files follow:
@@ -330,15 +335,17 @@ For the less common cases, a more deliberate set of commands are provided.
 Each of the following takes an optional argument (with tab completion) but
 defaults to a reasonable guess that follows Rails conventions.  For example,
 when editing app/models/employee.rb, :Rcontroller will default to
-app/controllers/employees_controller.rb.  The controller and model options,
-ideally set from  |rails-modelines|,  can override the mapping from model
-related files to controller related files (Rset controller=hiring) and vice
-versa (Rset model=employee).  See |rails-:Rset|.
+app/controllers/employees_controller.rb.  The controller and model options
+can override the mapping from model related files to controller related files
+(Rset controller=hiring) and vice versa (Rset model=employee).  See
+|rails-:Rset|.
 
 Each of the following commands has variants for splitting, vertical splitting
 and opening in a new tab.  For :Rmodel, those variants would be :RSmodel,
 :RVmodel, and :RTmodel.  There is also :REmodel which is a synonym for :Rmodel
-(future versions might allow customization of the behavior of :Rmodel).
+(future versions might allow customization of the behavior of :Rmodel).  They
+also allow for jumping to methods or line numbers using the same syntax as
+|:Redit|.
 
 
 Model Navigation Commands ~
@@ -346,8 +353,7 @@ Model Navigation Commands ~
 The default for model navigation commands is the current model, if it can be
 determined.  For example, test/unit/post_test.rb would have a current model
 of post.  Otherwise, if a controller name can be determined, said controller
-name will be singularized and used.  To override this, use a command or
-modeline like: &gt;
+name will be singularized and used.  To override this, use a command like: &gt;
 	Rset model=comment
 
 :Rmodel						|rails-:Rmodel|
@@ -363,10 +369,9 @@ modeline like: &gt;
 :Rmigration [{pattern}]	If {pattern} is a number, find the migration for that
 			particular set of digits, zero-padding if necessary.
 			Otherwise, find the newest migration containing the
-			given pattern.  The pattern defaults to the current
-			model name, pluralized.  So when editing the Post
-			model, :Rmigration with no arguments might find
-			create_posts.rb, or add_date_to_posts.rb.
+			given pattern.  Omitting the pattern selects the
+			latest migration.  Give a numeric argument of 0 to edit
+			db/schema.rb.
 
 						*rails-:Robserver*
 :Robserver [{name}]	Find the observer with a name like
@@ -386,7 +391,8 @@ modeline like: &gt;
 			between YAML and CSV fixtures.
 
 						*rails-:Runittest*
-:Runittest [{name}]	Edit the unit test for the specified model.
+:Runittest [{name}]	Edit the unit test or model spec for the specified
+			model.
 
 Controller Navigation Commands  ~
 						*rails-controller-navigation*
@@ -394,7 +400,7 @@ The default for controller navigation commands is the current controller, if
 it can be determined.  For example, test/functional/blog_test.rb would have a
 current controller of blog.  Otherwise, if a model name can be determined,
 said model name will be pluralized and used.  To override this, use a command
-or modeline like: &gt;
+like: &gt;
 	Rset controller=blog
 
 :Rcontroller					|rails-:Rcontroller|
@@ -426,11 +432,12 @@ or modeline like: &gt;
 						*rails-:Rapi*
 :Rapi [{name}]		Edit the API for the specified controller.  This
 			command is deprecated; add it yourself with
-			|:Rcommand| if you still desire it.
+			|:Rnavcommand| if you still desire it.
 
 						*rails-:Rfunctionaltest*
 :Rfunctionaltest [{name}]
-			Edit the functional test for the specified controller.
+			Edit the functional test or controller spec for the
+			specified controller.
 
 Miscellaneous Navigation Commands  ~
 						*rails-misc-navigation*
@@ -442,7 +449,10 @@ The following commands are not clearly associated with models or controllers.
 :Rplugin					|rails-:Rplugin|
 :Rlib						|rails-:Rlib|
 :Rtask						|rails-:Rtask|
+:Renvironment                                   |rails-:Renvironment|
+:Rinitializer                                   |rails-:Rinitializer|
 :Rintegrationtest				|rails-:Rintegrationtest|
+:Rspec						|rails-:Rspec|
 
 						*rails-:Rstylesheet*
 :Rstylesheet [{name}]	Edit the stylesheet for the specified name, defaulting
@@ -458,10 +468,11 @@ The following commands are not clearly associated with models or controllers.
 			is omitted, it defaults to init.rb.
 
 						*rails-:Rlib*
-:Rlib {name}		Edit the library from the lib directory for the
+:Rlib [{name}]		Edit the library from the lib directory for the
 			specified name.  If the current file is part of a
 			plugin, the libraries from that plugin can be
-			specified as well.
+			specified as well.  With no argument, defaults to
+			editing config/routes.rb.
 
 						*rails-:Rtask*
 :Rtask [{name}]		Edit the .rake file from lib/tasks for the specified
@@ -470,11 +481,25 @@ The following commands are not clearly associated with models or controllers.
 			argument is given, either the current plugin's
 			Rakefile or the application Rakefile will be edited.
 
+						*rails-:Renvironment*
+:Renvironment [{name}]  Edit the config/environments file specified.  With no
+			argument, defaults to editing config/environment.rb.
+
+						*rails-:Rinitializer*
+:Rinitializer [{name}]  Edit the config/initializers file specified.  With no
+			argument, defaults to editing config/routes.rb.
+
 						*rails-:Rintegrationtest*
 :Rintegrationtest [{name}]
-			Edit the integration test specified.  The default
-			is based on the current controller or model, with no
-			singularization or pluralization done.
+			Edit the integration test specified.  With no
+			argument, defaults to editing test/test_helper.rb.
+
+						*rails-:Rspec*
+:Rspec [{name}]		Edit the given spec.  With no argument, defaults to
+			editing spec/spec_helper.rb (If you want to jump to
+			the spec for the given file, use |:A| instead).  This
+			command is only defined if there is a spec folder in
+			the root of the application.
 
 Custom Navigation Commands  ~
 						*rails-custom-navigation*
@@ -483,8 +508,8 @@ It is also possible to create custom navigation commands.  This is best done
 in an initialization routine of some sort (e.g., an autocommand); see
 |rails-configuration| for details.
 
-						*rails-:Rcommand*
-:Rcommand [options] {name} [{path} ...]
+						*rails-:Rnavcommand*
+:Rnavcommand [options] {name} [{path} ...]
 			Create a navigation command with the supplied
 			name, looking in the supplied paths, using the
 			supplied options.  The -suffix option specifies what
@@ -498,13 +523,15 @@ in an initialization routine of some sort (e.g., an autocommand); see
 			model, controller, or both (as with :Rintegrationtest)
 			is used as a default.
 
-:Rcommand is still under development and far from fully documented, but the
-following examples should illustrate the basics:
-&gt;
-	Rcommand api         app/apis -glob=**/* -suffix=_api.rb
-	Rcommand config      config   -glob=*.*  -suffix= -default=routes.rb
-	Rcommand environment config/environments -default=../environment
-	Rcommand concern     app/concerns -glob=**/*
+						*rails-:Rcommand*
+:Rcommand		Deprecated alias for |:Rnavcommand|
+
+Examples: &gt;
+	Rnavcommand api      app/apis -glob=**/* -suffix=_api.rb
+	Rnavcommand config   config   -glob=*.*  -suffix= -default=routes.rb
+	Rnavcommand concern  app/concerns -glob=**/*
+	Rnavcommand exemplar test/exemplars spec/exemplars -glob=**/*
+			\ -default=model() -suffix=_exemplar.rb
 
 Finally, one Vim feature that proves helpful in conjunction with all of the
 above is |CTRL-^|.  This keystroke edits the previous file, and is helpful to
@@ -519,13 +546,18 @@ A limited amount of completion with &lt;Tab&gt; is supported.
 
 						*rails-:Rscript*
 :Rscript {script} {options}
-			Call ruby script/{script} {options}.
+			Call ruby script/{script} {options}.  Defaults to
+			calling script/console.
 
 						*rails-:Rconsole*
-:Rconsole {options}	Start script/console.  On Windows it will be launched
-			in the background with |!start|.  In the terminal
-			version GNU Screen is used if it is running and
-			|g:rails_gnu_screen| is set.
+:Rconsole {options}	Start script/console.  This command has been
+			deprecated for the pragmatic reason of making
+			|:Rcontroller| easier to tab complete.  To compensate
+			for the eventual disappearance of this command,
+			|:Rscript| now defaults to calling script/console if
+			no other arguments are given.  (If in spite of this
+			you still feel you would miss this command, speak up
+			now!)
 
 						*rails-:Rrunner*
 :[range]Rrunner {code}	Executes {code} with script/runner.  Differs from
@@ -546,11 +578,10 @@ A limited amount of completion with &lt;Tab&gt; is supported.
 
 						*rails-:Rgenerate*
 :Rgenerate {options}	Calls script/generate {options}, and then edits the
-			first file generated.  Respects |g:rails_subversion|.
+			first file generated.
 
 						*rails-:Rdestroy*
-:Rdestroy {options}	Calls script/destroy {options}.  Respects
-			|g:rails_subversion|.
+:Rdestroy {options}	Calls script/destroy {options}.
 
 						*rails-:Rserver*
 :Rserver {options}	Launches script/server {options} in the background.
@@ -581,7 +612,7 @@ The :Rextract command can be used to extract a partial to a new file.
 :[range]Rpartial [{controller}/]{name}	
 			Deprecated alias for :Rextract.
 
-If this is your file, in app/views/blog/show.rhtml: &gt;
+If this is your file, in app/views/blog/show.html.erb: &gt;
 
   1	&lt;div&gt;
   2	  &lt;h2&gt;&lt;%= @post.title %&gt;&lt;/h2&gt;
@@ -598,7 +629,7 @@ Your file will change to this: &gt;
   2	  &lt;%= render :partial =&gt; 'post' %&gt;
   3	&lt;/div&gt;
 
-And app/views/blog/_post.rhtml will now contain: &gt;
+And app/views/blog/_post.html.erb will now contain: &gt;
 
   1	&lt;h2&gt;&lt;%= post.title %&gt;&lt;/h2&gt;
   2	&lt;p&gt;&lt;%= post.body %&gt;&lt;/p&gt;
@@ -648,34 +679,24 @@ place) are also covered in this section.
 			a new project, with appropriate directories (app,
 			etc., but not vendor).
 
-						*rails-:Rproject!*
-:Rproject! [{file}]	Same as :Rproject, only delete existing project if it
-			exists and recreate it.  The logic to delete the old
-			project is convoluted and possibly erroneous; report
-			any problems to the |rails-plugin-author|.  A handy
-			mapping might look something like: &gt;
-		autocmd User Rails map &lt;buffer&gt; &lt;F6&gt; :Rproject!|silent w&lt;CR&gt;
-&lt;			As a bonus, this command organizes views into separate
-			directories for easier navigation.  The downside of
-			this is that you will have to regenerate your project
-			each time you add another view directory (which is why
-			this command recreates your project each time!).
-
 						*rails-:Rdbext* *rails-dbext*
 :Rdbext [{environment}] This command is only provided when the |dbext| plugin
 			is installed.  Loads the {environment} configuration
 			(defaults to $RAILS_ENV or development) from
 			config/database.yml and uses it to configure dbext.
-			The configuration is cached until a different Rails
-			application is edited.  This command is called for you
-			automatically when |g:rails_dbext| is set (default on
-			non-Windows systems).
+			The configuration is cached on a per application
+			basis.  With dbext versions 8.00 and newer, this
+			command is called automatically when needed.  For
+			older versions, it is called automatically when
+			rails.vim loads if |g:rails_dbext| is set (which it is
+			by default).
 
 						*rails-:Rdbext!*
 :Rdbext! [{environment}]
 			Load the database configuration as above, and then
 			attempt a CREATE DATABASE for it.  This is primarily
-			useful for demonstrations.
+			useful for demonstrations, and has been largely
+			superseded by |:Rake| db:create.
 
 						*rails-surround*
 The |surround| plugin available from vim.org enables adding and removing
@@ -722,36 +743,38 @@ core.  Supporting plugins and other add-ons to Rails has the potential to
 rapidly get out of hand.  However, a few pragmatic exceptions have been made.
 
 						*rails-template-types*
-Commands like :Rview use a hardwired list of extensions (rhtml, rjs, etc.)
+Commands like :Rview use a hardwired list of extensions (erb, rjs, etc.)
 when searching for files.  In order to facilitate working with non-standard
 template types, several popular extensions are featured in this list,
 including haml, liquid, and mab (markaby).  These extensions will disappear
 once a related configuration option is added to rails.vim.
 
 						*rails-rspec*
-Support for RSpec is currently experimental and incomplete, with only a
-handful of features being implemented.  :A knows about specs and will jump to
-them, but only if no test file is found.  The associated controller or model
-of a spec is detected, so all navigation commands should work as expected
-inside a spec file.  :Rfixtures will find spec fixtures, but the extension is
-mandatory and tab completion will not work.  :Rake will run the currently
-edited spec.
-
-While there are currently no built-in dedicated RSpec navigation commands, you
-can approximate your own with |:Rcommand|.
+The presence of a spec directory causes several additional behaviors to
+activate.  :A knows about specs and will jump to them (but Test::Unit files
+still get priority).  The associated controller or model of a spec is
+detected, so all navigation commands should work as expected inside a spec
+file.  :Rake in a spec runs just that spec, and in a model, controller, or
+helper, runs the associated spec.
+
+|:Runittest| and |:Rfunctionaltest| lead double lives, handling model and
+controller specs respectively.  For helper and view specs, you can use
+|:Rspec| or define your own navigation commands:
 &gt;
-	Rcommand specmodel      spec/models      -glob=**/*
-		\ -suffix=_spec.rb               -default=model()
-	Rcommand spechelper     spec/helpers     -glob=**/* 
+	Rnavcommand spechelper     spec/helpers     -glob=**/*
 		\ -suffix=_helper_spec.rb        -default=controller()
-	Rcommand speccontroller spec/controllers -glob=**/* 
-		\ -suffix=_controller_spec.rb    -default=controller()
-	Rcommand specview spec/views -glob=**/* -suffix=_view_spec.rb
+	Rnavcommand specview spec/views -glob=**/* -suffix=_spec.rb
 &lt;
+						*rails-merb*
+Merb support is a long term possibility.  For now, if you touch
+config/environment.rb in your Merb application, rails.vim will activate.
+Send feedback on what's missing to the |rails-plugin-author| and perhaps one
+day Merb can be officially supported.
+
 ==============================================================================
 ABBREVIATIONS				*rails-abbreviations* *rails-snippets*
 
-Abbreviations are still experimental.  They may later be extracted into a
+Abbreviations are &quot;snippets lite&quot;.  They may later be extracted into a
 separate plugin, or removed entirely.
 
 						*rails-:Rabbrev*
@@ -781,7 +804,7 @@ Rabbrev pa[ params		pa.inspect		params.inspect
 Rabbrev AR:: ActionRecord	AR::Base		ActiveRecord::Base
 Rabbrev :a :action\ =&gt;\		render :a&lt;Tab&gt;		render :action =&gt; 
 
-In short, :: expands on :, ( expands on (, and [ expands on both . and [.
+In short, ( expands on (, :: expands on . and :, and [ expands on . and [.
 These trailing punctuation marks are NOT part of the final abbreviation, and
 you cannot have two mappings that differ only by punctuation.
 
@@ -790,11 +813,11 @@ an abbreviation ending with &quot;(&quot;, you may define where to insert the
 parenthesis by splitting the expansion into two parts (divided by an unescaped
 space).
 
-Many abbreviations abbreviations are provided by default: use :Rabbrev to list
-them.  They vary depending on the type of file (models have different
-abbreviations than controllers).  There is one &quot;smart&quot; abbreviation, :c, which
-expands to &quot;:controller =&gt; &quot;, &quot;:collection =&gt; &quot;, or &quot;:conditions =&gt; &quot;
-depending on context.
+Many abbreviations are provided by default: use :Rabbrev to list them.  They
+vary depending on the type of file (models have different abbreviations than
+controllers).  There is one &quot;smart&quot; abbreviation, :c, which expands to
+&quot;:controller =&gt; &quot;, &quot;:collection =&gt; &quot;, or &quot;:conditions =&gt; &quot; depending on
+context.
 
 ==============================================================================
 SYNTAX HIGHLIGHTING				*rails-syntax*
@@ -815,7 +838,7 @@ they all link by default to railsMethod.
 If you feel a method has been wrongfully omitted, submit it to the
 |rails-plugin-author|.
 
-					*rails-@params* *rails-syntax-deprecated*
+				*rails-@params* *rails-syntax-deprecated*
 Certain deprecated syntax (like @params and render_text) is highlighted as an
 error.  If you trigger this highlighting, generally it means you need to
 update your code.
@@ -843,11 +866,11 @@ syntax group is used.  The list of assertions can be refreshed with
 						*rails-syntax-strings*
 In the following line of code, the &quot;?&quot; in the conditions clause and the &quot;ASC&quot;
 in the order clause will be highlighted: &gt;
-  Post.find(:all, :conditions =&gt; [&quot;body like ?&quot;,&quot;%e%&quot;] :order =&gt; &quot;title ASC&quot;)
+  Post.find(:all, :conditions =&gt; [&quot;body like ?&quot;,&quot;%e%&quot;], :order =&gt; &quot;title ASC&quot;)
 &lt;
-A string literal using %Q&lt;&gt; delimiters will have its contents highlighted as
-HTML.  This is sometimes useful when writing helpers. &gt;
-  link = %Q&lt;&lt;a href=&quot;http://www.vim.org&quot;&gt;Vim&lt;/a&gt;&gt;
+A string literal using %Q&lt;&gt; or %&lt;&gt; delimiters will have its contents
+highlighted as HTML.  This is sometimes useful when writing helpers. &gt;
+  link = %&lt;&lt;a href=&quot;http://www.vim.org&quot;&gt;Vim&lt;/a&gt;&gt;
 &lt;
 						*rails-syntax-yaml*
 YAML syntax highlighting has been extended to highlight eRuby, which can be
@@ -871,7 +894,7 @@ This makes it easy to access a buried file: &gt;
 	:find blog_controller.rb
 &lt;
 					*rails-'suffixesadd'*	*rails-'sua'*
-This is filetype dependent, but typically includes .rb, .rhtml, and several
+This is filetype dependent, but typically includes .rb, .rake, and several
 others.  This allows shortening the above example: &gt;
 	:find blog_controller
 &lt;
@@ -913,7 +936,7 @@ These are based off the information shown in the 'statusline' (see
 |rails-'statusline'|), with hyphens changed to periods. A few examples: &gt;
 	autocmd User Rails.controller*	iabbr &lt;buffer&gt; wsn wsdl_service_name
 	autocmd User Rails.model.arb*	iabbr &lt;buffer&gt; vfo validates_format_of
-	autocmd User Rails.view.rhtml*  imap  &lt;buffer&gt; &lt;C-Z&gt; &lt;%=  %&gt;&lt;C-O&gt;3h
+	autocmd User Rails.view.erb*    imap  &lt;buffer&gt; &lt;C-Z&gt; &lt;%=  %&gt;&lt;C-O&gt;3h
 End all such Rails autocommands with asterisks, even if you have an exact
 specification.  There is also a filename matching syntax: &gt;
 	autocmd User Rails/db/schema.rb  Rset task=db:schema:dump
@@ -930,8 +953,7 @@ Rails file is loaded.
 						*config/rails.vim*
 If you have settings particular to a specific project, they can be put in a
 config/rails.vim file in the root directory of the application.  The file is
-sourced in the |sandbox| for security reasons.  This only works in Vim 7 or
-newer.
+sourced in the |sandbox| for security reasons.
 
 						*rails-:Rset*
 :Rset {option}[={value}]
@@ -939,9 +961,9 @@ newer.
 			called directly, from an autocommand, or from
 			config/rails.vim.
 
-Options may be set set in one of four scopes, which my be indicated by an
+Options may be set in one of four scopes, which may be indicated by an
 optional prefix.  These scopes determine how broadly an option will apply.
-Generally, the default scope is sufficient
+Generally, the default scope is sufficient.
 
 Scope	Description ~
 a:	All files in one Rails application
@@ -962,11 +984,10 @@ l:preview	 URL stub for :Rpreview (e.g., blog/show/1)
 b:task		 Default task used with :Rake
 l:related	 Custom related file for :R, relative to the Rails root
 a:root_url	 Root URL for commands like :Rpreview
-a:ruby_fork_port Experimental: use ruby_fork on given port to speed things up
 
 Examples: &gt;
 	:Rset root_url=http://localhost:12345
-	:Rset related=app/views/blog/edit.rhtml preview=blog/edit/1
+	:Rset related=app/views/blog/edit.html.erb preview=blog/edit/1
 	:Rset alternate=app/models/
 	:Rset l:task=preview        &quot; Special pseudo-task for :Rake
 
@@ -977,12 +998,14 @@ If |g:rails_modelines| is enabled, these options can also be set from
 modelines near the beginning or end of the file.  These modelines will always
 set buffer-local options; scope should never be specified.  Examples: &gt;
 	# Rset task=db:schema:load
-	&lt;%# Rset alternate=app/views/layouts/application.rhtml %&gt;
+	&lt;%# Rset alternate=app/views/layouts/application.html.erb %&gt;
 Modelines can also be local to a method.  Example: &gt;
 	def test_comment
 	  # rset alternate=app/models/comment.rb
 These two forms differ only in case.
 
+Modelines are deprecated.
+
 ==============================================================================
 GLOBAL SETTINGS					*rails-global-settings*
 
@@ -991,8 +1014,9 @@ can be enabled by setting them to 1 in your vimrc, and disabled by setting
 them to 0. &gt;
 	let g:rails_some_option=1
 	let g:rails_some_option=0
-Most of these should never need to be used.  The few that might be interesting
-are |g:rails_expensive|, |g:rails_subversion|, and |g:rails_default_database|.
+Most of these seldom need to be used.  So seldom, in fact, that you should
+notify the |rails-plugin-author| if you find any of them useful, as nearly all
+are being considered for removal.
 
 						*g:loaded_rails*  &gt;
 	let g:loaded_rails=1
@@ -1010,8 +1034,7 @@ like table name completion and commands like &gt;
 	:Create database brablog_development
 	:Select * from posts where title like '%Denmark%'
 Note that dbext is a complicated plugin, and may require additional
-configuration.  See |dbext| (if installed) and |sql-completion-dynamic| (in
-Vim 7).
+configuration.  See |dbext| (if installed) and |sql-completion-dynamic|.
 
 						*g:rails_default_file*  &gt;
 	let g:rails_default_file='config/database.yml'
@@ -1054,8 +1077,7 @@ Modelines set buffer-local options using the :Rset command.
 Also enables method specific modelines (note the case difference): &gt;
 	  def show
 	    # rset preview=blog/show/1
-Modelines are extremely useful but may cause security concerns when editing
-projects from an untrusted source. Enabled by default.
+Modelines are deprecated and disabled by default.
 
 						*g:rails_menu*  &gt;
 	let g:rails_menu=1
@@ -1070,14 +1092,8 @@ b:rails_url.
 						*g:rails_statusline*  &gt;
 	let g:rails_statusline=1
 Give a clue in the statusline when this plugin is enabled.  Enabled by
-default.
-
-						*g:rails_subversion*  &gt;
-	let g:rails_subversion=1
-Automatically add/remove files to the subversion repository for commands like
-|:Rgenerate| and |:Rdestroy| (but not |:Rscript|).  Ignored when the
-application is not part of a subversion repository.  Deprecated and disabled
-by default.
+default.  (Does anybody care about this?  Years after writing it the author is
+beginning to think it is mostly a waste of space.)
 
 						*g:rails_syntax*  &gt;
 	let g:rails_syntax=1
@@ -1105,11 +1121,11 @@ The official homepage is
     http://rails.vim.tpope.net
 The latest stable version can be found at
     http://www.vim.org/scripts/script.php?script_id=1567
-In Vim 7, you can keep up to date with |GetLatestVimScripts|.
+You can keep up to date with |GetLatestVimScripts|.
 
 The very latest development versions can be retrieved from Git:
-    http://git.tpope.net/?p=vim-rails.git
-    git clone git://git.tpope.net/git/vim-rails.git
+    http://github.com/tpope/vim-rails
+    git clone git://github.com/tpope/vim-rails.git
 
 Feedback is highly desired on this plugin.  Please send all comments,
 complaints, and compliments to the author.  No bug is too small to report.</diff>
      <filename>doc/rails.txt</filename>
    </modified>
    <modified>
      <diff>@@ -23,8 +23,10 @@
 'Tlist_Use_SingleClick'	taglist.txt	/*'Tlist_Use_SingleClick'*
 'Tlist_WinHeight'	taglist.txt	/*'Tlist_WinHeight'*
 'Tlist_WinWidth'	taglist.txt	/*'Tlist_WinWidth'*
-:MatchDebug	matchit.txt	/*:MatchDebug*
 :MkVimball	pi_vimball.txt	/*:MkVimball*
+:NERDTree	NERD_tree.txt	/*:NERDTree*
+:NERDTreeFromBookmark	NERD_tree.txt	/*:NERDTreeFromBookmark*
+:NERDTreeToggle	NERD_tree.txt	/*:NERDTreeToggle*
 :RmVimball	pi_vimball.txt	/*:RmVimball*
 :Snippet	snippets_emu.txt	/*:Snippet*
 :TlistAddFiles	taglist.txt	/*:TlistAddFiles*
@@ -47,33 +49,93 @@
 :VimballList	pi_vimball.txt	/*:VimballList*
 CreateBundleSnippet	snippets_emu.txt	/*CreateBundleSnippet*
 CreateSnippet	snippets_emu.txt	/*CreateSnippet*
-MatchError	matchit.txt	/*MatchError*
+EnhComm-Bugs	EnhancedCommentify.txt	/*EnhComm-Bugs*
+EnhComm-Credits	EnhancedCommentify.txt	/*EnhComm-Credits*
+EnhComm-EnhCommentify	EnhancedCommentify.txt	/*EnhComm-EnhCommentify*
+EnhComm-Fallbacks	EnhancedCommentify.txt	/*EnhComm-Fallbacks*
+EnhComm-Keybindings	EnhancedCommentify.txt	/*EnhComm-Keybindings*
+EnhComm-NewLanguages	EnhancedCommentify.txt	/*EnhComm-NewLanguages*
+EnhComm-Options	EnhancedCommentify.txt	/*EnhComm-Options*
+EnhComm-Support	EnhancedCommentify.txt	/*EnhComm-Support*
+EnhancedCommentify	EnhancedCommentify.txt	/*EnhancedCommentify*
+EnhancedCommentify()	EnhancedCommentify.txt	/*EnhancedCommentify()*
+NERDChristmasTree	NERD_tree.txt	/*NERDChristmasTree*
+NERDTree	NERD_tree.txt	/*NERDTree*
+NERDTree-!	NERD_tree.txt	/*NERDTree-!*
+NERDTree-?	NERD_tree.txt	/*NERDTree-?*
+NERDTree-B	NERD_tree.txt	/*NERDTree-B*
+NERDTree-C	NERD_tree.txt	/*NERDTree-C*
+NERDTree-D	NERD_tree.txt	/*NERDTree-D*
+NERDTree-F	NERD_tree.txt	/*NERDTree-F*
+NERDTree-H	NERD_tree.txt	/*NERDTree-H*
+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-c-j	NERD_tree.txt	/*NERDTree-c-j*
+NERDTree-c-k	NERD_tree.txt	/*NERDTree-c-k*
+NERDTree-contents	NERD_tree.txt	/*NERDTree-contents*
+NERDTree-e	NERD_tree.txt	/*NERDTree-e*
+NERDTree-f	NERD_tree.txt	/*NERDTree-f*
+NERDTree-go	NERD_tree.txt	/*NERDTree-go*
+NERDTree-gtab	NERD_tree.txt	/*NERDTree-gtab*
+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-t	NERD_tree.txt	/*NERDTree-t*
+NERDTree-tab	NERD_tree.txt	/*NERDTree-tab*
+NERDTree-u	NERD_tree.txt	/*NERDTree-u*
+NERDTree-x	NERD_tree.txt	/*NERDTree-x*
+NERDTreeAuthor	NERD_tree.txt	/*NERDTreeAuthor*
+NERDTreeAutoCenter	NERD_tree.txt	/*NERDTreeAutoCenter*
+NERDTreeAutoCenterThreshold	NERD_tree.txt	/*NERDTreeAutoCenterThreshold*
+NERDTreeBookmarkCommands	NERD_tree.txt	/*NERDTreeBookmarkCommands*
+NERDTreeBookmarkTable	NERD_tree.txt	/*NERDTreeBookmarkTable*
+NERDTreeBookmarks	NERD_tree.txt	/*NERDTreeBookmarks*
+NERDTreeBookmarksFile	NERD_tree.txt	/*NERDTreeBookmarksFile*
+NERDTreeCaseSensitiveSort	NERD_tree.txt	/*NERDTreeCaseSensitiveSort*
+NERDTreeChDirMode	NERD_tree.txt	/*NERDTreeChDirMode*
+NERDTreeChangelog	NERD_tree.txt	/*NERDTreeChangelog*
+NERDTreeCredits	NERD_tree.txt	/*NERDTreeCredits*
+NERDTreeFilesysMenu	NERD_tree.txt	/*NERDTreeFilesysMenu*
+NERDTreeFunctionality	NERD_tree.txt	/*NERDTreeFunctionality*
+NERDTreeGlobalCommands	NERD_tree.txt	/*NERDTreeGlobalCommands*
+NERDTreeHighlightCursorline	NERD_tree.txt	/*NERDTreeHighlightCursorline*
+NERDTreeIgnore	NERD_tree.txt	/*NERDTreeIgnore*
+NERDTreeInvalidBookmarks	NERD_tree.txt	/*NERDTreeInvalidBookmarks*
+NERDTreeLicense	NERD_tree.txt	/*NERDTreeLicense*
+NERDTreeMappings	NERD_tree.txt	/*NERDTreeMappings*
+NERDTreeMouseMode	NERD_tree.txt	/*NERDTreeMouseMode*
+NERDTreeOptionDetails	NERD_tree.txt	/*NERDTreeOptionDetails*
+NERDTreeOptionSummary	NERD_tree.txt	/*NERDTreeOptionSummary*
+NERDTreeOptions	NERD_tree.txt	/*NERDTreeOptions*
+NERDTreePublicFunctions	NERD_tree.txt	/*NERDTreePublicFunctions*
+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*
+NERDTreeTodo	NERD_tree.txt	/*NERDTreeTodo*
+NERDTreeWinPos	NERD_tree.txt	/*NERDTreeWinPos*
+NERDTreeWinSize	NERD_tree.txt	/*NERDTreeWinSize*
+NERD_tree.txt	NERD_tree.txt	/*NERD_tree.txt*
 Tlist_Get_Tag_Prototype_By_Line()	taglist.txt	/*Tlist_Get_Tag_Prototype_By_Line()*
 Tlist_Get_Tagname_By_Line()	taglist.txt	/*Tlist_Get_Tagname_By_Line()*
 Tlist_Set_App()	taglist.txt	/*Tlist_Set_App()*
 Tlist_Update_File_Tags()	taglist.txt	/*Tlist_Update_File_Tags()*
 Vimball-copyright	pi_vimball.txt	/*Vimball-copyright*
-[%	matchit.txt	/*[%*
-]%	matchit.txt	/*]%*
-b:match_col	matchit.txt	/*b:match_col*
-b:match_debug	matchit.txt	/*b:match_debug*
-b:match_ignorecase	matchit.txt	/*b:match_ignorecase*
-b:match_ini	matchit.txt	/*b:match_ini*
-b:match_iniBR	matchit.txt	/*b:match_iniBR*
-b:match_match	matchit.txt	/*b:match_match*
-b:match_pat	matchit.txt	/*b:match_pat*
-b:match_skip	matchit.txt	/*b:match_skip*
-b:match_table	matchit.txt	/*b:match_table*
-b:match_tail	matchit.txt	/*b:match_tail*
-b:match_wholeBR	matchit.txt	/*b:match_wholeBR*
-b:match_word	matchit.txt	/*b:match_word*
-b:match_words	matchit.txt	/*b:match_words*
 basic-snippet	snippets_emu.txt	/*basic-snippet*
 config/rails.vim	rails.txt	/*config\/rails.vim*
 creating-snippets	snippets_emu.txt	/*creating-snippets*
 cs	surround.txt	/*cs*
 ds	surround.txt	/*ds*
-g%	matchit.txt	/*g%*
 g:loaded_rails	rails.txt	/*g:loaded_rails*
 g:rails_abbreviations	rails.txt	/*g:rails_abbreviations*
 g:rails_dbext	rails.txt	/*g:rails_dbext*
@@ -86,7 +148,6 @@ g:rails_mappings	rails.txt	/*g:rails_mappings*
 g:rails_menu	rails.txt	/*g:rails_menu*
 g:rails_modelines	rails.txt	/*g:rails_modelines*
 g:rails_statusline	rails.txt	/*g:rails_statusline*
-g:rails_subversion	rails.txt	/*g:rails_subversion*
 g:rails_syntax	rails.txt	/*g:rails_syntax*
 g:rails_tabstop	rails.txt	/*g:rails_tabstop*
 g:rails_url	rails.txt	/*g:rails_url*
@@ -95,36 +156,9 @@ g:vimball_mkdir	pi_vimball.txt	/*g:vimball_mkdir*
 g:vimball_path_escape	pi_vimball.txt	/*g:vimball_path_escape*
 i_CTRL-G_S	surround.txt	/*i_CTRL-G_S*
 i_CTRL-G_s	surround.txt	/*i_CTRL-G_s*
+loaded_nerd_tree	NERD_tree.txt	/*loaded_nerd_tree*
 macros/rails.vim	rails.txt	/*macros\/rails.vim*
-matchit	matchit.txt	/*matchit*
-matchit-%	matchit.txt	/*matchit-%*
-matchit-\1	matchit.txt	/*matchit-\\1*
-matchit-activate	matchit.txt	/*matchit-activate*
-matchit-backref	matchit.txt	/*matchit-backref*
-matchit-bugs	matchit.txt	/*matchit-bugs*
-matchit-choose	matchit.txt	/*matchit-choose*
-matchit-configure	matchit.txt	/*matchit-configure*
-matchit-debug	matchit.txt	/*matchit-debug*
-matchit-details	matchit.txt	/*matchit-details*
-matchit-highlight	matchit.txt	/*matchit-highlight*
-matchit-hl	matchit.txt	/*matchit-hl*
-matchit-intro	matchit.txt	/*matchit-intro*
-matchit-languages	matchit.txt	/*matchit-languages*
-matchit-modes	matchit.txt	/*matchit-modes*
-matchit-newlang	matchit.txt	/*matchit-newlang*
-matchit-o_%	matchit.txt	/*matchit-o_%*
-matchit-parse	matchit.txt	/*matchit-parse*
-matchit-s:notend	matchit.txt	/*matchit-s:notend*
-matchit-s:sol	matchit.txt	/*matchit-s:sol*
-matchit-spaces	matchit.txt	/*matchit-spaces*
-matchit-troubleshoot	matchit.txt	/*matchit-troubleshoot*
-matchit-v_%	matchit.txt	/*matchit-v_%*
-matchit.txt	matchit.txt	/*matchit.txt*
-matchit.vim	matchit.txt	/*matchit.vim*
 named-tags	snippets_emu.txt	/*named-tags*
-o_[%	matchit.txt	/*o_[%*
-o_]%	matchit.txt	/*o_]%*
-o_g%	matchit.txt	/*o_g%*
 pi_vimball.txt	pi_vimball.txt	/*pi_vimball.txt*
 project	project.txt	/*project*
 project-adding-mappings	project.txt	/*project-adding-mappings*
@@ -188,12 +222,14 @@ rails-:Rdestroy	rails.txt	/*rails-:Rdestroy*
 rails-:Rdoc	rails.txt	/*rails-:Rdoc*
 rails-:Rdoc!	rails.txt	/*rails-:Rdoc!*
 rails-:Redit	rails.txt	/*rails-:Redit*
+rails-:Renvironment	rails.txt	/*rails-:Renvironment*
 rails-:Rextract	rails.txt	/*rails-:Rextract*
 rails-:Rfind	rails.txt	/*rails-:Rfind*
 rails-:Rfixtures	rails.txt	/*rails-:Rfixtures*
 rails-:Rfunctionaltest	rails.txt	/*rails-:Rfunctionaltest*
 rails-:Rgenerate	rails.txt	/*rails-:Rgenerate*
 rails-:Rhelper	rails.txt	/*rails-:Rhelper*
+rails-:Rinitializer	rails.txt	/*rails-:Rinitializer*
 rails-:Rintegrationtest	rails.txt	/*rails-:Rintegrationtest*
 rails-:Rinvert	rails.txt	/*rails-:Rinvert*
 rails-:Rjavascript	rails.txt	/*rails-:Rjavascript*
@@ -203,6 +239,7 @@ rails-:Rlib	rails.txt	/*rails-:Rlib*
 rails-:Rlog	rails.txt	/*rails-:Rlog*
 rails-:Rmigration	rails.txt	/*rails-:Rmigration*
 rails-:Rmodel	rails.txt	/*rails-:Rmodel*
+rails-:Rnavcommand	rails.txt	/*rails-:Rnavcommand*
 rails-:Robserver	rails.txt	/*rails-:Robserver*
 rails-:Rp	rails.txt	/*rails-:Rp*
 rails-:Rpartial	rails.txt	/*rails-:Rpartial*
@@ -211,7 +248,6 @@ rails-:Rpp	rails.txt	/*rails-:Rpp*
 rails-:Rpreview	rails.txt	/*rails-:Rpreview*
 rails-:Rpreview!	rails.txt	/*rails-:Rpreview!*
 rails-:Rproject	rails.txt	/*rails-:Rproject*
-rails-:Rproject!	rails.txt	/*rails-:Rproject!*
 rails-:Rrefresh	rails.txt	/*rails-:Rrefresh*
 rails-:Rrefresh!	rails.txt	/*rails-:Rrefresh!*
 rails-:Rrunner	rails.txt	/*rails-:Rrunner*
@@ -219,6 +255,7 @@ rails-:Rscript	rails.txt	/*rails-:Rscript*
 rails-:Rserver	rails.txt	/*rails-:Rserver*
 rails-:Rserver!	rails.txt	/*rails-:Rserver!*
 rails-:Rset	rails.txt	/*rails-:Rset*
+rails-:Rspec	rails.txt	/*rails-:Rspec*
 rails-:Rstylesheet	rails.txt	/*rails-:Rstylesheet*
 rails-:Rtags	rails.txt	/*rails-:Rtags*
 rails-:Rtask	rails.txt	/*rails-:Rtask*
@@ -247,6 +284,7 @@ rails-integration	rails.txt	/*rails-integration*
 rails-introduction	rails.txt	/*rails-introduction*
 rails-license	rails.txt	/*rails-license*
 rails-menu	rails.txt	/*rails-menu*
+rails-merb	rails.txt	/*rails-merb*
 rails-migrations	rails.txt	/*rails-migrations*
 rails-misc-navigation	rails.txt	/*rails-misc-navigation*
 rails-model-navigation	rails.txt	/*rails-model-navigation*
@@ -322,10 +360,7 @@ taglist-session	taglist.txt	/*taglist-session*
 taglist-todo	taglist.txt	/*taglist-todo*
 taglist-using	taglist.txt	/*taglist-using*
 taglist.txt	taglist.txt	/*taglist.txt*
-v_[%	matchit.txt	/*v_[%*
-v_]%	matchit.txt	/*v_]%*
-v_a%	matchit.txt	/*v_a%*
-v_g%	matchit.txt	/*v_g%*
+taskpaper	taskpaper.txt	/*taskpaper*
 vba	pi_vimball.txt	/*vba*
 vimball	pi_vimball.txt	/*vimball*
 vimball-contents	pi_vimball.txt	/*vimball-contents*</diff>
      <filename>doc/tags</filename>
    </modified>
    <modified>
      <diff>@@ -17,8 +17,8 @@
 
 syntax on
 filetype indent on
-set shiftwidth=4 expandtab smarttab incsearch
+set shiftwidth=2 expandtab smarttab incsearch
 augroup railstoolkit
   autocmd!
-  autocmd FileType ruby,yaml,haml,sass set autoindent shiftwidth=2 softtabstop=2 expandtab number smarttab incsearch
+  autocmd FileType txt,html,ruby,yaml,haml,sass set autoindent shiftwidth=2 softtabstop=2 expandtab number smarttab incsearch
 augroup END</diff>
      <filename>macros/rails-toolkit.vim</filename>
    </modified>
    <modified>
      <diff>@@ -2,7 +2,7 @@
 &quot; File:        NERD_tree.vim
 &quot; Description: vim global plugin that provides a nice tree explorer
 &quot; Maintainer:  Martin Grenfell &lt;martin_grenfell at msn dot com&gt;
-&quot; Last Change: 20 July, 2008
+&quot; Last Change: 27 Jan, 2009
 &quot; License:     This program is free software. It comes without any warranty,
 &quot;              to the extent permitted by applicable law. You can redistribute
 &quot;              it and/or modify it under the terms of the Do What The Fuck You
@@ -10,7 +10,7 @@
 &quot;              See http://sam.zoy.org/wtfpl/COPYING for more details.
 &quot;
 &quot; ============================================================================
-let s:NERD_tree_version = '2.14.0'
+let s:NERD_tree_version = '3.1.0'
 
 &quot; SECTION: Script init stuff {{{1
 &quot;============================================================
@@ -22,6 +22,11 @@ if v:version &lt; 700
     finish
 endif
 let loaded_nerd_tree = 1
+
+&quot;for line continuation - i.e dont want C in &amp;cpo
+let s:old_cpo = &amp;cpo
+set cpo&amp;vim
+
 &quot;Function: s:initVariable() function {{{2
 &quot;This function is used to initialise a given variable to a given value. The
 &quot;variable is only initialised if it does not exist prior
@@ -49,8 +54,9 @@ call s:initVariable(&quot;g:NERDTreeChDirMode&quot;, 0)
 if !exists(&quot;g:NERDTreeIgnore&quot;)
     let g:NERDTreeIgnore = ['\~$']
 endif
-call s:initVariable(&quot;g:NERDTreeHighlightCursorline&quot;, 1)
 call s:initVariable(&quot;g:NERDTreeBookmarksFile&quot;, expand('$HOME') . '/.NERDTreeBookmarks')
+call s:initVariable(&quot;g:NERDTreeHighlightCursorline&quot;, 1)
+call s:initVariable(&quot;g:NERDTreeHijackNetrw&quot;, 1)
 call s:initVariable(&quot;g:NERDTreeMouseMode&quot;, 1)
 call s:initVariable(&quot;g:NERDTreeNotificationThreshold&quot;, 100)
 call s:initVariable(&quot;g:NERDTreeQuitOnOpen&quot;, 0)
@@ -73,6 +79,7 @@ endif
 &quot;once here
 let s:NERDTreeSortStarIndex = index(g:NERDTreeSortOrder, '*')
 
+call s:initVariable(&quot;g:NERDTreeStatusline&quot;, &quot;%{b:NERDTreeRoot.path.strForOS(0)}&quot;)
 call s:initVariable(&quot;g:NERDTreeWinPos&quot;, &quot;left&quot;)
 call s:initVariable(&quot;g:NERDTreeWinSize&quot;, 31)
 
@@ -109,26 +116,27 @@ call s:initVariable(&quot;g:NERDTreeMapOpenExpl&quot;, &quot;e&quot;)
 call s:initVariable(&quot;g:NERDTreeMapOpenInTab&quot;, &quot;t&quot;)
 call s:initVariable(&quot;g:NERDTreeMapOpenInTabSilent&quot;, &quot;T&quot;)
 call s:initVariable(&quot;g:NERDTreeMapOpenRecursively&quot;, &quot;O&quot;)
-call s:initVariable(&quot;g:NERDTreeMapOpenSplit&quot;, &quot;&lt;tab&gt;&quot;)
+call s:initVariable(&quot;g:NERDTreeMapOpenSplit&quot;, &quot;i&quot;)
+call s:initVariable(&quot;g:NERDTreeMapOpenVSplit&quot;, &quot;s&quot;)
 call s:initVariable(&quot;g:NERDTreeMapPreview&quot;, &quot;g&quot; . NERDTreeMapActivateNode)
 call s:initVariable(&quot;g:NERDTreeMapPreviewSplit&quot;, &quot;g&quot; . NERDTreeMapOpenSplit)
+call s:initVariable(&quot;g:NERDTreeMapPreviewVSplit&quot;, &quot;g&quot; . NERDTreeMapOpenVSplit)
 call s:initVariable(&quot;g:NERDTreeMapQuit&quot;, &quot;q&quot;)
 call s:initVariable(&quot;g:NERDTreeMapRefresh&quot;, &quot;r&quot;)
 call s:initVariable(&quot;g:NERDTreeMapRefreshRoot&quot;, &quot;R&quot;)
 call s:initVariable(&quot;g:NERDTreeMapToggleBookmarks&quot;, &quot;B&quot;)
 call s:initVariable(&quot;g:NERDTreeMapToggleFiles&quot;, &quot;F&quot;)
 call s:initVariable(&quot;g:NERDTreeMapToggleFilters&quot;, &quot;f&quot;)
-call s:initVariable(&quot;g:NERDTreeMapToggleHidden&quot;, &quot;H&quot;)
+call s:initVariable(&quot;g:NERDTreeMapToggleHidden&quot;, &quot;I&quot;)
 call s:initVariable(&quot;g:NERDTreeMapUpdir&quot;, &quot;u&quot;)
 call s:initVariable(&quot;g:NERDTreeMapUpdirKeepOpen&quot;, &quot;U&quot;)
 
 &quot;SECTION: Script level variable declaration{{{2
 let s:escape_chars =  &quot; \\`\|\&quot;#%&amp;,?()\*^&lt;&gt;&quot;
-let s:NERDTreeWinName = '_NERD_tree_'
+let s:NERDTreeBufName = 'NERD_tree_'
 
 let s:tree_wid = 2
-let s:tree_markup_reg = '[ \-+~`|]'
-let s:tree_markup_reg_neg = '[^ \-+~`|]'
+let s:tree_markup_reg = '^[ `|]*[\-+~]'
 let s:tree_up_dir_line = '.. (up a dir)'
 
 let s:os_slash = '/'
@@ -136,20 +144,32 @@ if s:running_windows
     let s:os_slash = '\'
 endif
 
+&quot;the number to add to the nerd tree buffer name to make the buf name unique
+let s:next_buffer_number = 1
 
 &quot; SECTION: Commands {{{1
 &quot;============================================================
 &quot;init the command that users start the nerd tree with
-command! -n=? -complete=dir NERDTree :call s:initNerdTree('&lt;args&gt;')
-command! -n=? -complete=dir NERDTreeToggle :call s:toggle('&lt;args&gt;')
-command! -n=0 NERDTreeClose :call s:closeTreeIfOpen()
-command! -n=1 -complete=customlist,s:completeBookmarks NERDTreeFromBookmark call s:initNerdTree('&lt;args&gt;')
+command! -n=? -complete=dir -bar NERDTree :call s:initNerdTree('&lt;args&gt;')
+command! -n=? -complete=dir -bar NERDTreeToggle :call s:toggle('&lt;args&gt;')
+command! -n=0 -bar NERDTreeClose :call s:closeTreeIfOpen()
+command! -n=1 -complete=customlist,s:completeBookmarks -bar NERDTreeFromBookmark call s:initNerdTree('&lt;args&gt;')
+command! -n=0 -complete=customlist,s:completeNERDTreeMirrors -bar NERDTreeMirror call s:initNerdTreeMirror()
 &quot; SECTION: Auto commands {{{1
 &quot;============================================================
-&quot;Save the cursor position whenever we close the nerd tree
-exec &quot;autocmd BufWinLeave *&quot;. s:NERDTreeWinName .&quot; call &lt;SID&gt;saveScreenState()&quot;
-&quot;cache bookmarks when vim loads
-autocmd VimEnter * call s:Bookmark.CacheBookmarks(0)
+augroup NERDTree
+    &quot;Save the cursor position whenever we close the nerd tree
+    exec &quot;autocmd BufWinLeave *&quot;. s:NERDTreeBufName .&quot; call &lt;SID&gt;saveScreenState()&quot;
+    &quot;cache bookmarks when vim loads
+    autocmd VimEnter * call s:Bookmark.CacheBookmarks(0)
+augroup END
+
+if g:NERDTreeHijackNetrw
+    augroup NERDTreeHijackNetrw
+        autocmd VimEnter * silent! autocmd! FileExplorer
+        au BufEnter,VimEnter * call s:checkForBrowse(expand(&quot;&lt;amatch&gt;&quot;))
+    augroup END
+endif
 
 &quot;SECTION: Classes {{{1
 &quot;============================================================
@@ -161,7 +181,7 @@ let s:Bookmark = {}
 &quot; 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
+        if i.name ==# a:name
             let i.path = a:path
             return
         endif
@@ -185,7 +205,7 @@ function! s:Bookmark.BookmarkExistsFor(name)
     try
         call s:Bookmark.BookmarkFor(a:name)
         return 1
-    catch /NERDTree.BookmarkNotFound/
+    catch /^NERDTree.BookmarkNotFoundError/
         return 0
     endtry
 endfunction
@@ -194,11 +214,11 @@ endfunction
 &quot; bookmark is found
 function! s:Bookmark.BookmarkFor(name)
     for i in s:Bookmark.Bookmarks()
-        if i.name == a:name
+        if i.name ==# a:name
             return i
         endif
     endfor
-    throw &quot;NERDTree.BookmarkNotFound exception: no bookmark found for name: \&quot;&quot;. a:name  .'&quot;'
+    throw &quot;NERDTree.BookmarkNotFoundError: no bookmark found for name: \&quot;&quot;. a:name  .'&quot;'
 endfunction
 &quot; Function: Bookmark.BookmarkNames()   {{{3
 &quot; Class method to return an array of all bookmark names
@@ -232,7 +252,7 @@ function! s:Bookmark.CacheBookmarks(silent)
                 try
                     let bookmark = s:Bookmark.New(name, s:Path.New(path))
                     call add(g:NERDTreeBookmarks, bookmark)
-                catch /NERDTree.Path.InvalidArguments/
+                catch /^NERDTree.InvalidArgumentsError/
                     call add(g:NERDTreeInvalidBookmarks, i)
                     let invalidBookmarksFound += 1
                 endtry
@@ -267,7 +287,7 @@ function! s:Bookmark.delete()
     let node = {}
     try
         let node = self.getNode(1)
-    catch /NERDTree.BookmarkedNodeNotFound/
+    catch /^NERDTree.BookmarkedNodeNotFoundError/
     endtry
     call remove(s:Bookmark.Bookmarks(), index(s:Bookmark.Bookmarks(), self))
     if !empty(node)
@@ -282,10 +302,10 @@ endfunction
 &quot; searchFromAbsoluteRoot: specifies whether we should search from the current
 &quot; tree root, or the highest cached node
 function! s:Bookmark.getNode(searchFromAbsoluteRoot)
-    let searchRoot = a:searchFromAbsoluteRoot ? s:TreeDirNode.AbsoluteTreeRoot() : t:NERDTreeRoot
+    let searchRoot = a:searchFromAbsoluteRoot ? s:TreeDirNode.AbsoluteTreeRoot() : b:NERDTreeRoot
     let targetNode = searchRoot.findNode(self.path)
     if empty(targetNode)
-        throw &quot;NERDTree.BookmarkedNodeNotFound no node was found for bookmark: &quot; . self.name
+        throw &quot;NERDTree.BookmarkedNodeNotFoundError: no node was found for bookmark: &quot; . self.name
     endif
     return targetNode
 endfunction
@@ -309,7 +329,7 @@ endfunction
 function! s:Bookmark.mustExist()
     if !self.path.exists()
         call s:Bookmark.CacheBookmarks(1)
-        throw &quot;NERDTree.BookmarkPointsToInvalidLocation exception: the bookmark \&quot;&quot;.
+        throw &quot;NERDTree.BookmarkPointsToInvalidLocationError: the bookmark \&quot;&quot;.
             \ self.name .&quot;\&quot; points to a non existing location: \&quot;&quot;. self.path.strForOS(0)
     endif
 endfunction
@@ -317,7 +337,7 @@ endfunction
 &quot; Create a new bookmark object with the given name and path object
 function! s:Bookmark.New(name, path)
     if a:name =~ ' '
-        throw &quot;NERDTree.IllegalBookmarkName illegal name:&quot; . a:name
+        throw &quot;NERDTree.IllegalBookmarkNameError: illegal name:&quot; . a:name
     endif
 
     let newBookmark = copy(self)
@@ -355,13 +375,13 @@ endfunction
 function! s:Bookmark.toRoot()
     if self.validate()
         try
-            let targetNode = s:Bookmark.GetNodeForName(self.name, 1)
-        catch /NERDTree.BookmarkedNodeNotFound/
+            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 s:putCursorOnNode(targetNode, 0, 0)
+        call targetNode.putCursorHere(0, 0)
     endif
 endfunction
 &quot; FUNCTION: Bookmark.ToRoot(name) {{{3
@@ -412,7 +432,7 @@ function! s:TreeFileNode.bookmark(name)
     try
         let oldMarkedNode = s:Bookmark.GetNodeForName(a:name, 1)
         call oldMarkedNode.path.cacheDisplayString()
-    catch /NERDTree.Bookmark\(DoesntExist\|NotFound\)/
+    catch /^NERDTree.BookmarkNotFoundError/
     endtry
 
     call s:Bookmark.AddBookmark(a:name, self.path)
@@ -425,7 +445,7 @@ function! s:TreeFileNode.cacheParent()
     if empty(self.parent)
         let parentPath = self.path.getParent()
         if parentPath.equals(self.path)
-            throw &quot;NERDTree.CannotCacheParent exception: already at root&quot;
+            throw &quot;NERDTree.CannotCacheParentError: already at root&quot;
         endif
         let self.parent = s:TreeFileNode.New(parentPath)
     endif
@@ -455,7 +475,7 @@ endfunction
 function! s:TreeFileNode.copy(dest)
     call self.path.copy(a:dest)
     let newPath = s:Path.New(a:dest)
-    let parent = t:NERDTreeRoot.findNode(newPath.getParent())
+    let parent = b:NERDTreeRoot.findNode(newPath.getParent())
     if !empty(parent)
         call parent.refresh()
     endif
@@ -469,6 +489,83 @@ function! s:TreeFileNode.delete()
     call self.parent.removeChild(self)
 endfunction
 
+&quot;FUNCTION: TreeFileNode.renderToString {{{3
+&quot;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
+
+
+&quot;Args:
+&quot;depth: the current depth in the tree for this call
+&quot;drawText: 1 if we should actually draw the line for this node (if 0 then the
+&quot;child nodes are rendered only)
+&quot;vertMap: a binary array that indicates whether a vertical bar should be draw
+&quot;for each depth in the tree
+&quot;isLastChild:true if this curNode is the last child of its parent
+function! s:TreeFileNode._renderToString(depth, drawText, vertMap, isLastChild)
+    let output = &quot;&quot;
+    if a:drawText ==# 1
+
+        let treeParts = ''
+
+        &quot;get all the leading spaces and vertical tree parts for this line
+        if a:depth &gt; 1
+            for j in a:vertMap[0:-2]
+                if j ==# 1
+                    let treeParts = treeParts . '| '
+                else
+                    let treeParts = treeParts . '  '
+                endif
+            endfor
+        endif
+
+        &quot;get the last vertical tree part for this line which will be different
+        &quot;if this node is the last child of its parent
+        if a:isLastChild
+            let treeParts = treeParts . '`'
+        else
+            let treeParts = treeParts . '|'
+        endif
+
+
+        &quot;smack the appropriate dir/file symbol on the line before the file/dir
+        &quot;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.strDisplay()
+
+        let output = output . line . &quot;\n&quot;
+    endif
+
+    &quot;if the node is an open dir, draw its children
+    if self.path.isDirectory ==# 1 &amp;&amp; self.isOpen ==# 1
+
+        let childNodesToDraw = self.getVisibleChildren()
+        if len(childNodesToDraw) &gt; 0
+
+            &quot;draw all the nodes children except the last
+            let lastIndx = len(childNodesToDraw)-1
+            if lastIndx &gt; 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
+
+            &quot;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
 &quot;FUNCTION: TreeFileNode.equals(treenode) {{{3
 &quot;
 &quot;Compares this treenode to the input treenode and returns 1 if they are the
@@ -480,7 +577,7 @@ endfunction
 &quot;Args:
 &quot;treenode: the other treenode to compare to
 function! s:TreeFileNode.equals(treenode)
-    return self.path.str(1) == a:treenode.path.str(1)
+    return self.path.str(1) ==# a:treenode.path.str(1)
 endfunction
 
 &quot;FUNCTION: TreeFileNode.findNode(path) {{{3
@@ -495,7 +592,7 @@ function! s:TreeFileNode.findNode(path)
     endif
     return {}
 endfunction
-&quot;FUNCTION: TreeFileNode.findOpenDirSiblingWithChildren(direction) {{{3
+&quot;FUNCTION: TreeFileNode.findOpenDirSiblingWithVisibleChildren(direction) {{{3
 &quot;
 &quot;Finds the next sibling for this node in the indicated direction. This sibling
 &quot;must be a directory and may/may not have children as specified.
@@ -505,7 +602,7 @@ endfunction
 &quot;
 &quot;Return:
 &quot;a treenode object or {} if no appropriate sibling could be found
-function! s:TreeFileNode.findOpenDirSiblingWithChildren(direction)
+function! s:TreeFileNode.findOpenDirSiblingWithVisibleChildren(direction)
     &quot;if we have no parent then we can have no siblings
     if self.parent != {}
         let nextSibling = self.findSibling(a:direction)
@@ -538,7 +635,7 @@ function! s:TreeFileNode.findSibling(direction)
 
         if siblingIndx != -1
             &quot;move a long to the next potential sibling node
-            let siblingIndx = a:direction == 1 ? siblingIndx+1 : siblingIndx-1
+            let siblingIndx = a:direction ==# 1 ? siblingIndx+1 : siblingIndx-1
 
             &quot;keep moving along to the next sibling till we find one that is valid
             let numSiblings = self.parent.getChildCount()
@@ -546,12 +643,12 @@ function! s:TreeFileNode.findSibling(direction)
 
                 &quot;if the next node is not an ignored node (i.e. wont show up in the
                 &quot;view) then return it
-                if self.parent.children[siblingIndx].path.ignore() == 0
+                if self.parent.children[siblingIndx].path.ignore() ==# 0
                     return self.parent.children[siblingIndx]
                 endif
 
                 &quot;go to next node
-                let siblingIndx = a:direction == 1 ? siblingIndx+1 : siblingIndx-1
+                let siblingIndx = a:direction ==# 1 ? siblingIndx+1 : siblingIndx-1
             endwhile
         endif
     endif
@@ -559,38 +656,109 @@ function! s:TreeFileNode.findSibling(direction)
     return {}
 endfunction
 
+&quot;FUNCTION: TreeFileNode.getLineNum(){{{3
+&quot;returns the line number this node is rendered on, or -1 if it isnt rendered
+function! s:TreeFileNode.getLineNum()
+    &quot;if the node is the root then return the root line no.
+    if self.isRoot()
+        return s:TreeFileNode.GetRootLineNum()
+    endif
+
+    let totalLines = line(&quot;$&quot;)
+
+    &quot;the path components we have matched so far
+    let pathcomponents = [substitute(b:NERDTreeRoot.path.str(0), '/ *$', '', '')]
+    &quot;the index of the component we are searching for
+    let curPathComponent = 1
+
+    let fullpath = self.path.str(0)
+
+
+    let lnum = s:TreeFileNode.GetRootLineNum()
+    while lnum &gt; 0
+        let lnum = lnum + 1
+        &quot;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
+
+&quot;FUNCTION: TreeFileNode.GetRootLineNum(){{{3
+&quot;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
+
+&quot;FUNCTION: TreeFileNode.GetSelected() {{{3
+&quot;gets the treenode that the cursor is currently over
+function! s:TreeFileNode.GetSelected()
+    try
+        let path = s:getPath(line(&quot;.&quot;))
+        if path ==# {}
+            return {}
+        endif
+        return b:NERDTreeRoot.findNode(path)
+    catch /NERDTree/
+        return {}
+    endtry
+endfunction
 &quot;FUNCTION: TreeFileNode.isVisible() {{{3
 &quot;returns 1 if this node should be visible according to the tree filters and
 &quot;hidden file filters (and their on/off status)
 function! s:TreeFileNode.isVisible()
     return !self.path.ignore()
 endfunction
-
-
 &quot;FUNCTION: TreeFileNode.isRoot() {{{3
-&quot;returns 1 if this node is t:NERDTreeRoot
+&quot;returns 1 if this node is b:NERDTreeRoot
 function! s:TreeFileNode.isRoot()
-    if !s:treeExistsForTab()
-        throw &quot;NERDTree.TreeFileNode.IsRoot exception: No tree exists for the current tab&quot;
+    if !s:treeExistsForBuf()
+        throw &quot;NERDTree.NoTreeError: No tree exists for the current buffer&quot;
     endif
-    return self.equals(t:NERDTreeRoot)
+
+    return self.equals(b:NERDTreeRoot)
 endfunction
 
 &quot;FUNCTION: TreeFileNode.makeRoot() {{{3
 &quot;Make this node the root of the tree
 function! s:TreeFileNode.makeRoot()
     if self.path.isDirectory
-        let t:NERDTreeRoot = self
+        let b:NERDTreeRoot = self
     else
         call self.cacheParent()
-        let t:NERDTreeRoot = self.parent
+        let b:NERDTreeRoot = self.parent
     endif
 
-    call t:NERDTreeRoot.open()
+    call b:NERDTreeRoot.open()
 
     &quot;change dir to the dir of the new root if instructed to
-    if g:NERDTreeChDirMode == 2
-        exec &quot;cd &quot; . t:NERDTreeRoot.path.strForEditCmd()
+    if g:NERDTreeChDirMode ==# 2
+        exec &quot;cd &quot; . b:NERDTreeRoot.path.strForEditCmd()
     endif
 endfunction
 &quot;FUNCTION: TreeFileNode.New(path) {{{3
@@ -610,6 +778,157 @@ function! s:TreeFileNode.New(path)
     endif
 endfunction
 
+&quot;FUNCTION: TreeFileNode.open() {{{3
+&quot;Open the file represented by the given node in the current window, splitting
+&quot;the window if needed
+&quot;
+&quot;ARGS:
+&quot;treenode: file node to open
+function! s:TreeFileNode.open()
+    if b:NERDTreeType ==# &quot;secondary&quot;
+        exec 'edit ' . self.path.strForEditCmd()
+        return
+    endif
+
+    &quot;if the file is already open in this tab then just stick the cursor in it
+    let winnr = bufwinnr('^' . self.path.strForOS(0) . '$')
+    if winnr != -1
+        call s:exec(winnr . &quot;wincmd w&quot;)
+
+    else
+        if !s:isWindowUsable(winnr(&quot;#&quot;)) &amp;&amp; s:firstNormalWindow() ==# -1
+            call self.openSplit()
+        else
+            try
+                if !s:isWindowUsable(winnr(&quot;#&quot;))
+                    call s:exec(s:firstNormalWindow() . &quot;wincmd w&quot;)
+                else
+                    call s:exec('wincmd p')
+                endif
+                exec (&quot;edit &quot; . self.path.strForEditCmd())
+            catch /^Vim\%((\a\+)\)\=:E37/
+                call s:putCursorInTreeWin()
+                throw &quot;NERDTree.FileAlreadyOpenAndModifiedError: &quot;. self.path.str(0) .&quot; is already open and modified.&quot;
+            catch /^Vim\%((\a\+)\)\=:/
+                echo v:exception
+            endtry
+        endif
+    endif
+endfunction
+&quot;FUNCTION: TreeFileNode.openSplit() {{{3
+&quot;Open this node in a new window
+function! s:TreeFileNode.openSplit()
+
+    if b:NERDTreeType ==# &quot;secondary&quot;
+        exec &quot;split &quot; . self.path.strForEditCmd()
+        return
+    endif
+
+    &quot; Save the user's settings for splitbelow and splitright
+    let savesplitbelow=&amp;splitbelow
+    let savesplitright=&amp;splitright
+
+    &quot; 'there' will be set to a command to move from the split window
+    &quot; back to the explorer window
+    &quot;
+    &quot; 'back' will be set to a command to move from the explorer window
+    &quot; back to the newly split window
+    &quot;
+    &quot; 'right' and 'below' will be set to the settings needed for
+    &quot; splitbelow and splitright IF the explorer is the only window.
+    &quot;
+    let there= g:NERDTreeWinPos ==# &quot;left&quot; ? &quot;wincmd h&quot; : &quot;wincmd l&quot;
+    let back = g:NERDTreeWinPos ==# &quot;left&quot; ? &quot;wincmd l&quot; : &quot;wincmd h&quot;
+    let right= g:NERDTreeWinPos ==# &quot;left&quot;
+    let below=0
+
+    &quot; Attempt to go to adjacent window
+    call s:exec(back)
+
+    let onlyOneWin = (winnr(&quot;$&quot;) ==# 1)
+
+    &quot; If no adjacent window, set splitright and splitbelow appropriately
+    if onlyOneWin
+        let &amp;splitright=right
+        let &amp;splitbelow=below
+    else
+        &quot; found adjacent window - invert split direction
+        let &amp;splitright=!right
+        let &amp;splitbelow=!below
+    endif
+
+    let splitMode = onlyOneWin ? &quot;vertical&quot; : &quot;&quot;
+
+    &quot; Open the new window
+    try
+        exec(splitMode.&quot; sp &quot; . self.path.strForEditCmd())
+    catch /^Vim\%((\a\+)\)\=:E37/
+        call s:putCursorInTreeWin()
+        throw &quot;NERDTree.FileAlreadyOpenAndModifiedError: &quot;. self.path.str(0) .&quot; is already open and modified.&quot;
+    catch /^Vim\%((\a\+)\)\=:/
+        &quot;do nothing
+    endtry
+
+    &quot;resize the tree window if no other window was open before
+    if onlyOneWin
+        let size = exists(&quot;b:NERDTreeOldWindowSize&quot;) ? b:NERDTreeOldWindowSize : g:NERDTreeWinSize
+        call s:exec(there)
+        exec(&quot;silent &quot;. splitMode .&quot; resize &quot;. size)
+        call s:exec('wincmd p')
+    endif
+
+    &quot; Restore splitmode settings
+    let &amp;splitbelow=savesplitbelow
+    let &amp;splitright=savesplitright
+endfunction
+&quot;FUNCTION: TreeFileNode.openVSplit() {{{3
+&quot;Open this node in a new vertical window
+function! s:TreeFileNode.openVSplit()
+    if b:NERDTreeType ==# &quot;secondary&quot;
+        exec &quot;vnew &quot; . self.path.strForEditCmd()
+        return
+    endif
+
+    let winwidth = winwidth(&quot;.&quot;)
+    if winnr(&quot;$&quot;)==#1
+        let winwidth = g:NERDTreeWinSize
+    endif
+
+    call s:exec(&quot;wincmd p&quot;)
+    exec &quot;vnew &quot; . self.path.strForEditCmd()
+
+    &quot;resize the nerd tree back to the original size
+    call s:putCursorInTreeWin()
+    exec(&quot;silent vertical resize &quot;. winwidth)
+    call s:exec('wincmd p')
+endfunction
+&quot;FUNCTION: TreeFileNode.putCursorHere(isJump, recurseUpward){{{3
+&quot;Places the cursor on the line number this node is rendered on
+&quot;
+&quot;Args:
+&quot;isJump: 1 if this cursor movement should be counted as a jump by vim
+&quot;recurseUpward: try to put the cursor on the parent if the this node isnt
+&quot;visible
+function! s:TreeFileNode.putCursorHere(isJump, recurseUpward)
+    let ln = self.getLineNum()
+    if ln != -1
+        if a:isJump
+            mark '
+        endif
+        call cursor(ln, col(&quot;.&quot;))
+    else
+        if a:recurseUpward
+            let node = self
+            while node != {} &amp;&amp; node.getLineNum() ==# -1
+                let node = node.parent
+                call node.open()
+            endwhile
+            call s:renderView()
+            call node.putCursorHere(a:isJump, 0)
+        endif
+    endif
+endfunction
+
 &quot;FUNCTION: TreeFileNode.refresh() {{{3
 function! s:TreeFileNode.refresh()
     call self.path.refresh()
@@ -622,7 +941,7 @@ function! s:TreeFileNode.rename(newName)
     call self.parent.removeChild(self)
 
     let parentPath = self.path.getPathTrunk()
-    let newParent = t:NERDTreeRoot.findNode(parentPath)
+    let newParent = b:NERDTreeRoot.findNode(parentPath)
 
     if newParent != {}
         call newParent.createChild(self.path, 1)
@@ -649,7 +968,7 @@ let s:TreeDirNode = copy(s:TreeFileNode)
 &quot;FUNCTION: TreeDirNode.AbsoluteTreeRoot(){{{3
 &quot;class method that returns the highest cached ancestor of the current root
 function! s:TreeDirNode.AbsoluteTreeRoot()
-    let currentNode = t:NERDTreeRoot
+    let currentNode = b:NERDTreeRoot
     while currentNode.parent != {}
         let currentNode = currentNode.parent
     endwhile
@@ -708,11 +1027,12 @@ endfunction
 &quot;
 &quot;Args:
 &quot;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(1), self.path.str(1), 0) == -1
+    if stridx(a:path.str(1), self.path.str(1), 0) ==# -1
         return {}
     endif
 
@@ -726,7 +1046,6 @@ function! s:TreeDirNode.findNode(path)
     endif
     return {}
 endfunction
-
 &quot;FUNCTION: TreeDirNode.getChildCount() {{{3
 &quot;Returns the number of children this node has
 function! s:TreeDirNode.getChildCount()
@@ -742,12 +1061,12 @@ endfunction
 &quot;Args:
 &quot;path: a path object
 function! s:TreeDirNode.getChild(path)
-    if stridx(a:path.str(1), self.path.str(1), 0) == -1
+    if stridx(a:path.str(1), self.path.str(1), 0) ==# -1
         return {}
     endif
 
     let index = self.getChildIndex(a:path)
-    if index == -1
+    if index ==# -1
         return {}
     else
         return self.children[index]
@@ -764,7 +1083,7 @@ endfunction
 function! s:TreeDirNode.getChildByIndex(indx, visible)
     let array_to_search = a:visible? self.getVisibleChildren() : self.children
     if a:indx &gt; len(array_to_search)
-        throw &quot;NERDTree.TreeDirNode.InvalidArguments exception. Index is out of bounds.&quot;
+        throw &quot;NERDTree.InvalidArgumentsError: Index is out of bounds.&quot;
     endif
     return array_to_search[a:indx]
 endfunction
@@ -778,7 +1097,7 @@ endfunction
 &quot;Args:
 &quot;path: a path object
 function! s:TreeDirNode.getChildIndex(path)
-    if stridx(a:path.str(1), self.path.str(1), 0) == -1
+    if stridx(a:path.str(1), self.path.str(1), 0) ==# -1
         return -1
     endif
 
@@ -789,9 +1108,9 @@ function! s:TreeDirNode.getChildIndex(path)
         let mid = (a+z)/2
         let diff = a:path.compareTo(self.children[mid].path)
 
-        if diff == -1
+        if diff ==# -1
             let z = mid
-        elseif diff == 1
+        elseif diff ==# 1
             let a = mid+1
         else
             return mid
@@ -800,6 +1119,19 @@ function! s:TreeDirNode.getChildIndex(path)
     return -1
 endfunction
 
+&quot;FUNCTION: TreeDirNode.GetSelected() {{{3
+&quot;Returns the current node if it is a dir node, or else returns the current
+&quot;nodes parent
+unlet s:TreeDirNode.GetSelected
+function! s:TreeDirNode.GetSelected()
+    let currentDir = s:TreeFileNode.GetSelected()
+    if currentDir != {} &amp;&amp; !currentDir.isRoot()
+        if currentDir.path.isDirectory ==# 0
+            let currentDir = currentDir.parent
+        endif
+    endif
+    return currentDir
+endfunction
 &quot;FUNCTION: TreeDirNode.getVisibleChildCount() {{{3
 &quot;Returns the number of visible children this node has
 function! s:TreeDirNode.getVisibleChildCount()
@@ -814,7 +1146,7 @@ endfunction
 function! s:TreeDirNode.getVisibleChildren()
     let toReturn = []
     for i in self.children
-        if i.path.ignore() == 0
+        if i.path.ignore() ==# 0
             call add(toReturn, i)
         endif
     endfor
@@ -824,7 +1156,7 @@ endfunction
 &quot;FUNCTION: TreeDirNode.hasVisibleChildren() {{{3
 &quot;returns 1 if this node has any childre, 0 otherwise..
 function! s:TreeDirNode.hasVisibleChildren()
-    return self.getChildCount() != 0
+    return self.getVisibleChildCount() != 0
 endfunction
 
 &quot;FUNCTION: TreeDirNode._initChildren() {{{3
@@ -860,7 +1192,7 @@ function! s:TreeDirNode._initChildren(silent)
             try
                 let path = s:Path.New(i)
                 call self.createChild(path, 0)
-            catch /^NERDTree.Path.\(InvalidArguments\|InvalidFiletype\)/
+            catch /^NERDTree.\(InvalidArguments\|InvalidFiletype\)Error/
                 let invalidFilesFound += 1
             endtry
         endif
@@ -882,9 +1214,10 @@ endfunction
 &quot;
 &quot;Args:
 &quot;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 &quot;NERDTree.TreeDirNode.InvalidArguments exception. A TreeDirNode object must be instantiated with a directory Path object.&quot;
+        throw &quot;NERDTree.InvalidArgumentsError: A TreeDirNode object must be instantiated with a directory Path object.&quot;
     endif
 
     let newTreeNode = copy(self)
@@ -901,15 +1234,29 @@ endfunction
 &quot;Reads in all this nodes children
 &quot;
 &quot;Return: the number of child nodes read
+unlet s:TreeDirNode.open
 function! s:TreeDirNode.open()
     let self.isOpen = 1
-    if self.children == []
+    if self.children ==# []
         return self._initChildren(0)
     else
         return 0
     endif
 endfunction
 
+&quot; FUNCTION: TreeDirNode.openExplorer() {{{3
+&quot; opens an explorer window for this node in the previous window (could be a
+&quot; nerd tree or a netrw)
+function! s:TreeDirNode.openExplorer()
+    let oldwin = winnr()
+    call s:exec('wincmd p')
+    if oldwin ==# winnr() || (&amp;modified &amp;&amp; s:bufInWindows(winbufnr(winnr())) &lt; 2)
+        call s:exec('wincmd p')
+        call self.openSplit()
+    else
+        exec (&quot;silent edit &quot; . self.path.strForEditCmd())
+    endif
+endfunction
 &quot;FUNCTION: TreeDirNode.openRecursively() {{{3
 &quot;Opens this treenode and all of its children whose paths arent 'ignored'
 &quot;because of the file filters.
@@ -928,14 +1275,14 @@ endfunction
 &quot;Args:
 &quot;forceOpen: 1 if this node should be opened regardless of file filters
 function! s:TreeDirNode._openRecursively2(forceOpen)
-    if self.path.ignore() == 0 || a:forceOpen
+    if self.path.ignore() ==# 0 || a:forceOpen
         let self.isOpen = 1
-        if self.children == []
+        if self.children ==# []
             call self._initChildren(1)
         endif
 
         for i in self.children
-            if i.path.isDirectory == 1
+            if i.path.isDirectory ==# 1
                 call i._openRecursively2(0)
             endif
         endfor
@@ -943,6 +1290,7 @@ function! s:TreeDirNode._openRecursively2(forceOpen)
 endfunction
 
 &quot;FUNCTION: TreeDirNode.refresh() {{{3
+unlet s:TreeDirNode.refresh
 function! s:TreeDirNode.refresh()
     call self.path.refresh()
 
@@ -973,7 +1321,7 @@ function! s:TreeDirNode.refresh()
                     endif
 
 
-                catch /^NERDTree.InvalidArguments/
+                catch /^NERDTree.InvalidArgumentsError/
                     let invalidFilesFound = 1
                 endtry
             endif
@@ -996,7 +1344,7 @@ endfunction
 &quot;Args:
 &quot;treenode: the node to remove
 &quot;
-&quot;Throws a NERDTree.TreeDirNode exception if the given treenode is not found
+&quot;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)
@@ -1005,7 +1353,7 @@ function! s:TreeDirNode.removeChild(treenode)
         endif
     endfor
 
-    throw &quot;NERDTree.TreeDirNode exception: child node was not found&quot;
+    throw &quot;NERDTree.ChildNotFoundError: child node was not found&quot;
 endfunction
 
 &quot;FUNCTION: TreeDirNode.sortChildren() {{{3
@@ -1021,7 +1369,7 @@ endfunction
 &quot;FUNCTION: TreeDirNode.toggleOpen() {{{3
 &quot;Opens this directory if it is closed and vice versa
 function! s:TreeDirNode.toggleOpen()
-    if self.isOpen == 1
+    if self.isOpen ==# 1
         call self.close()
     else
         call self.open()
@@ -1048,12 +1396,28 @@ endfunction
 &quot;CLASS: Path {{{2
 &quot;============================================================
 let s:Path = {}
+&quot;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:os_slash . a:str
+    endif
+
+    return toReturn
+endfunction
 &quot;FUNCTION: Path.bookmarkNames() {{{3
 function! s:Path.bookmarkNames()
-    if !exists(&quot;self.bookmarkNames&quot;)
+    if !exists(&quot;self._bookmarkNames&quot;)
         call self.cacheDisplayString()
     endif
-    return self.bookmarkNames
+    return self._bookmarkNames
 endfunction
 &quot;FUNCTION: Path.cacheDisplayString() {{{3
 function! s:Path.cacheDisplayString()
@@ -1063,14 +1427,14 @@ function! s:Path.cacheDisplayString()
         let self.cachedDisplayString = self.cachedDisplayString . '*'
     endif
 
-    let self.bookmarkNames = []
+    let self._bookmarkNames = []
     for i in s:Bookmark.Bookmarks()
         if i.path.equals(self)
-            call add(self.bookmarkNames, i.name)
+            call add(self._bookmarkNames, i.name)
         endif
     endfor
-    if !empty(self.bookmarkNames)
-        let self.cachedDisplayString .= ' {' . join(self.bookmarkNames) . '}'
+    if !empty(self._bookmarkNames)
+        let self.cachedDisplayString .= ' {' . join(self._bookmarkNames) . '}'
     endif
 
     if self.isSymLink
@@ -1084,7 +1448,7 @@ endfunction
 &quot;FUNCTION: Path.changeToDir() {{{3
 function! s:Path.changeToDir()
     let dir = self.strForCd()
-    if self.isDirectory == 0
+    if self.isDirectory ==# 0
         let dir = self.getPathTrunk().strForCd()
     endif
 
@@ -1092,7 +1456,7 @@ function! s:Path.changeToDir()
         execute &quot;cd &quot; . dir
         call s:echo(&quot;CWD is now: &quot; . getcwd())
     catch
-        throw &quot;NERDTree.Path.Change exception: cannot change to &quot; . dir
+        throw &quot;NERDTree.PathChangeError: cannot change CWD to &quot; . dir
     endtry
 endfunction
 
@@ -1111,7 +1475,7 @@ function! s:Path.compareTo(path)
     let thatPath = a:path.getLastPathComponent(1)
 
     &quot;if the paths are the same then clearly we return 0
-    if thisPath == thatPath
+    if thisPath ==# thatPath
         return 0
     endif
 
@@ -1149,7 +1513,7 @@ endfunction
 function! s:Path.Create(fullpath)
     &quot;bail if the a:fullpath already exists
     if isdirectory(a:fullpath) || filereadable(a:fullpath)
-        throw &quot;NERDTree.Path.Exists Exception: Directory Exists: '&quot; . a:fullpath . &quot;'&quot;
+        throw &quot;NERDTree.CreatePathError: Directory Exists: '&quot; . a:fullpath . &quot;'&quot;
     endif
 
     try
@@ -1165,8 +1529,8 @@ function! s:Path.Create(fullpath)
         else
             call writefile([], a:fullpath)
         endif
-    catch /.*/
-        throw &quot;NERDTree.Path Exception: Could not create path: '&quot; . a:fullpath . &quot;'&quot;
+    catch
+        throw &quot;NERDTree.CreatePathError: Could not create path: '&quot; . a:fullpath . &quot;'&quot;
     endtry
 
     return s:Path.New(a:fullpath)
@@ -1180,7 +1544,7 @@ endfunction
 &quot;dest: the location to copy this dir/file to
 function! s:Path.copy(dest)
     if !s:Path.CopyingSupported()
-        throw &quot;NERDTree.Path.CopyingNotSupported Exception: Copying is not supported on this OS&quot;
+        throw &quot;NERDTree.CopyingNotSupportedError: Copying is not supported on this OS&quot;
     endif
 
     let dest = s:Path.WinToUnixPath(a:dest)
@@ -1188,7 +1552,7 @@ function! s:Path.copy(dest)
     let cmd = g:NERDTreeCopyCmd . &quot; &quot; . self.strForOS(0) . &quot; &quot; . dest
     let success = system(cmd)
     if success != 0
-        throw &quot;NERDTree.Path Exception: Could not copy ''&quot;. self.strForOS(0) .&quot;'' to: '&quot; . a:dest . &quot;'&quot;
+        throw &quot;NERDTree.CopyError: Could not copy ''&quot;. self.strForOS(0) .&quot;'' to: '&quot; . a:dest . &quot;'&quot;
     endif
 endfunction
 
@@ -1239,12 +1603,12 @@ function! s:Path.delete()
         let success = system(cmd)
 
         if v:shell_error != 0
-            throw &quot;NERDTree.Path.Deletion Exception: Could not delete directory: '&quot; . self.strForOS(0) . &quot;'&quot;
+            throw &quot;NERDTree.PathDeletionError: Could not delete directory: '&quot; . self.strForOS(0) . &quot;'&quot;
         endif
     else
         let success = delete(self.strForOS(0))
         if success != 0
-            throw &quot;NERDTree.Path.Deletion Exception: Could not delete file: '&quot; . self.str(0) . &quot;'&quot;
+            throw &quot;NERDTree.PathDeletionError: Could not delete file: '&quot; . self.str(0) . &quot;'&quot;
         endif
     endif
 
@@ -1337,7 +1701,7 @@ function! s:Path.ignore()
     let lastPathComponent = self.getLastPathComponent(0)
 
     &quot;filter out the user specified paths to ignore
-    if t:NERDTreeIgnoreEnabled
+    if b:NERDTreeIgnoreEnabled
         for i in g:NERDTreeIgnore
             if lastPathComponent =~ i
                 return 1
@@ -1346,11 +1710,11 @@ function! s:Path.ignore()
     endif
 
     &quot;dont show hidden files unless instructed to
-    if t:NERDTreeShowHidden == 0 &amp;&amp; lastPathComponent =~ '^\.'
+    if b:NERDTreeShowHidden ==# 0 &amp;&amp; lastPathComponent =~ '^\.'
         return 1
     endif
 
-    if t:NERDTreeShowFiles == 0 &amp;&amp; self.isDirectory == 0
+    if b:NERDTreeShowFiles ==# 0 &amp;&amp; self.isDirectory ==# 0
         return 1
     endif
 
@@ -1374,17 +1738,15 @@ endfunction
 &quot;Args:
 &quot;path: the other path obj to compare this with
 function! s:Path.equals(path)
-    return self.str(0) == a:path.str(0)
+    return self.str(0) ==# a:path.str(0)
 endfunction
 
 &quot;FUNCTION: Path.New() {{{3
-&quot;
 &quot;The Constructor for the Path object
-&quot;Throws NERDTree.Path.InvalidArguments exception.
-function! s:Path.New(fullpath)
+function! s:Path.New(path)
     let newPath = copy(self)
 
-    call newPath.readInfoFromDisk(a:fullpath)
+    call newPath.readInfoFromDisk(s:Path.AbsolutePathFor(a:path))
 
     let newPath.cachedDisplayString = &quot;&quot;
 
@@ -1400,21 +1762,20 @@ function! s:Path.readInfoFromDisk(fullpath)
 
     let fullpath = s:Path.WinToUnixPath(a:fullpath)
 
-    if getftype(fullpath) == &quot;fifo&quot;
-        throw &quot;NERDTree.Path.InvalidFiletype Exception: Cant handle FIFO files: &quot; . a:fullpath
+    if getftype(fullpath) ==# &quot;fifo&quot;
+        throw &quot;NERDTree.InvalidFiletypeError: Cant handle FIFO files: &quot; . a:fullpath
     endif
 
     let self.pathSegments = split(fullpath, '/')
 
-
     let self.isReadOnly = 0
     if isdirectory(a:fullpath)
         let self.isDirectory = 1
     elseif filereadable(a:fullpath)
         let self.isDirectory = 0
-        let self.isReadOnly = filewritable(a:fullpath) == 0
+        let self.isReadOnly = filewritable(a:fullpath) ==# 0
     else
-        throw &quot;NERDTree.Path.InvalidArguments Exception: Invalid path = &quot; . a:fullpath
+        throw &quot;NERDTree.InvalidArgumentsError: Invalid path = &quot; . a:fullpath
     endif
 
     let self.isExecutable = 0
@@ -1456,13 +1817,13 @@ endfunction
 &quot;
 &quot;Renames this node on the filesystem
 function! s:Path.rename(newPath)
-    if a:newPath == ''
-        throw &quot;NERDTree.Path.InvalidArguments exception. Invalid newPath for renaming = &quot;. a:newPath
+    if a:newPath ==# ''
+        throw &quot;NERDTree.InvalidArgumentsError: Invalid newPath for renaming = &quot;. a:newPath
     endif
 
     let success =  rename(self.strForOS(0), a:newPath)
     if success != 0
-        throw &quot;NERDTree.Path.Rename Exception: Could not rename: '&quot; . self.strForOS(0) . &quot;'&quot; . 'to:' . a:newPath
+        throw &quot;NERDTree.PathRenameError: Could not rename: '&quot; . self.strForOS(0) . &quot;'&quot; . 'to:' . a:newPath
     endif
     call self.readInfoFromDisk(a:newPath)
 
@@ -1522,7 +1883,7 @@ endfunction
 &quot;Return:
 &quot;a string that can be used in the view to represent this path
 function! s:Path.strDisplay()
-    if self.cachedDisplayString == &quot;&quot;
+    if self.cachedDisplayString ==# &quot;&quot;
         call self.cacheDisplayString()
     endif
 
@@ -1534,12 +1895,27 @@ endfunction
 &quot;Return: the string for this path that is suitable to be used with the :edit
 &quot;command
 function! s:Path.strForEditCmd()
+    let p = self.str(1)
+    let cwd = getcwd()
+
     if s:running_windows
-        return self.strForOS(0)
-    else
-        return self.str(1)
+        let p = tolower(self.strForOS(0))
+        let cwd = tolower(getcwd())
     endif
 
+    let cwd = cwd . s:os_slash
+
+    &quot;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
 &quot;FUNCTION: Path.strForGlob() {{{3
 function! s:Path.strForGlob()
@@ -1632,7 +2008,7 @@ function! s:bufInWindows(bnum)
         if bufnum &lt; 0
             break
         endif
-        if bufnum == a:bnum
+        if bufnum ==# a:bnum
             let cnt = cnt + 1
         endif
         let winnum = winnum + 1
@@ -1640,7 +2016,13 @@ function! s:bufInWindows(bnum)
 
     return cnt
 endfunction &quot; &gt;&gt;&gt;
-
+&quot;FUNCTION: s:checkForBrowse(dir) {{{2
+&quot;inits a secondary nerd tree in the current buffer if appropriate
+function! s:checkForBrowse(dir)
+    if a:dir != '' &amp;&amp; isdirectory(a:dir)
+        call s:initNerdTreeInPlace(a:dir)
+    endif
+endfunction
 &quot;FUNCTION: s:compareBookmarks(first, second) {{{2
 &quot;Compares two bookmarks
 function! s:compareBookmarks(first, second)
@@ -1652,6 +2034,14 @@ endfunction
 function! s:completeBookmarks(A,L,P)
     return filter(s:Bookmark.BookmarkNames(), 'v:val =~ &quot;^' . a:A . '&quot;')
 endfunction
+&quot; FUNCTION: s:exec(cmd) {{{2
+&quot; same as :exec cmd  but eventignore=all is set for the duration
+function! s:exec(cmd)
+    let old_ei = &amp;ei
+    set ei=all
+    exec a:cmd
+    let &amp;ei = old_ei
+endfunction
 &quot;FUNCTION: s:initNerdTree(name) {{{2
 &quot;Initialise the nerd tree for this tab. The tree will start in either the
 &quot;given directory, or the directory associated with the given bookmark
@@ -1663,11 +2053,17 @@ function! s:initNerdTree(name)
     if s:Bookmark.BookmarkExistsFor(a:name)
         let path = s:Bookmark.BookmarkFor(a:name).path
     else
-        let dir = a:name == '' ? expand('%:p:h') : a:name
+        let dir = a:name ==# '' ? getcwd() : a:name
+
+        &quot;hack to get an absolute path if a relative path is given
+        if dir =~ '^\.'
+            let dir = getcwd() . s:os_slash . dir
+        endif
         let dir = resolve(dir)
+
         try
             let path = s:Path.New(dir)
-        catch /NERDTree.Path.InvalidArguments/
+        catch /^NERDTree.InvalidArgumentsError/
             call s:echo(&quot;No bookmark or directory found for: &quot; . a:name)
             return
         endtry
@@ -1682,30 +2078,189 @@ function! s:initNerdTree(name)
         exec 'cd ' . path.strForCd()
     endif
 
-    let t:treeShowHelp = 0
-    let t:NERDTreeIgnoreEnabled = 1
-    let t:NERDTreeShowFiles = g:NERDTreeShowFiles
-    let t:NERDTreeShowHidden = g:NERDTreeShowHidden
-    let t:NERDTreeShowBookmarks = g:NERDTreeShowBookmarks
-
     if s:treeExistsForTab()
         if s:isTreeOpen()
             call s:closeTree()
         endif
-        unlet t:NERDTreeRoot
+        unlet t:NERDTreeBufName
     endif
 
-    let t:NERDTreeRoot = s:TreeDirNode.New(path)
-    call t:NERDTreeRoot.open()
+    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 = &quot;primary&quot;
+
+    call s:renderView()
+    call b:NERDTreeRoot.putCursorHere(0, 0)
+endfunction
+
+&quot;FUNCTION: s:initNerdTreeInPlace(dir) {{{2
+function! s:initNerdTreeInPlace(dir)
+    try
+        let path = s:Path.New(a:dir)
+    catch /^NERDTree.InvalidArgumentsError/
+        call s:echo(&quot;Invalid directory name:&quot; . a:name)
+        return
+    endtry
+
+    &quot;we want the directory buffer to disappear when we do the :edit below
+    setlocal bufhidden=wipe
+
+    let previousBuf = expand(&quot;#&quot;)
+
+    &quot;we need a unique name for each secondary tree buffer to ensure they are
+    &quot;all independent
+    exec &quot;silent edit &quot; . s:nextBufferName()
+
+    let b:NERDTreePreviousBuf = bufnr(previousBuf)
+
+    let b:NERDTreeRoot = s:TreeDirNode.New(path)
+    call b:NERDTreeRoot.open()
+
+    &quot;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 &lt;buffer&gt;
+
+    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 = &quot;secondary&quot;
+
+    call s:bindMappings()
+    setfiletype nerdtree
+    &quot; syntax highlighting
+    if has(&quot;syntax&quot;) &amp;&amp; exists(&quot;g:syntax_on&quot;) &amp;&amp; !has(&quot;syntax_items&quot;)
+        call s:setupSyntaxHighlighting()
+    endif
+
     call s:renderView()
-    call s:putCursorOnNode(t:NERDTreeRoot, 0, 0)
+endfunction
+&quot; FUNCTION: s:initNerdTreeMirror() {{{2
+function! s:initNerdTreeMirror()
+
+    &quot;get the names off all the nerd tree buffers
+    let treeBufNames = []
+    for i in range(1, tabpagenr(&quot;$&quot;))
+        let nextName = s:tabpagevar(i, 'NERDTreeBufName')
+        if nextName != -1 &amp;&amp; (!exists(&quot;t:NERDTreeBufName&quot;) || nextName != t:NERDTreeBufName)
+            call add(treeBufNames, nextName)
+        endif
+    endfor
+    let treeBufNames = s:unique(treeBufNames)
+
+    &quot;map the option names (that the user will be prompted with) to the nerd
+    &quot;tree buffer names
+    let options = {}
+    let i = 0
+    while i &lt; len(treeBufNames)
+        let bufName = treeBufNames[i]
+        let treeRoot = getbufvar(bufName, &quot;NERDTreeRoot&quot;)
+        let options[i+1 . '. ' . treeRoot.path.strForOS(0) . '  (buf name: ' . bufName . ')'] = bufName
+        let i = i + 1
+    endwhile
+
+    &quot;work out which tree to mirror, if there is more than 1 then ask the user
+    let bufferName = ''
+    if len(keys(options)) &gt; 1
+        let choices = [&quot;Choose a tree to mirror&quot;]
+        let choices = extend(choices, sort(keys(options)))
+        let choice = inputlist(choices)
+        if choice &lt; 1 || choice &gt; len(options) || choice ==# ''
+            return
+        endif
+
+        let bufferName = options[keys(options)[choice-1]]
+    elseif len(keys(options)) ==# 1
+        let bufferName = values(options)[0]
+    else
+        call s:echo(&quot;No trees to mirror&quot;)
+        return
+    endif
+
+    if s:treeExistsForTab() &amp;&amp; s:isTreeOpen()
+        call s:closeTree()
+    endif
+
+    let t:NERDTreeBufName = bufferName
+    call s:createTreeWin()
+    exec 'buffer ' .  bufferName
+    if !&amp;hidden
+        call s:renderView()
+    endif
+endfunction
+&quot; FUNCTION: s:nextBufferName() {{{2
+&quot; 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
+&quot; FUNCTION: s:tabpagevar(tabnr, var) {{{2
+function! s:tabpagevar(tabnr, var)
+    let currentTab = tabpagenr()
+    let old_ei = &amp;ei
+    set ei=all
+
+    exec &quot;tabnext &quot; . a:tabnr
+    let v = -1
+    if exists('t:' . a:var)
+        exec 'let v = t:' . a:var
+    endif
+    exec &quot;tabnext &quot; . currentTab
+
+    let &amp;ei = old_ei
+
+    return v
+endfunction
+&quot; Function: s:treeExistsForBuffer()   {{{2
+&quot; Returns 1 if a nerd tree root exists in the current buffer
+function! s:treeExistsForBuf()
+    return exists(&quot;b:NERDTreeRoot&quot;)
 endfunction
 &quot; Function: s:treeExistsForTab()   {{{2
 &quot; Returns 1 if a nerd tree root exists in the current tab
 function! s:treeExistsForTab()
-    return exists(&quot;t:NERDTreeRoot&quot;)
+    return exists(&quot;t:NERDTreeBufName&quot;)
+endfunction
+&quot; Function: s:unique(list)   {{{2
+&quot; 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
 &quot; SECTION: Public Functions {{{1
 &quot;============================================================
@@ -1719,7 +2274,7 @@ endfunction
 &quot;If the cursor is not on a node then an empty dictionary {} is returned.
 function! NERDTreeGetCurrentNode()
     if !s:treeExistsForTab() || !s:isTreeOpen()
-        throw &quot;NERDTree.NoTreeForTab exception: there is no NERD tree open for the current tab&quot;
+        throw &quot;NERDTree.NoTreeForTabError: there is no NERD tree open for the current tab&quot;
     endif
 
     let winnr = winnr()
@@ -1727,10 +2282,10 @@ function! NERDTreeGetCurrentNode()
         call s:putCursorInTreeWin()
     endif
 
-    let treenode = s:getSelectedNode()
+    let treenode = s:TreeFileNode.GetSelected()
 
     if winnr != winnr()
-        wincmd w
+        call s:exec('wincmd w')
     endif
 
     return treenode
@@ -1767,13 +2322,13 @@ endfunction
 &quot;Closes the NERD tree window
 function! s:closeTree()
     if !s:isTreeOpen()
-        throw &quot;NERDTree.view.closeTree exception: no NERDTree is open&quot;
+        throw &quot;NERDTree.NoTreeFoundError: no NERDTree is open&quot;
     endif
 
     if winnr(&quot;$&quot;) != 1
-        execute s:getTreeWinNum() . &quot; wincmd w&quot;
+        call s:exec(s:getTreeWinNum() . &quot; wincmd w&quot;)
         close
-        execute &quot;wincmd p&quot;
+        call s:exec(&quot;wincmd p&quot;)
     else
         :q
     endif
@@ -1798,19 +2353,22 @@ endfunction
 &quot;options etc
 function! s:createTreeWin()
     &quot;create the nerd tree window
-    let splitLocation = (g:NERDTreeWinPos == &quot;top&quot; || g:NERDTreeWinPos == &quot;left&quot;) ? &quot;topleft &quot; : &quot;botright &quot;
-    let splitMode = s:shouldSplitVertically() ? &quot;vertical &quot; : &quot;&quot;
+    let splitLocation = g:NERDTreeWinPos ==# &quot;left&quot; ? &quot;topleft &quot; : &quot;botright &quot;
     let splitSize = g:NERDTreeWinSize
-    let t:NERDTreeWinName = localtime() . s:NERDTreeWinName
-    let cmd = splitLocation . splitMode . splitSize . ' new ' . t:NERDTreeWinName
-    silent! execute cmd
+    silent! exec splitLocation . 'vertical ' . splitSize . ' new'
+
+    if !exists('t:NERDTreeBufName')
+        let t:NERDTreeBufName = s:nextBufferName()
+        silent! exec &quot;edit &quot; . t:NERDTreeBufName
+    else
+        silent! exec &quot;buffer &quot; . t:NERDTreeBufName
+    endif
 
     setlocal winfixwidth
 
     &quot;throwaway buffer options
     setlocal noswapfile
     setlocal buftype=nofile
-    setlocal bufhidden=delete
     setlocal nowrap
     setlocal foldcolumn=0
     setlocal nobuflisted
@@ -1827,10 +2385,7 @@ function! s:createTreeWin()
         setlocal cursorline
     endif
 
-
-    &quot; for line continuation
-    let cpo_save1 = &amp;cpo
-    set cpo&amp;vim
+    call s:setupStatusline()
 
     call s:bindMappings()
     setfiletype nerdtree
@@ -1840,109 +2395,42 @@ function! s:createTreeWin()
     endif
 endfunction
 
-&quot;FUNCTION: s:drawTree {{{2
-&quot;Draws the given node recursively
-&quot;
-&quot;Args:
-&quot;curNode: the node that is being rendered with this call
-&quot;depth: the current depth in the tree for this call
-&quot;drawText: 1 if we should actually draw the line for this node (if 0 then the
-&quot;child nodes are rendered only)
-&quot;vertMap: a binary array that indicates whether a vertical bar should be draw
-&quot;for each depth in the tree
-&quot;isLastChild:true if this curNode is the last child of its parent
-function! s:drawTree(curNode, depth, drawText, vertMap, isLastChild)
-    if a:drawText == 1
-
-        let treeParts = ''
-
-        &quot;get all the leading spaces and vertical tree parts for this line
-        if a:depth &gt; 1
-            for j in a:vertMap[0:-2]
-                if j == 1
-                    let treeParts = treeParts . '| '
-                else
-                    let treeParts = treeParts . '  '
-                endif
-            endfor
-        endif
-
-        &quot;get the last vertical tree part for this line which will be different
-        &quot;if this node is the last child of its parent
-        if a:isLastChild
-            let treeParts = treeParts . '`'
-        else
-            let treeParts = treeParts . '|'
-        endif
-
-
-        &quot;smack the appropriate dir/file symbol on the line before the file/dir
-        &quot;name itself
-        if a:curNode.path.isDirectory
-            if a:curNode.isOpen
-                let treeParts = treeParts . '~'
-            else
-                let treeParts = treeParts . '+'
-            endif
-        else
-            let treeParts = treeParts . '-'
-        endif
-        let line = treeParts . a:curNode.strDisplay()
-
-        call setline(line(&quot;.&quot;)+1, line)
-        call cursor(line(&quot;.&quot;)+1, col(&quot;.&quot;))
-    endif
-
-    &quot;if the node is an open dir, draw its children
-    if a:curNode.path.isDirectory == 1 &amp;&amp; a:curNode.isOpen == 1
-
-        let childNodesToDraw = a:curNode.getVisibleChildren()
-        if len(childNodesToDraw) &gt; 0
-
-            &quot;draw all the nodes children except the last
-            let lastIndx = len(childNodesToDraw)-1
-            if lastIndx &gt; 0
-                for i in childNodesToDraw[0:lastIndx-1]
-                    call s:drawTree(i, a:depth + 1, 1, add(copy(a:vertMap), 1), 0)
-                endfor
-            endif
-
-            &quot;draw the last child, indicating that it IS the last
-            call s:drawTree(childNodesToDraw[lastIndx], a:depth + 1, 1, add(copy(a:vertMap), 0), 1)
-        endif
-    endif
-endfunction
-
-
 &quot;FUNCTION: s:dumpHelp  {{{2
 &quot;prints out the quick help
 function! s:dumpHelp()
     let old_h = @h
-    if t:treeShowHelp == 1
+    if b:treeShowHelp ==# 1
         let @h=   &quot;\&quot; NERD tree (&quot; . s:NERD_tree_version . &quot;) quickhelp~\n&quot;
         let @h=@h.&quot;\&quot; ============================\n&quot;
         let @h=@h.&quot;\&quot; File node mappings~\n&quot;
-        let @h=@h.&quot;\&quot; &quot;. (g:NERDTreeMouseMode == 3 ? &quot;single&quot; : &quot;double&quot;) .&quot;-click,\n&quot;
-        let @h=@h.&quot;\&quot; &quot;. g:NERDTreeMapActivateNode .&quot;: open in prev window\n&quot;
-        let @h=@h.&quot;\&quot; &quot;. g:NERDTreeMapPreview .&quot;: preview\n&quot;
+        let @h=@h.&quot;\&quot; &quot;. (g:NERDTreeMouseMode ==# 3 ? &quot;single&quot; : &quot;double&quot;) .&quot;-click,\n&quot;
+        if b:NERDTreeType ==# &quot;primary&quot;
+            let @h=@h.&quot;\&quot; &quot;. g:NERDTreeMapActivateNode .&quot;: open in prev window\n&quot;
+        else
+            let @h=@h.&quot;\&quot; &quot;. g:NERDTreeMapActivateNode .&quot;: open in current window\n&quot;
+        endif
+        if b:NERDTreeType ==# &quot;primary&quot;
+            let @h=@h.&quot;\&quot; &quot;. g:NERDTreeMapPreview .&quot;: preview\n&quot;
+        endif
         let @h=@h.&quot;\&quot; &quot;. g:NERDTreeMapOpenInTab.&quot;: open in new tab\n&quot;
         let @h=@h.&quot;\&quot; &quot;. g:NERDTreeMapOpenInTabSilent .&quot;: open in new tab silently\n&quot;
         let @h=@h.&quot;\&quot; middle-click,\n&quot;
         let @h=@h.&quot;\&quot; &quot;. g:NERDTreeMapOpenSplit .&quot;: open split\n&quot;
         let @h=@h.&quot;\&quot; &quot;. g:NERDTreeMapPreviewSplit .&quot;: preview split\n&quot;
+        let @h=@h.&quot;\&quot; &quot;. g:NERDTreeMapOpenVSplit .&quot;: open vsplit\n&quot;
+        let @h=@h.&quot;\&quot; &quot;. g:NERDTreeMapPreviewVSplit .&quot;: preview vsplit\n&quot;
         let @h=@h.&quot;\&quot; &quot;. g:NERDTreeMapExecute.&quot;: Execute file\n&quot;
 
         let @h=@h.&quot;\&quot;\n\&quot; ----------------------------\n&quot;
         let @h=@h.&quot;\&quot; Directory node mappings~\n&quot;
-        let @h=@h.&quot;\&quot; &quot;. (g:NERDTreeMouseMode == 1 ? &quot;double&quot; : &quot;single&quot;) .&quot;-click,\n&quot;
+        let @h=@h.&quot;\&quot; &quot;. (g:NERDTreeMouseMode ==# 1 ? &quot;double&quot; : &quot;single&quot;) .&quot;-click,\n&quot;
         let @h=@h.&quot;\&quot; &quot;. g:NERDTreeMapActivateNode .&quot;: open &amp; close node\n&quot;
         let @h=@h.&quot;\&quot; &quot;. g:NERDTreeMapOpenRecursively .&quot;: recursively open node\n&quot;
         let @h=@h.&quot;\&quot; &quot;. g:NERDTreeMapCloseDir .&quot;: close parent of node\n&quot;
         let @h=@h.&quot;\&quot; &quot;. g:NERDTreeMapCloseChildren .&quot;: close all child nodes of\n&quot;
         let @h=@h.&quot;\&quot;    current node recursively\n&quot;
         let @h=@h.&quot;\&quot; middle-click,\n&quot;
-        let @h=@h.&quot;\&quot; &quot;. g:NERDTreeMapOpenExpl.&quot;: Open netrw for selected\n&quot;
-        let @h=@h.&quot;\&quot;    node\n&quot;
+        let @h=@h.&quot;\&quot; &quot;. g:NERDTreeMapOpenExpl.&quot;: explore selected dir\n&quot;
 
         let @h=@h.&quot;\&quot;\n\&quot; ----------------------------\n&quot;
         let @h=@h.&quot;\&quot; Bookmark table mappings~\n&quot;
@@ -1976,10 +2464,10 @@ function! s:dumpHelp()
 
         let @h=@h.&quot;\&quot;\n\&quot; ----------------------------\n&quot;
         let @h=@h.&quot;\&quot; Tree filtering mappings~\n&quot;
-        let @h=@h.&quot;\&quot; &quot;. g:NERDTreeMapToggleHidden .&quot;: hidden files (&quot; . (t:NERDTreeShowHidden ? &quot;on&quot; : &quot;off&quot;) . &quot;)\n&quot;
-        let @h=@h.&quot;\&quot; &quot;. g:NERDTreeMapToggleFilters .&quot;: file filters (&quot; . (t:NERDTreeIgnoreEnabled ? &quot;on&quot; : &quot;off&quot;) . &quot;)\n&quot;
-        let @h=@h.&quot;\&quot; &quot;. g:NERDTreeMapToggleFiles .&quot;: files (&quot; . (t:NERDTreeShowFiles ? &quot;on&quot; : &quot;off&quot;) . &quot;)\n&quot;
-        let @h=@h.&quot;\&quot; &quot;. g:NERDTreeMapToggleBookmarks .&quot;: bookmarks (&quot; . (t:NERDTreeShowBookmarks ? &quot;on&quot; : &quot;off&quot;) . &quot;)\n&quot;
+        let @h=@h.&quot;\&quot; &quot;. g:NERDTreeMapToggleHidden .&quot;: hidden files (&quot; . (b:NERDTreeShowHidden ? &quot;on&quot; : &quot;off&quot;) . &quot;)\n&quot;
+        let @h=@h.&quot;\&quot; &quot;. g:NERDTreeMapToggleFilters .&quot;: file filters (&quot; . (b:NERDTreeIgnoreEnabled ? &quot;on&quot; : &quot;off&quot;) . &quot;)\n&quot;
+        let @h=@h.&quot;\&quot; &quot;. g:NERDTreeMapToggleFiles .&quot;: files (&quot; . (b:NERDTreeShowFiles ? &quot;on&quot; : &quot;off&quot;) . &quot;)\n&quot;
+        let @h=@h.&quot;\&quot; &quot;. g:NERDTreeMapToggleBookmarks .&quot;: bookmarks (&quot; . (b:NERDTreeShowBookmarks ? &quot;on&quot; : &quot;off&quot;) . &quot;)\n&quot;
 
         let @h=@h.&quot;\&quot;\n\&quot; ----------------------------\n&quot;
         let @h=@h.&quot;\&quot; Other mappings~\n&quot;
@@ -2028,68 +2516,21 @@ function! s:echoError(msg)
     call s:echo(a:msg)
     echohl normal
 endfunction
-&quot;FUNCTION: s:findNodeLineNumber(treenode){{{2
-&quot;Finds the line number for the given tree node
-&quot;
-&quot;Args:
-&quot;treenode: the node to find the line no. for
-function! s:findNodeLineNumber(treenode)
-    &quot;if the node is the root then return the root line no.
-    if a:treenode.isRoot()
-        return s:findRootNodeLineNumber()
-    endif
-
-    let totalLines = line(&quot;$&quot;)
-
-    &quot;the path components we have matched so far
-    let pathcomponents = [substitute(t:NERDTreeRoot.path.str(0), '/ *$', '', '')]
-    &quot;the index of the component we are searching for
-    let curPathComponent = 1
-
-    let fullpath = a:treenode.path.str(0)
-
-
-    let lnum = s:findRootNodeLineNumber()
-    while lnum &gt; 0
-        let lnum = lnum + 1
-        &quot;have we reached the bottom of the tree?
-        if lnum == totalLines+1
-            return -1
+&quot;FUNCTION: s:firstNormalWindow(){{{2
+&quot;find the window number of the first normal window
+function! s:firstNormalWindow()
+    let i = 1
+    while i &lt;= winnr(&quot;$&quot;)
+        let bnum = winbufnr(i)
+        if bnum != -1 &amp;&amp; getbufvar(bnum, '&amp;buftype') ==# ''
+                    \ &amp;&amp; !getwinvar(i, '&amp;previewwindow')
+            return i
         endif
 
-        let curLine = getline(lnum)
-
-        let indent = match(curLine,s:tree_markup_reg_neg) / s:tree_wid
-        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
+        let i += 1
     endwhile
     return -1
 endfunction
-
-&quot;FUNCTION: s:findRootNodeLineNumber(){{{2
-&quot;Finds the line number of the root node
-function! s:findRootNodeLineNumber()
-    let rootLine = 1
-    while getline(rootLine) !~ '^/'
-        let rootLine = rootLine + 1
-    endwhile
-    return rootLine
-endfunction
-
 &quot;FUNCTION: s:getPath(ln) {{{2
 &quot;Gets the full path to the node that is rendered on the given line number
 &quot;
@@ -2105,7 +2546,7 @@ function! s:getPath(ln)
 
     &quot;check to see if we have the root node
     if line =~ '^\/'
-        return t:NERDTreeRoot.path
+        return b:NERDTreeRoot.path
     endif
 
     &quot; in case called from outside the tree
@@ -2113,13 +2554,11 @@ function! s:getPath(ln)
         return {}
     endif
 
-    if line == s:tree_up_dir_line
-        return t:NERDTreeRoot.path.getParent()
+    if line ==# s:tree_up_dir_line
+        return b:NERDTreeRoot.path.getParent()
     endif
 
-    &quot;get the indent level for the file (i.e. how deep in the tree it is)
-    let indent = match(line, s:tree_markup_reg_neg) / s:tree_wid
-
+    let indent = s:indentLevelFor(line)
 
     &quot;remove the tree parts and the leading space
     let curFile = s:stripMarkupFromLine(line, 0)
@@ -2144,7 +2583,7 @@ function! s:getPath(ln)
             break
         endif
         if curLineStripped =~ '/$'
-            let lpindent = match(curLine,s:tree_markup_reg_neg) / s:tree_wid
+            let lpindent = s:indentLevelFor(curLine)
             if lpindent &lt; indent
                 let indent = indent - 1
 
@@ -2153,7 +2592,7 @@ function! s:getPath(ln)
             endif
         endif
     endwhile
-    let curFile = t:NERDTreeRoot.path.drive . dir . curFile
+    let curFile = b:NERDTreeRoot.path.drive . dir . curFile
     let toReturn = s:Path.New(curFile)
     return toReturn
 endfunction
@@ -2166,59 +2605,67 @@ function! s:getSelectedBookmark()
     if name != line
         try
             return s:Bookmark.BookmarkFor(name)
-        catch /NERDTree.BookmarkNotFound/
+        catch /^NERDTree.BookmarkNotFoundError/
             return {}
         endtry
     endif
     return {}
 endfunction
 
-&quot;FUNCTION: s:getSelectedDir() {{{2
-&quot;Returns the current node if it is a dir node, or else returns the current
-&quot;nodes parent
-function! s:getSelectedDir()
-    let currentDir = s:getSelectedNode()
-    if currentDir != {} &amp;&amp; !currentDir.isRoot()
-        if currentDir.path.isDirectory == 0
-            let currentDir = currentDir.parent
-        endif
-    endif
-    return currentDir
-endfunction
-&quot;FUNCTION: s:getSelectedNode() {{{2
-&quot;gets the treenode that the cursor is currently over
-function! s:getSelectedNode()
-    try
-        let path = s:getPath(line(&quot;.&quot;))
-        if path == {}
-            return {}
-        endif
-        return t:NERDTreeRoot.findNode(path)
-    catch /^NERDTree/
-        return {}
-    endtry
-endfunction
 &quot;FUNCTION: s:getTreeWinNum() {{{2
 &quot;gets the nerd tree window number for this tab
 function! s:getTreeWinNum()
-    if exists(&quot;t:NERDTreeWinName&quot;)
-        return bufwinnr(t:NERDTreeWinName)
+    if exists(&quot;t:NERDTreeBufName&quot;)
+        return bufwinnr(t:NERDTreeBufName)
     else
         return -1
     endif
 endfunction
-
+&quot;FUNCTION: s:indentLevelFor(line) {{{2
+function! s:indentLevelFor(line)
+    return match(a:line, '[^ \-+~`|]') / s:tree_wid
+endfunction
 &quot;FUNCTION: s:isTreeOpen() {{{2
 function! s:isTreeOpen()
     return s:getTreeWinNum() != -1
 endfunction
+&quot;FUNCTION: s:isWindowUsable(winnumber) {{{2
+&quot;Returns 1 if opening a file from the tree in the given window requires it to
+&quot;be split
+&quot;
+&quot;Args:
+&quot;winnumber: the number of the window in question
+function! s:isWindowUsable(winnumber)
+    &quot;gotta split if theres only one window (i.e. the NERD tree)
+    if winnr(&quot;$&quot;) ==# 1
+        return 0
+    endif
+
+    let oldwinnr = winnr()
+    call s:exec(a:winnumber . &quot;wincmd p&quot;)
+    let specialWindow = getbufvar(&quot;%&quot;, '&amp;buftype') != '' || getwinvar('%', '&amp;previewwindow')
+    let modified = &amp;modified
+    call s:exec(oldwinnr . &quot;wincmd p&quot;)
+
+    &quot;if its a special window e.g. quickfix or another explorer plugin then we
+    &quot;have to split
+    if specialWindow
+        return 0
+    endif
+
+    if &amp;hidden
+        return 1
+    endif
+
+    return !modified || s:bufInWindows(winbufnr(a:winnumber)) &gt;= 2
+endfunction
 
 &quot; FUNCTION: s:jumpToChild(direction) {{{2
 &quot; Args:
 &quot; direction: 0 if going to first child, 1 if going to last
 function! s:jumpToChild(direction)
-    let currentNode = s:getSelectedNode()
-    if currentNode == {} || currentNode.isRoot()
+    let currentNode = s:TreeFileNode.GetSelected()
+    if currentNode ==# {} || currentNode.isRoot()
         call s:echo(&quot;cannot jump to &quot; . (a:direction ? &quot;last&quot; : &quot;first&quot;) .  &quot; child&quot;)
         return
     end
@@ -2231,172 +2678,19 @@ function! s:jumpToChild(direction)
     endif
 
     if targetNode.equals(currentNode)
-        let siblingDir = currentNode.parent.findOpenDirSiblingWithChildren(a:direction)
+        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 s:putCursorOnNode(targetNode, 1, 0)
+    call targetNode.putCursorHere(1, 0)
 
     call s:centerView()
 endfunction
 
 
-&quot;FUNCTION: s:openDirNodeSplit(treenode) {{{2
-&quot;Open the file represented by the given node in a new window.
-&quot;No action is taken for file nodes
-&quot;
-&quot;ARGS:
-&quot;treenode: file node to open
-function! s:openDirNodeSplit(treenode)
-    if a:treenode.path.isDirectory == 1
-        call s:openNodeSplit(a:treenode)
-    endif
-endfunction
-
-&quot; FUNCTION: s:openExplorerFor(treenode) {{{2
-&quot; opens a netrw window for the given dir treenode
-function! s:openExplorerFor(treenode)
-    let oldwin = winnr()
-    wincmd p
-    if oldwin == winnr() || (&amp;modified &amp;&amp; s:bufInWindows(winbufnr(winnr())) &lt; 2)
-        wincmd p
-        call s:openDirNodeSplit(a:treenode)
-    else
-        exec (&quot;silent edit &quot; . a:treenode.path.strForEditCmd())
-    endif
-endfunction
-&quot;FUNCTION: s:openFileNode(treenode) {{{2
-&quot;Open the file represented by the given node in the current window, splitting
-&quot;the window if needed
-&quot;
-&quot;ARGS:
-&quot;treenode: file node to open
-function! s:openFileNode(treenode)
-    call s:putCursorInTreeWin()
-
-    &quot;if the file is already open in this tab then just stick the cursor in it
-    let winnr = bufwinnr('^' . a:treenode.path.strForOS(0) . '$')
-    if winnr != -1
-        exec winnr . &quot;wincmd w&quot;
-
-    elseif s:shouldSplitToOpen(winnr(&quot;#&quot;))
-        call s:openFileNodeSplit(a:treenode)
-    else
-        try
-            wincmd p
-            exec (&quot;edit &quot; . a:treenode.path.strForEditCmd())
-        catch /^Vim\%((\a\+)\)\=:E37/
-            call s:putCursorInTreeWin()
-            call s:echo(&quot;Cannot open file, it is already open and modified&quot;)
-        catch /^Vim\%((\a\+)\)\=:/
-            echo v:exception
-        endtry
-    endif
-endfunction
-
-&quot;FUNCTION: s:openFileNodeSplit(treenode) {{{2
-&quot;Open the file represented by the given node in a new window.
-&quot;No action is taken for dir nodes
-&quot;
-&quot;ARGS:
-&quot;treenode: file node to open
-function! s:openFileNodeSplit(treenode)
-    if a:treenode.path.isDirectory == 0
-        try
-            call s:openNodeSplit(a:treenode)
-        catch /^NERDTree.view.FileOpen/
-            call s:echo(&quot;Cannot open file, it is already open and modified&quot; )
-        endtry
-    endif
-endfunction
-
-&quot;FUNCTION: s:openNodeSplit(treenode) {{{2
-&quot;Open the file/dir represented by the given node in a new window
-&quot;
-&quot;ARGS:
-&quot;treenode: file node to open
-function! s:openNodeSplit(treenode)
-    call s:putCursorInTreeWin()
-
-    &quot; Save the user's settings for splitbelow and splitright
-    let savesplitbelow=&amp;splitbelow
-    let savesplitright=&amp;splitright
-
-    &quot; Figure out how to do the split based on the user's preferences.
-    &quot; We want to split to the (left,right,top,bottom) of the explorer
-    &quot; window, but we want to extract the screen real-estate from the
-    &quot; window next to the explorer if possible.
-    &quot;
-    &quot; 'there' will be set to a command to move from the split window
-    &quot; back to the explorer window
-    &quot;
-    &quot; 'back' will be set to a command to move from the explorer window
-    &quot; back to the newly split window
-    &quot;
-    &quot; 'right' and 'below' will be set to the settings needed for
-    &quot; splitbelow and splitright IF the explorer is the only window.
-    &quot;
-    if s:shouldSplitVertically()
-        let there= g:NERDTreeWinPos == &quot;left&quot; ? &quot;wincmd h&quot; : &quot;wincmd l&quot;
-        let back = g:NERDTreeWinPos == &quot;left&quot; ? &quot;wincmd l&quot; : &quot;wincmd h&quot;
-        let right= g:NERDTreeWinPos == &quot;left&quot;
-        let below=0
-    else
-        let there= g:NERDTreeWinPos == &quot;top&quot; ? &quot;wincmd k&quot; : &quot;wincmd j&quot;
-        let back = g:NERDTreeWinPos == &quot;top&quot; ? &quot;wincmd j&quot; : &quot;wincmd k&quot;
-        let below= g:NERDTreeWinPos == &quot;top&quot;
-        let right=0
-    endif
-
-    &quot; Attempt to go to adjacent window
-    exec(back)
-
-    let onlyOneWin = (winnr() == s:getTreeWinNum())
-
-    &quot; If no adjacent window, set splitright and splitbelow appropriately
-    if onlyOneWin
-        let &amp;splitright=right
-        let &amp;splitbelow=below
-    else
-        &quot; found adjacent window - invert split direction
-        let &amp;splitright=!right
-        let &amp;splitbelow=!below
-    endif
-
-    &quot; Create a variable to use if splitting vertically
-    let splitMode = &quot;&quot;
-    if (onlyOneWin &amp;&amp; s:shouldSplitVertically()) || (!onlyOneWin &amp;&amp; !s:shouldSplitVertically())
-        let splitMode = &quot;vertical&quot;
-    endif
-
-    echomsg splitMode
-
-    &quot; Open the new window
-    try
-        exec(splitMode.&quot; sp &quot; . a:treenode.path.strForEditCmd())
-    catch /^Vim\%((\a\+)\)\=:E37/
-        call s:putCursorInTreeWin()
-        throw &quot;NERDTree.view.FileOpen exception: &quot;. a:treenode.path.str(0) .&quot; is already open and modified.&quot;
-    catch /^Vim\%((\a\+)\)\=:/
-        &quot;do nothing
-    endtry
-
-    &quot;resize the tree window if no other window was open before
-    if onlyOneWin
-        let size = exists(&quot;t:NERDTreeOldWindowSize&quot;) ? t:NERDTreeOldWindowSize : g:NERDTreeWinSize
-        exec(there)
-        exec(&quot;silent &quot;. splitMode .&quot; resize &quot;. size)
-        wincmd p
-    endif
-
-    &quot; Restore splitmode settings
-    let &amp;splitbelow=savesplitbelow
-    let &amp;splitright=savesplitright
-endfunction
-
 &quot;FUNCTION: s:promptToDelBuffer(bufnum, msg){{{2
 &quot;prints out the given msg and, if the user responds by pushing 'y' then the
 &quot;buffer with the given bufnum is deleted
@@ -2407,7 +2701,7 @@ endfunction
 &quot;     del the buffer
 function! s:promptToDelBuffer(bufnum, msg)
     echo a:msg
-    if nr2char(getchar()) == 'y'
+    if nr2char(getchar()) ==# 'y'
         exec &quot;silent bdelete! &quot; . a:bufnum
     endif
 endfunction
@@ -2415,58 +2709,30 @@ endfunction
 &quot;FUNCTION: s:putCursorOnBookmarkTable(){{{2
 &quot;Places the cursor at the top of the bookmarks table
 function! s:putCursorOnBookmarkTable()
-    if !t:NERDTreeShowBookmarks
-        throw &quot;NERDTree.IllegalOperation exception: cant find bookmark table, bookmarks arent active&quot;
+    if !b:NERDTreeShowBookmarks
+        throw &quot;NERDTree.IllegalOperationError: cant find bookmark table, bookmarks arent active&quot;
     endif
 
-    let rootNodeLine = s:findRootNodeLineNumber()
+    let rootNodeLine = s:TreeFileNode.GetRootLineNum()
 
     let line = 1
     while getline(line) !~ '^&gt;-\+Bookmarks-\+$'
         let line = line + 1
         if line &gt;= rootNodeLine
-            throw &quot;NERDTree.BookmarkTableNotFound exception: didnt find the bookmarks table&quot;
+            throw &quot;NERDTree.BookmarkTableNotFoundError: didnt find the bookmarks table&quot;
         endif
     endwhile
     call cursor(line, 0)
 endfunction
 
-&quot;FUNCTION: s:putCursorOnNode(treenode, isJump, recurseUpward){{{2
-&quot;Places the cursor on the line number representing the given node
-&quot;
-&quot;Args:
-&quot;treenode: the node to put the cursor on
-&quot;isJump: 1 if this cursor movement should be counted as a jump by vim
-&quot;recurseUpward: try to put the cursor on the parent if the this node isnt
-&quot;visible
-function! s:putCursorOnNode(treenode, isJump, recurseUpward)
-    let ln = s:findNodeLineNumber(a:treenode)
-    if ln != -1
-        if a:isJump
-            mark '
-        endif
-        call cursor(ln, col(&quot;.&quot;))
-    else
-        if a:recurseUpward
-            let node = a:treenode
-            while s:findNodeLineNumber(node) == -1 &amp;&amp; node != {}
-                let node = node.parent
-                call node.open()
-            endwhile
-            call s:renderView()
-            call s:putCursorOnNode(a:treenode, a:isJump, 0)
-        endif
-    endif
-endfunction
-
 &quot;FUNCTION: s:putCursorInTreeWin(){{{2
 &quot;Places the cursor in the nerd tree window
 function! s:putCursorInTreeWin()
     if !s:isTreeOpen()
-        throw &quot;NERDTree.view.InvalidOperation Exception: No NERD tree window exists&quot;
+        throw &quot;NERDTree.InvalidOperationError: cant put cursor in NERD tree window, no window exists&quot;
     endif
 
-    exec s:getTreeWinNum() . &quot;wincmd w&quot;
+    call s:exec(s:getTreeWinNum() . &quot;wincmd w&quot;)
 endfunction
 
 &quot;FUNCTION: s:renderBookmarks {{{2
@@ -2484,13 +2750,8 @@ function! s:renderBookmarks()
     call cursor(line(&quot;.&quot;)+1, col(&quot;.&quot;))
 endfunction
 &quot;FUNCTION: s:renderView {{{2
-&quot;The entry function for rendering the tree. Renders the root then calls
-&quot;s:drawTree to draw the children of the root
-&quot;
-&quot;Args:
+&quot;The entry function for rendering the tree
 function! s:renderView()
-    execute s:getTreeWinNum() . &quot;wincmd w&quot;
-
     setlocal modifiable
 
     &quot;remember the top line of the buffer and the current line so we can
@@ -2508,7 +2769,7 @@ function! s:renderView()
     call setline(line(&quot;.&quot;)+1, &quot;&quot;)
     call cursor(line(&quot;.&quot;)+1, col(&quot;.&quot;))
 
-    if t:NERDTreeShowBookmarks
+    if b:NERDTreeShowBookmarks
         call s:renderBookmarks()
     endif
 
@@ -2517,11 +2778,14 @@ function! s:renderView()
     call cursor(line(&quot;.&quot;)+1, col(&quot;.&quot;))
 
     &quot;draw the header line
-    call setline(line(&quot;.&quot;)+1, t:NERDTreeRoot.path.str(0))
+    call setline(line(&quot;.&quot;)+1, b:NERDTreeRoot.path.str(0))
     call cursor(line(&quot;.&quot;)+1, col(&quot;.&quot;))
 
     &quot;draw the tree
-    call s:drawTree(t:NERDTreeRoot, 0, 0, [], t:NERDTreeRoot.getChildCount() == 1)
+    let old_o = @o
+    let @o = b:NERDTreeRoot.renderToString()
+    silent put o
+    let @o = old_o
 
     &quot;delete the blank line at the top of the buffer
     silent 1,1delete _
@@ -2541,7 +2805,7 @@ endfunction
 &quot;Renders the tree and ensures the cursor stays on the current node or the
 &quot;current nodes parent if it is no longer available upon re-rendering
 function! s:renderViewSavingPosition()
-    let currentNode = s:getSelectedNode()
+    let currentNode = s:TreeFileNode.GetSelected()
 
     &quot;go up the tree till we find a node that will be visible or till we run
     &quot;out of nodes
@@ -2552,7 +2816,7 @@ function! s:renderViewSavingPosition()
     call s:renderView()
 
     if currentNode != {}
-        call s:putCursorOnNode(currentNode, 0, 0)
+        call currentNode.putCursorHere(0, 0)
     endif
 endfunction
 &quot;FUNCTION: s:restoreScreenState() {{{2
@@ -2562,16 +2826,16 @@ endfunction
 &quot;
 &quot;Assumes the cursor is in the NERDTree window
 function! s:restoreScreenState()
-    if !exists(&quot;t:NERDTreeOldTopLine&quot;) || !exists(&quot;t:NERDTreeOldPos&quot;) || !exists(&quot;t:NERDTreeOldWindowSize&quot;)
+    if !exists(&quot;b:NERDTreeOldTopLine&quot;) || !exists(&quot;b:NERDTreeOldPos&quot;) || !exists(&quot;b:NERDTreeOldWindowSize&quot;)
         return
     endif
-    exec(&quot;silent &quot;. (s:shouldSplitVertically() ? &quot;vertical&quot; : &quot;&quot;) .&quot; resize &quot;.t:NERDTreeOldWindowSize)
+    exec(&quot;silent vertical resize &quot;.b:NERDTreeOldWindowSize)
 
     let old_scrolloff=&amp;scrolloff
     let &amp;scrolloff=0
-    call cursor(t:NERDTreeOldTopLine, 0)
+    call cursor(b:NERDTreeOldTopLine, 0)
     normal! zt
-    call setpos(&quot;.&quot;, t:NERDTreeOldPos)
+    call setpos(&quot;.&quot;, b:NERDTreeOldPos)
     let &amp;scrolloff=old_scrolloff
 endfunction
 
@@ -2580,13 +2844,22 @@ endfunction
 &quot;scroll position
 function! s:saveScreenState()
     let win = winnr()
-    call s:putCursorInTreeWin()
-    let t:NERDTreeOldPos = getpos(&quot;.&quot;)
-    let t:NERDTreeOldTopLine = line(&quot;w0&quot;)
-    let t:NERDTreeOldWindowSize = s:shouldSplitVertically() ? winwidth(&quot;&quot;) : winheight(&quot;&quot;)
-    exec win . &quot;wincmd w&quot;
+    try
+        call s:putCursorInTreeWin()
+        let b:NERDTreeOldPos = getpos(&quot;.&quot;)
+        let b:NERDTreeOldTopLine = line(&quot;w0&quot;)
+        let b:NERDTreeOldWindowSize = winwidth(&quot;&quot;)
+        call s:exec(win . &quot;wincmd w&quot;)
+    catch /^NERDTree.InvalidOperationError/
+    endtry
 endfunction
 
+&quot;FUNCTION: s:setupStatusline() {{{2
+function! s:setupStatusline()
+    if g:NERDTreeStatusline != -1
+        let &amp;l:statusline = g:NERDTreeStatusline
+    endif
+endfunction
 &quot;FUNCTION: s:setupSyntaxHighlighting() {{{2
 function! s:setupSyntaxHighlighting()
     &quot;treeFlags are syntax items that should be invisible, but give clues as to
@@ -2618,7 +2891,7 @@ function! s:setupSyntaxHighlighting()
     syn match treeHelp  #^&quot;.*# contains=treeHelpKey,treeHelpTitle,treeFlag,treeToggleOff,treeToggleOn,treeHelpCommand
 
     &quot;highlighting for readonly files
-    syn match treeRO #[\/0-9a-zA-Z]\+.*\[RO\]# contains=treeFlag,treeBookmark
+    syn match treeRO #.*\[RO\]#hs=s+2 contains=treeFlag,treeBookmark,treePart,treePartFile
 
     &quot;highlighting for sym links
     syn match treeLink #[^-| `].* -&gt; # contains=treeBookmark,treeOpenable,treeClosable,treeDirSlash
@@ -2678,42 +2951,6 @@ function! s:setupSyntaxHighlighting()
     hi def link NERDTreeCurrentNode Search
 endfunction
 
-&quot;FUNCTION: s:shouldSplitToOpen() {{{2
-&quot;Returns 1 if opening a file from the tree in the given window requires it to
-&quot;be split
-&quot;
-&quot;Args:
-&quot;winnumber: the number of the window in question
-function! s:shouldSplitToOpen(winnumber)
-    &quot;gotta split if theres only one window (i.e. the NERD tree)
-    if winnr(&quot;$&quot;) == 1
-        return 1
-    endif
-
-    let oldwinnr = winnr()
-    exec a:winnumber . &quot;wincmd p&quot;
-    let specialWindow = getbufvar(&quot;%&quot;, '&amp;buftype') != '' || getwinvar('%', '&amp;previewwindow')
-    let modified = &amp;modified
-    exec oldwinnr . &quot;wincmd p&quot;
-
-    &quot;if its a special window e.g. quickfix or another explorer plugin then we
-    &quot;have to split
-    if specialWindow
-        return 1
-    endif
-
-    if &amp;hidden
-        return 0
-    endif
-
-    return modified &amp;&amp; s:bufInWindows(winbufnr(a:winnumber)) &lt; 2
-endfunction
-
-&quot; Function: s:shouldSplitVertically()   {{{2
-&quot; Returns 1 if g:NERDTreeWinPos is 'left' or 'right'
-function! s:shouldSplitVertically()
-    return g:NERDTreeWinPos == 'left' || g:NERDTreeWinPos == 'right'
-endfunction
 &quot;FUNCTION: s:stripMarkupFromLine(line, removeLeadingSpaces){{{2
 &quot;returns the given line with all the tree parts stripped off
 &quot;
@@ -2724,7 +2961,7 @@ endfunction
 function! s:stripMarkupFromLine(line, removeLeadingSpaces)
     let line = a:line
     &quot;remove the tree parts and the leading space
-    let line = substitute (line,&quot;^&quot; . s:tree_markup_reg . &quot;*&quot;,&quot;&quot;,&quot;&quot;)
+    let line = substitute (line, s:tree_markup_reg,&quot;&quot;,&quot;&quot;)
 
     &quot;strip off any read only flag
     let line = substitute (line, ' \[RO\]', &quot;&quot;,&quot;&quot;)
@@ -2740,7 +2977,7 @@ function! s:stripMarkupFromLine(line, removeLeadingSpaces)
         let wasdir = 1
     endif
     let line = substitute (line,' -&gt; .*',&quot;&quot;,&quot;&quot;) &quot; remove link to
-    if wasdir == 1
+    if wasdir ==# 1
         let line = substitute (line, '/\?$', '/', &quot;&quot;)
     endif
 
@@ -2762,9 +2999,10 @@ function! s:toggle(dir)
     if s:treeExistsForTab()
         if !s:isTreeOpen()
             call s:createTreeWin()
-            call s:renderView()
-
             call s:restoreScreenState()
+            if !&amp;hidden
+                call s:renderView()
+            endif
         else
             call s:closeTree()
         endif
@@ -2781,18 +3019,18 @@ endfunction
 &quot;args:
 &quot;forceKeepWindowOpen - dont close the window even if NERDTreeQuitOnOpen is set
 function! s:activateNode(forceKeepWindowOpen)
-    if getline(&quot;.&quot;) == s:tree_up_dir_line
+    if getline(&quot;.&quot;) ==# s:tree_up_dir_line
         return s:upDir(0)
     endif
 
-    let treenode = s:getSelectedNode()
+    let treenode = s:TreeFileNode.GetSelected()
     if treenode != {}
         if treenode.path.isDirectory
             call treenode.toggleOpen()
             call s:renderView()
-            call s:putCursorOnNode(treenode, 0, 0)
+            call treenode.putCursorHere(0, 0)
         else
-            call s:openFileNode(treenode)
+            call treenode.open()
             if !a:forceKeepWindowOpen
                 call s:closeTreeIfQuitOnOpen()
             end
@@ -2804,7 +3042,7 @@ function! s:activateNode(forceKeepWindowOpen)
                 call bookmark.toRoot()
             else
                 if bookmark.validate()
-                    call s:openFileNode(s:TreeFileNode.New(bookmark.path))
+                    call (s:TreeFileNode.New(bookmark.path)).open()
                 endif
             endif
         endif
@@ -2819,11 +3057,13 @@ function! s:bindMappings()
     nnoremap &lt;silent&gt; &lt;buffer&gt; &lt;2-leftmouse&gt; :call &lt;SID&gt;activateNode(0)&lt;cr&gt;
 
     exec &quot;nnoremap &lt;silent&gt; &lt;buffer&gt; &quot;. g:NERDTreeMapActivateNode . &quot; :call &lt;SID&gt;activateNode(0)&lt;cr&gt;&quot;
-    exec &quot;nnoremap &lt;silent&gt; &lt;buffer&gt; &quot;. g:NERDTreeMapOpenSplit .&quot; :call &lt;SID&gt;openEntrySplit(0)&lt;cr&gt;&quot;
+    exec &quot;nnoremap &lt;silent&gt; &lt;buffer&gt; &quot;. g:NERDTreeMapOpenSplit .&quot; :call &lt;SID&gt;openEntrySplit(0,0)&lt;cr&gt;&quot;
 
     exec &quot;nnoremap &lt;silent&gt; &lt;buffer&gt; &quot;. g:NERDTreeMapPreview .&quot; :call &lt;SID&gt;previewNode(0)&lt;cr&gt;&quot;
     exec &quot;nnoremap &lt;silent&gt; &lt;buffer&gt; &quot;. g:NERDTreeMapPreviewSplit .&quot; :call &lt;SID&gt;previewNode(1)&lt;cr&gt;&quot;
 
+    exec &quot;nnoremap &lt;silent&gt; &lt;buffer&gt; &quot;. g:NERDTreeMapOpenVSplit .&quot; :call &lt;SID&gt;openEntrySplit(1,0)&lt;cr&gt;&quot;
+    exec &quot;nnoremap &lt;silent&gt; &lt;buffer&gt; &quot;. g:NERDTreeMapPreviewVSplit .&quot; :call &lt;SID&gt;previewNode(2)&lt;cr&gt;&quot;
 
     exec &quot;nnoremap &lt;silent&gt; &lt;buffer&gt; &quot;. g:NERDTreeMapExecute .&quot; :call &lt;SID&gt;executeNode()&lt;cr&gt;&quot;
 
@@ -2835,7 +3075,7 @@ function! s:bindMappings()
 
     exec &quot;nnoremap &lt;silent&gt; &lt;buffer&gt; &quot;. g:NERDTreeMapChdir .&quot; :call &lt;SID&gt;chCwd()&lt;cr&gt;&quot;
 
-    exec &quot;nnoremap &lt;silent&gt; &lt;buffer&gt; &quot;. g:NERDTreeMapQuit .&quot; :NERDTreeToggle&lt;cr&gt;&quot;
+    exec &quot;nnoremap &lt;silent&gt; &lt;buffer&gt; &quot;. g:NERDTreeMapQuit .&quot; :call &lt;SID&gt;closeTreeWindow()&lt;cr&gt;&quot;
 
     exec &quot;nnoremap &lt;silent&gt; &lt;buffer&gt; &quot;. g:NERDTreeMapRefreshRoot .&quot; :call &lt;SID&gt;refreshRoot()&lt;cr&gt;&quot;
     exec &quot;nnoremap &lt;silent&gt; &lt;buffer&gt; &quot;. g:NERDTreeMapRefresh .&quot; :call &lt;SID&gt;refreshCurrent()&lt;cr&gt;&quot;
@@ -2878,12 +3118,12 @@ endfunction
 &quot; FUNCTION: s:bookmarkNode(name) {{{2
 &quot; Associate the current node with the given name
 function! s:bookmarkNode(name)
-    let currentNode = s:getSelectedNode()
+    let currentNode = s:TreeFileNode.GetSelected()
     if currentNode != {}
         try
             call currentNode.bookmark(a:name)
             call s:renderView()
-        catch /NERDTree.IllegalBookmarkName/
+        catch /^NERDTree.IllegalBookmarkNameError/
             call s:echo(&quot;bookmark names must not contain spaces&quot;)
         endtry
     else
@@ -2895,7 +3135,7 @@ endfunction
 &quot;called (directories are automatically opened if the symbol beside them is
 &quot;clicked)
 function! s:checkForActivate()
-    let currentNode = s:getSelectedNode()
+    let currentNode = s:TreeFileNode.GetSelected()
     if currentNode != {}
         let startToCur = strpart(getline(line(&quot;.&quot;)), 0, col(&quot;.&quot;))
         let char = strpart(startToCur, strlen(startToCur)-1, 1)
@@ -2903,14 +3143,13 @@ function! s:checkForActivate()
         &quot;if they clicked a dir, check if they clicked on the + or ~ sign
         &quot;beside it
         if currentNode.path.isDirectory
-            let reg = '^' . s:tree_markup_reg .'*[~+]$'
-            if startToCur =~ reg
+            if startToCur =~ s:tree_markup_reg . '$' &amp;&amp; char =~ '[+~]'
                 call s:activateNode(0)
                 return
             endif
         endif
 
-        if (g:NERDTreeMouseMode == 2 &amp;&amp; currentNode.path.isDirectory) || g:NERDTreeMouseMode == 3
+        if (g:NERDTreeMouseMode ==# 2 &amp;&amp; currentNode.path.isDirectory) || g:NERDTreeMouseMode ==# 3
             if char !~ s:tree_markup_reg &amp;&amp; startToCur !~ '\/$'
                 call s:activateNode(0)
                 return
@@ -2921,15 +3160,15 @@ endfunction
 
 &quot; FUNCTION: s:chCwd() {{{2
 function! s:chCwd()
-    let treenode = s:getSelectedNode()
-    if treenode == {}
+    let treenode = s:TreeFileNode.GetSelected()
+    if treenode ==# {}
         call s:echo(&quot;Select a node first&quot;)
         return
     endif
 
     try
         call treenode.path.changeToDir()
-    catch /^NERDTree.Path.Change/
+    catch /^NERDTree.PathChangeError/
         call s:echoWarning(&quot;could not change cwd&quot;)
     endtry
 endfunction
@@ -2937,21 +3176,21 @@ endfunction
 &quot; FUNCTION: s:chRoot() {{{2
 &quot; changes the current root to the selected one
 function! s:chRoot()
-    let treenode = s:getSelectedNode()
-    if treenode == {}
+    let treenode = s:TreeFileNode.GetSelected()
+    if treenode ==# {}
         call s:echo(&quot;Select a node first&quot;)
         return
     endif
 
     call treenode.makeRoot()
     call s:renderView()
-    call s:putCursorOnNode(t:NERDTreeRoot, 0, 0)
+    call b:NERDTreeRoot.putCursorHere(0, 0)
 endfunction
 
 &quot; FUNCTION: s:clearBookmarks(bookmarks) {{{2
 function! s:clearBookmarks(bookmarks)
-    if a:bookmarks == ''
-        let currentNode = s:getSelectedNode()
+    if a:bookmarks ==# ''
+        let currentNode = s:TreeFileNode.GetSelected()
         if currentNode != {}
             call currentNode.clearBoomarks()
         endif
@@ -2966,21 +3205,21 @@ endfunction
 &quot; FUNCTION: s:closeChildren() {{{2
 &quot; closes all childnodes of the current node
 function! s:closeChildren()
-    let currentNode = s:getSelectedDir()
-    if currentNode == {}
+    let currentNode = s:TreeDirNode.GetSelected()
+    if currentNode ==# {}
         call s:echo(&quot;Select a node first&quot;)
         return
     endif
 
     call currentNode.closeChildren()
     call s:renderView()
-    call s:putCursorOnNode(currentNode, 0, 0)
+    call currentNode.putCursorHere(0, 0)
 endfunction
 &quot; FUNCTION: s:closeCurrentDir() {{{2
 &quot; closes the parent dir of the current node
 function! s:closeCurrentDir()
-    let treenode = s:getSelectedNode()
-    if treenode == {}
+    let treenode = s:TreeFileNode.GetSelected()
+    if treenode ==# {}
         call s:echo(&quot;Select a node first&quot;)
         return
     endif
@@ -2991,14 +3230,26 @@ function! s:closeCurrentDir()
     else
         call treenode.parent.close()
         call s:renderView()
-        call s:putCursorOnNode(treenode.parent, 0, 0)
+        call treenode.parent.putCursorHere(0, 0)
+    endif
+endfunction
+&quot; FUNCTION: s:closeTreeWindow() {{{2
+&quot; close the tree window
+function! s:closeTreeWindow()
+    if b:NERDTreeType ==# &quot;secondary&quot; &amp;&amp; b:NERDTreePreviousBuf != -1
+        exec &quot;buffer &quot; . b:NERDTreePreviousBuf
+    else
+        if winnr(&quot;$&quot;) &gt; 1
+            wincmd c
+        else
+            call s:echo(&quot;Cannot close last window&quot;)
+        endif
     endif
 endfunction
-
 &quot; FUNCTION: s:copyNode() {{{2
 function! s:copyNode()
-    let currentNode = s:getSelectedNode()
-    if currentNode == {}
+    let currentNode = s:TreeFileNode.GetSelected()
+    if currentNode ==# {}
         call s:echo(&quot;Put the cursor on a file node first&quot;)
         return
     endif
@@ -3016,14 +3267,14 @@ function! s:copyNode()
         if currentNode.path.copyingWillOverwrite(newNodePath)
             call s:echo(&quot;\nWarning: copying may overwrite files! Continue? (yN)&quot;)
             let choice = nr2char(getchar())
-            let confirmed = choice == 'y'
+            let confirmed = choice ==# 'y'
         endif
 
         if confirmed
             try
                 let newNode = currentNode.copy(newNodePath)
                 call s:renderView()
-                call s:putCursorOnNode(newNode, 0, 0)
+                call newNode.putCursorHere(0, 0)
             catch /^NERDTree/
                 call s:echoWarning(&quot;Could not copy node&quot;)
             endtry
@@ -3038,14 +3289,14 @@ endfunction
 &quot; if the cursor is on a bookmark, prompt to delete
 function! s:deleteBookmark()
     let bookmark = s:getSelectedBookmark()
-    if bookmark == {}
+    if bookmark ==# {}
         call s:echo(&quot;Put the cursor on a bookmark&quot;)
         return
     endif
 
     echo  &quot;Are you sure you wish to delete the bookmark:\n\&quot;&quot; . bookmark.name . &quot;\&quot; (yN):&quot;
 
-    if  nr2char(getchar()) == 'y'
+    if  nr2char(getchar()) ==# 'y'
         try
             call bookmark.delete()
             call s:renderView()
@@ -3063,8 +3314,8 @@ endfunction
 &quot; if the current node is a file, pops up a dialog giving the user the option
 &quot; to delete it
 function! s:deleteNode()
-    let currentNode = s:getSelectedNode()
-    if currentNode == {}
+    let currentNode = s:TreeFileNode.GetSelected()
+    if currentNode ==# {}
         call s:echo(&quot;Put the cursor on a file node first&quot;)
         return
     endif
@@ -3076,14 +3327,14 @@ function! s:deleteNode()
                          \ &quot;==========================================================\n&quot; .
                          \ &quot;STOP! To delete this entire directory, type 'yes'\n&quot; .
                          \ &quot;&quot; . currentNode.path.strForOS(0) . &quot;: &quot;)
-        let confirmed = choice == 'yes'
+        let confirmed = choice ==# 'yes'
     else
         echo &quot;Delete the current node\n&quot; .
            \ &quot;==========================================================\n&quot;.
            \ &quot;Are you sure you wish to delete the node:\n&quot; .
            \ &quot;&quot; . currentNode.path.strForOS(0) . &quot; (yN):&quot;
         let choice = nr2char(getchar())
-        let confirmed = choice == 'y'
+        let confirmed = choice ==# 'y'
     endif
 
 
@@ -3096,7 +3347,7 @@ function! s:deleteNode()
             &quot;close that buffer
             let bufnum = bufnr(currentNode.path.str(0))
             if buflisted(bufnum)
-                let prompt = &quot;\nNode deleted.\n\nThe file is open in buffer &quot;. bufnum . (bufwinnr(bufnum) == -1 ? &quot; (hidden)&quot; : &quot;&quot;) .&quot;. Delete this buffer? (yN)&quot;
+                let prompt = &quot;\nNode deleted.\n\nThe file is open in buffer &quot;. bufnum . (bufwinnr(bufnum) ==# -1 ? &quot; (hidden)&quot; : &quot;&quot;) .&quot;. Delete this buffer? (yN)&quot;
                 call s:promptToDelBuffer(bufnum, prompt)
             endif
 
@@ -3113,15 +3364,15 @@ endfunction
 &quot; FUNCTION: s:displayHelp() {{{2
 &quot; toggles the help display
 function! s:displayHelp()
-    let t:treeShowHelp = t:treeShowHelp ? 0 : 1
+    let b:treeShowHelp = b:treeShowHelp ? 0 : 1
     call s:renderView()
     call s:centerView()
 endfunction
 
 &quot; FUNCTION: s:executeNode() {{{2
 function! s:executeNode()
-    let treenode = s:getSelectedNode()
-    if treenode == {} || treenode.path.isDirectory
+    let treenode = s:TreeFileNode.GetSelected()
+    if treenode ==# {} || treenode.path.isDirectory
         call s:echo(&quot;Select an executable file node first&quot; )
     else
         echo &quot;NERDTree executor\n&quot; .
@@ -3140,8 +3391,8 @@ endfunction
 
 &quot; FUNCTION: s:handleMiddleMouse() {{{2
 function! s:handleMiddleMouse()
-    let curNode = s:getSelectedNode()
-    if curNode == {}
+    let curNode = s:TreeFileNode.GetSelected()
+    if curNode ==# {}
         call s:echo(&quot;Put the cursor on a node first&quot; )
         return
     endif
@@ -3149,7 +3400,7 @@ function! s:handleMiddleMouse()
     if curNode.path.isDirectory
         call s:openExplorer()
     else
-        call s:openEntrySplit(0)
+        call s:openEntrySplit(0,0)
     endif
 endfunction
 
@@ -3157,8 +3408,8 @@ endfunction
 &quot; FUNCTION: s:insertNewNode() {{{2
 &quot; Adds a new node to the filesystem and then into the tree
 function! s:insertNewNode()
-    let curDirNode = s:getSelectedDir()
-    if curDirNode == {}
+    let curDirNode = s:TreeDirNode.GetSelected()
+    if curDirNode ==# {}
         call s:echo(&quot;Put the cursor on a node first&quot; )
         return
     endif
@@ -3168,20 +3419,20 @@ function! s:insertNewNode()
                           \ &quot;Enter the dir/file name to be created. Dirs end with a '/'\n&quot; .
                           \ &quot;&quot;, curDirNode.path.strForGlob() . s:os_slash)
 
-    if newNodeName == ''
+    if newNodeName ==# ''
         call s:echo(&quot;Node Creation Aborted.&quot;)
         return
     endif
 
     try
         let newPath = s:Path.Create(newNodeName)
-        let parentNode = t:NERDTreeRoot.findNode(newPath.getPathTrunk())
+        let parentNode = b:NERDTreeRoot.findNode(newPath.getPathTrunk())
 
         let newTreeNode = s:TreeFileNode.New(newPath)
         if parentNode.isOpen || !empty(parentNode.children)
             call parentNode.addChild(newTreeNode, 1)
             call s:renderView()
-            call s:putCursorOnNode(newTreeNode, 1, 0)
+            call newTreeNode.putCursorHere(1, 0)
         endif
     catch /^NERDTree/
         call s:echoWarning(&quot;Node Not Created.&quot;)
@@ -3203,10 +3454,10 @@ endfunction
 &quot; FUNCTION: s:jumpToParent() {{{2
 &quot; moves the cursor to the parent of the current node
 function! s:jumpToParent()
-    let currentNode = s:getSelectedNode()
+    let currentNode = s:TreeFileNode.GetSelected()
     if !empty(currentNode)
         if !empty(currentNode.parent)
-            call s:putCursorOnNode(currentNode.parent, 1, 0)
+            call currentNode.parent.putCursorHere(1, 0)
             call s:centerView()
         else
             call s:echo(&quot;cannot jump to parent&quot;)
@@ -3219,7 +3470,7 @@ endfunction
 &quot; FUNCTION: s:jumpToRoot() {{{2
 &quot; moves the cursor to the root node
 function! s:jumpToRoot()
-    call s:putCursorOnNode(t:NERDTreeRoot, 1, 0)
+    call b:NERDTreeRoot.putCursorHere(1, 0)
     call s:centerView()
 endfunction
 
@@ -3230,12 +3481,12 @@ endfunction
 &quot; forward: 1 if the cursor should move to the next sibling, 0 if it should
 &quot; move back to the previous sibling
 function! s:jumpToSibling(forward)
-    let currentNode = s:getSelectedNode()
+    let currentNode = s:TreeFileNode.GetSelected()
     if !empty(currentNode)
         let sibling = currentNode.findSibling(a:forward)
 
         if !empty(sibling)
-            call s:putCursorOnNode(sibling, 1, 0)
+            call sibling.putCursorHere(1, 0)
             call s:centerView()
         endif
     else
@@ -3248,29 +3499,33 @@ endfunction
 function! s:openBookmark(name)
     try
         let targetNode = s:Bookmark.GetNodeForName(a:name, 0)
-        call s:putCursorOnNode(targetNode, 0, 1)
+        call targetNode.putCursorHere(0, 1)
         redraw!
-    catch /NERDTree.BookmarkedNodeNotFound/
+    catch /^NERDTree.BookmarkedNodeNotFoundError/
         call s:echo(&quot;note - target node is not cached&quot;)
         let bookmark = s:Bookmark.BookmarkFor(a:name)
         let targetNode = s:TreeFileNode.New(bookmark.path)
     endtry
     if targetNode.path.isDirectory
-        call s:openExplorerFor(targetNode)
+        call targetNode.openExplorer()
     else
-        call s:openFileNode(targetNode)
+        call targetNode.open()
     endif
 endfunction
-&quot; FUNCTION: s:openEntrySplit(forceKeepWindowOpen) {{{2
+&quot; FUNCTION: s:openEntrySplit(vertical, forceKeepWindowOpen) {{{2
 &quot;Opens the currently selected file from the explorer in a
 &quot;new window
 &quot;
 &quot;args:
 &quot;forceKeepWindowOpen - dont close the window even if NERDTreeQuitOnOpen is set
-function! s:openEntrySplit(forceKeepWindowOpen)
-    let treenode = s:getSelectedNode()
+function! s:openEntrySplit(vertical, forceKeepWindowOpen)
+    let treenode = s:TreeFileNode.GetSelected()
     if treenode != {}
-        call s:openFileNodeSplit(treenode)
+        if a:vertical
+            call treenode.openVSplit()
+        else
+            call treenode.openSplit()
+        endif
         if !a:forceKeepWindowOpen
             call s:closeTreeIfQuitOnOpen()
         endif
@@ -3281,9 +3536,9 @@ endfunction
 
 &quot; FUNCTION: s:openExplorer() {{{2
 function! s:openExplorer()
-    let treenode = s:getSelectedDir()
+    let treenode = s:TreeDirNode.GetSelected()
     if treenode != {}
-        call s:openExplorerFor(treenode)
+        call treenode.openExplorer()
     else
         call s:echo(&quot;select a node first&quot;)
     endif
@@ -3297,7 +3552,7 @@ endfunction
 function! s:openInNewTab(stayCurrentTab)
     let currentTab = tabpagenr()
 
-    let treenode = s:getSelectedNode()
+    let treenode = s:TreeFileNode.GetSelected()
     if treenode != {}
         if treenode.path.isDirectory
             tabnew
@@ -3323,8 +3578,8 @@ endfunction
 
 &quot; FUNCTION: s:openNodeRecursively() {{{2
 function! s:openNodeRecursively()
-    let treenode = s:getSelectedNode()
-    if treenode == {} || treenode.path.isDirectory == 0
+    let treenode = s:TreeFileNode.GetSelected()
+    if treenode ==# {} || treenode.path.isDirectory ==# 0
         call s:echo(&quot;Select a directory node first&quot; )
     else
         call s:echo(&quot;Recursively opening node. Please wait...&quot;)
@@ -3337,13 +3592,17 @@ function! s:openNodeRecursively()
 endfunction
 
 &quot;FUNCTION: s:previewNode() {{{2
+&quot;Args:
+&quot;   openNewWin: if 0, use the previous window, if 1 open in new split, if 2
+&quot;               open in a vsplit
 function! s:previewNode(openNewWin)
-    if a:openNewWin
-        call s:openEntrySplit(1)
+    let currentBuf = bufnr(&quot;&quot;)
+    if a:openNewWin &gt; 0
+        call s:openEntrySplit(a:openNewWin ==# 2,1)
     else
         call s:activateNode(1)
     end
-    call s:putCursorInTreeWin()
+    call s:exec(bufwinnr(currentBuf) . &quot;wincmd w&quot;)
 endfunction
 
 &quot; FUNCTION: s:revealBookmark(name) {{{2
@@ -3351,8 +3610,8 @@ endfunction
 function! s:revealBookmark(name)
     try
         let targetNode = s:Bookmark.GetNodeForName(a:name, 0)
-        call s:putCursorOnNode(targetNode, 0, 1)
-    catch /NERDTree.BookmarkDoesntExist/
+        call targetNode.putCursorHere(0, 1)
+    catch /^NERDTree.BookmarkNotFoundError/
         call s:echo(&quot;Bookmark isnt cached under the current root&quot;)
     endtry
 endfunction
@@ -3361,7 +3620,7 @@ endfunction
 &quot; will be reloaded.
 function! s:refreshRoot()
     call s:echo(&quot;Refreshing the root node. This could take a while...&quot;)
-    call t:NERDTreeRoot.refresh()
+    call b:NERDTreeRoot.refresh()
     call s:renderView()
     redraw
     call s:echo(&quot;Refreshing the root node. This could take a while... DONE&quot;)
@@ -3370,8 +3629,8 @@ endfunction
 &quot; FUNCTION: s:refreshCurrent() {{{2
 &quot; refreshes the root for the current node
 function! s:refreshCurrent()
-    let treenode = s:getSelectedDir()
-    if treenode == {}
+    let treenode = s:TreeDirNode.GetSelected()
+    if treenode ==# {}
         call s:echo(&quot;Refresh failed. Select a node first&quot;)
         return
     endif
@@ -3385,8 +3644,8 @@ endfunction
 &quot; FUNCTION: s:renameCurrent() {{{2
 &quot; allows the user to rename the current node
 function! s:renameCurrent()
-    let curNode = s:getSelectedNode()
-    if curNode == {}
+    let curNode = s:TreeFileNode.GetSelected()
+    if curNode ==# {}
         call s:echo(&quot;Put the cursor on a node first&quot; )
         return
     endif
@@ -3396,7 +3655,7 @@ function! s:renameCurrent()
                           \ &quot;Enter the new path for the node:                          \n&quot; .
                           \ &quot;&quot;, curNode.path.strForOS(0))
 
-    if newNodePath == ''
+    if newNodePath ==# ''
         call s:echo(&quot;Node Renaming Aborted.&quot;)
         return
     endif
@@ -3410,11 +3669,11 @@ function! s:renameCurrent()
         &quot;if the node is open in a buffer, ask the user if they want to
         &quot;close that buffer
         if bufnum != -1
-            let prompt = &quot;\nNode renamed.\n\nThe old file is open in buffer &quot;. bufnum . (bufwinnr(bufnum) == -1 ? &quot; (hidden)&quot; : &quot;&quot;) .&quot;. Delete this buffer? (yN)&quot;
+            let prompt = &quot;\nNode renamed.\n\nThe old file is open in buffer &quot;. bufnum . (bufwinnr(bufnum) ==# -1 ? &quot; (hidden)&quot; : &quot;&quot;) .&quot;. Delete this buffer? (yN)&quot;
             call s:promptToDelBuffer(bufnum, prompt)
         endif
 
-        call s:putCursorOnNode(curNode, 1, 0)
+        call curNode.putCursorHere(1, 0)
 
         redraw
     catch /^NERDTree/
@@ -3424,8 +3683,8 @@ endfunction
 
 &quot; FUNCTION: s:showFileSystemMenu() {{{2
 function! s:showFileSystemMenu()
-    let curNode = s:getSelectedNode()
-    if curNode == {}
+    let curNode = s:TreeFileNode.GetSelected()
+    if curNode ==# {}
         call s:echo(&quot;Put the cursor on a node first&quot; )
         return
     endif
@@ -3461,7 +3720,7 @@ endfunction
 &quot; FUNCTION: s:toggleIgnoreFilter() {{{2
 &quot; toggles the use of the NERDTreeIgnore option
 function! s:toggleIgnoreFilter()
-    let t:NERDTreeIgnoreEnabled = !t:NERDTreeIgnoreEnabled
+    let b:NERDTreeIgnoreEnabled = !b:NERDTreeIgnoreEnabled
     call s:renderViewSavingPosition()
     call s:centerView()
 endfunction
@@ -3469,8 +3728,8 @@ endfunction
 &quot; FUNCTION: s:toggleShowBookmarks() {{{2
 &quot; toggles the display of bookmarks
 function! s:toggleShowBookmarks()
-    let t:NERDTreeShowBookmarks = !t:NERDTreeShowBookmarks
-    if t:NERDTreeShowBookmarks
+    let b:NERDTreeShowBookmarks = !b:NERDTreeShowBookmarks
+    if b:NERDTreeShowBookmarks
         call s:renderView()
         call s:putCursorOnBookmarkTable()
     else
@@ -3481,7 +3740,7 @@ endfunction
 &quot; FUNCTION: s:toggleShowFiles() {{{2
 &quot; toggles the display of hidden files
 function! s:toggleShowFiles()
-    let t:NERDTreeShowFiles = !t:NERDTreeShowFiles
+    let b:NERDTreeShowFiles = !b:NERDTreeShowFiles
     call s:renderViewSavingPosition()
     call s:centerView()
 endfunction
@@ -3489,7 +3748,7 @@ endfunction
 &quot; FUNCTION: s:toggleShowHidden() {{{2
 &quot; toggles the display of hidden files
 function! s:toggleShowHidden()
-    let t:NERDTreeShowHidden = !t:NERDTreeShowHidden
+    let b:NERDTreeShowHidden = !b:NERDTreeShowHidden
     call s:renderViewSavingPosition()
     call s:centerView()
 endfunction
@@ -3501,30 +3760,37 @@ endfunction
 &quot;keepState: 1 if the current root should be left open when the tree is
 &quot;re-rendered
 function! s:upDir(keepState)
-    let cwd = t:NERDTreeRoot.path.str(0)
-    if cwd == &quot;/&quot; || cwd =~ '^[^/]..$'
+    let cwd = b:NERDTreeRoot.path.str(0)
+    if cwd ==# &quot;/&quot; || cwd =~ '^[^/]..$'
         call s:echo(&quot;already at top dir&quot;)
     else
         if !a:keepState
-            call t:NERDTreeRoot.close()
+            call b:NERDTreeRoot.close()
         endif
 
-        let oldRoot = t:NERDTreeRoot
+        let oldRoot = b:NERDTreeRoot
 
-        if empty(t:NERDTreeRoot.parent)
-            let path = t:NERDTreeRoot.path.getPathTrunk()
+        if empty(b:NERDTreeRoot.parent)
+            let path = b:NERDTreeRoot.path.getPathTrunk()
             let newRoot = s:TreeDirNode.New(path)
             call newRoot.open()
-            call newRoot.transplantChild(t:NERDTreeRoot)
-            let t:NERDTreeRoot = newRoot
+            call newRoot.transplantChild(b:NERDTreeRoot)
+            let b:NERDTreeRoot = newRoot
         else
-            let t:NERDTreeRoot = t:NERDTreeRoot.parent
+            let b:NERDTreeRoot = b:NERDTreeRoot.parent
+        endif
 
+        if g:NERDTreeChDirMode ==# 2
+            exec 'cd ' . b:NERDTreeRoot.path.strForCd()
         endif
 
         call s:renderView()
-        call s:putCursorOnNode(oldRoot, 0, 0)
+        call oldRoot.putCursorHere(0, 0)
     endif
 endfunction
 
+
+&quot;reset &amp;cpo back to users setting
+let &amp;cpo = s:old_cpo
+
 &quot; vim: set sw=4 sts=4 et fdm=marker:</diff>
      <filename>plugin/NERD_tree.vim</filename>
    </modified>
    <modified>
      <diff>@@ -1,10 +1,10 @@
 &quot;=============================================================================
 &quot; fuzzyfinder.vim : Fuzzy/Partial pattern explorer for
-&quot;                   buffer/file/MRU/command/favorite/tag/etc.
+&quot;                   buffer/file/MRU/command/bookmark/tag/etc.
 &quot;=============================================================================
 &quot;
 &quot; Author:  Takeshi NISHIDA &lt;ns9tks@DELETE-ME.gmail.com&gt;
-&quot; Version: 2.13, for Vim 7.1
+&quot; Version: 2.19, for Vim 7.1
 &quot; Licence: MIT Licence
 &quot; URL:     http://www.vim.org/scripts/script.php?script_id=1984
 &quot;
@@ -39,11 +39,11 @@
 &quot;     - Buffer mode
 &quot;     - File mode
 &quot;     - Directory mode (yet another :cd command)
-&quot;     - MRU-file mode (most recently used files)
-&quot;     - MRU-command mode (most recently used command-lines)
-&quot;     - Favorite-file mode
+&quot;     - MRU-File mode (most recently used files)
+&quot;     - MRU-Command mode (most recently used command-lines)
+&quot;     - Bookmark mode
 &quot;     - Tag mode (yet another :tag command)
-&quot;     - Tagged-file mode (files which are included in current tags)
+&quot;     - Tagged-File mode (files which are included in current tags)
 &quot;
 &quot;   Fuzzyfinder supports the multibyte.
 &quot;
@@ -56,14 +56,14 @@
 &quot;   Starting Fuzzyfinder:
 &quot;     You can start Fuzzyfinder by the following commands:
 &quot;
-&quot;       :FuzzyFinderBuffer      - launchs buffer-mode Fuzzyfinder.
-&quot;       :FuzzyFinderFile        - launchs file-mode Fuzzyfinder.
-&quot;       :FuzzyFinderDir         - launchs directory-mode Fuzzyfinder.
-&quot;       :FuzzyFinderMruFile     - launchs MRU-file-mode Fuzzyfinder.
-&quot;       :FuzzyFinderMruCmd      - launchs MRU-command-mode Fuzzyfinder.
-&quot;       :FuzzyFinderFavFile     - launchs favorite-file-mode Fuzzyfinder.
-&quot;       :FuzzyFinderTag         - launchs tag-mode Fuzzyfinder.
-&quot;       :FuzzyFinderTaggedFile  - launchs tagged-file-mode Fuzzyfinder.
+&quot;       :FuzzyFinderBuffer      - launchs Fuzzyfinder as Buffer mode.
+&quot;       :FuzzyFinderFile        - launchs Fuzzyfinder as File mode.
+&quot;       :FuzzyFinderDir         - launchs Fuzzyfinder as Directory mode.
+&quot;       :FuzzyFinderMruFile     - launchs Fuzzyfinder as MRU-File mode.
+&quot;       :FuzzyFinderMruCmd      - launchs Fuzzyfinder as MRU-Command mode.
+&quot;       :FuzzyFinderBookmark    - launchs Fuzzyfinder as Bookmark mode.
+&quot;       :FuzzyFinderTag         - launchs Fuzzyfinder as Tag mode.
+&quot;       :FuzzyFinderTaggedFile  - launchs Fuzzyfinder as Tagged-File mode.
 &quot;
 &quot;     It is recommended to map these commands. These commands can take initial
 &quot;     text as a command argument. The text will be entered after Fuzzyfinder
@@ -73,30 +73,32 @@
 &quot;
 &quot;
 &quot;   In Fuzzyfinder:
-&quot;     The entered pattern is converted to the fuzzy pattern and buffers/files
-&quot;     which match the pattern is shown in a completion menu.
+&quot;     An entered pattern is converted to a fuzzy pattern and items which match
+&quot;     the pattern is shown in a completion menu.
 &quot;
-&quot;     A completion menu is shown when you type at the end of the line and the
-&quot;     length of entered pattern is more than setting value. By default, it is
-&quot;     shown at the beginning.
+&quot;     A completion menu is shown when you type at an end of a line and the
+&quot;     length of the entered pattern is more than setting value. By default, it
+&quot;     is shown at the beginning.
 &quot;
-&quot;     If too many items (200, by default) were matched, the completion is
-&quot;     aborted to reduce nonresponse.
+&quot;     The number of items shown in the completion menu is limited (50, by
+&quot;     default) to speed up the response time.
 &quot;
-&quot;     If an item were matched with entered pattern exactly, it is shown first.
-&quot;     The item whose file name has longer prefix matching is placed upper.
-&quot;     Also, an item which matched more sequentially is placed upper. The item
-&quot;     whose index were matched with a number suffixed with entered pattern is
-&quot;     placed lower. the first item in the completion menu will be selected
-&quot;     automatically.
+&quot;     Fuzzyfinder sorts completion items with some rules:
+&quot;       - A perfect matching puts first.
+&quot;       - A sequential matching puts higher than a fragmentary matching.
+&quot;       - A backward matching puts higher than a forward matching.
+&quot;       - A short item is put higher than a long item.
+&quot;
+&quot;     Plus, Fuzzyfinder has a learning system. An item which has been
+&quot;     completed in the past with a current pattern is placed upper.
+&quot;
+&quot;     The first item in the completion menu will be selected automatically.
 &quot;
 &quot;     You can open a selected item in various ways:
 &quot;       &lt;CR&gt;  - opens in a previous window.
 &quot;       &lt;C-j&gt; - opens in a split window.
 &quot;       &lt;C-k&gt; - opens in a vertical-split window.
 &quot;       &lt;C-]&gt; - opens in a new tab page.
-&quot;     In MRU-command mode, &lt;CR&gt; executes a selected command and others just
-&quot;     put it into a command-line. These key mappings are customizable.
 &quot;
 &quot;     To cancel and return to previous window, leave Insert mode.
 &quot;
@@ -106,12 +108,12 @@
 &quot;     If you want to temporarily change whether or not to ignore case, use
 &quot;     &lt;C-t&gt;. This key mapping is customizable.
 &quot;
-&quot;   To Hide The Completion Temporarily Menu In Fuzzyfinder:
+&quot;   To Hide The Completion Menu Temporarily In Fuzzyfinder:
 &quot;     You can close it by &lt;C-e&gt; and reopen it by &lt;C-x&gt;&lt;C-u&gt;.
 &quot;
 &quot;   About Highlighting:
-&quot;     Fuzzyfinder highlights the buffer with &quot;Error&quot; group when the completion
-&quot;     item was not found or the completion process was aborted.
+&quot;     Fuzzyfinder highlights the buffer with &quot;Error&quot; group when the number of
+&quot;     completion items found is 0 or over enumerating_limit.
 &quot;
 &quot;   About Alternative Approach For Tag Jump:
 &quot;     Following mappings are replacements for :tag and &lt;C-]&gt;:
@@ -155,15 +157,24 @@
 &quot;       &quot;~/project/**/src/*t*x*t*&quot;
 &quot;       &quot;.vim/plugin/*t*x*t*&quot;
 &quot;
-&quot;   Adding Favorite Files:
-&quot;     You can add a favorite file by the following commands:
+&quot;   About Bookmark Mode:
+&quot;     You can jump to a line you have added to bookmarks beforehand.
+&quot;     Fuzzyfinder adjusts a line number for jump. If a line of bookmarked
+&quot;     position does not match to a pattern when the bookmark was added,
+&quot;     Fuzzyfinder searches a matching line around bookmarked position. So you
+&quot;     can jump to a bookmarked line even if the line is out of bookmarked
+&quot;     position. If you want to jump to bookmarked line number, set
+&quot;     g:FuzzyFinderOptions.Bookmark.searching_range option to 0.
+&quot;
+&quot;   Adding Bookmark:
+&quot;     You can add a cursor line to bookmarks by the following commands:
 &quot;
-&quot;       :FuzzyFinderAddFavFile {filename}
+&quot;       :FuzzyFinderAddBookmark
 &quot;
-&quot;     If you do not specify the filename, current file name is used.
+&quot;     Execute that command and you will be prompted to enter a bookmark name.
 &quot;
 &quot;   About Information File:
-&quot;     Fuzzyfinder writes information of the MRU, favorite, etc to the file by
+&quot;     Fuzzyfinder writes information of the MRU, bookmark, etc to the file by
 &quot;     default (~/.vimfuzzyfinder).
 
 &quot;     :FuzzyFinderEditInfo command is helpful in editing your information
@@ -171,8 +182,9 @@
 &quot;     Write the buffer and the information file will be updated.
 &quot;
 &quot;   About Cache:
-&quot;     Once a cache was created, It is not updated automatically to improve
-&quot;     response by default. To update it, use :FuzzyFinderRemoveCache command.
+&quot;     Once a cache was created, It is not automatically updated to speed up
+&quot;     the response time by default. To update it, use :FuzzyFinderRemoveCache
+&quot;     command.
 &quot;
 &quot;   About Migemo:
 &quot;     Migemo is a search method for Japanese language.
@@ -186,7 +198,7 @@
 &quot;
 &quot;-----------------------------------------------------------------------------
 &quot; Setting Example:
-&quot;   let g:FuzzyFinderOptions = { 'Base':{}, 'Buffer':{}, 'File':{}, 'Dir':{}, 'MruFile':{}, 'MruCmd':{}, 'FavFile':{}, 'Tag':{}, 'TaggedFile':{}}
+&quot;   let g:FuzzyFinderOptions = { 'Base':{}, 'Buffer':{}, 'File':{}, 'Dir':{}, 'MruFile':{}, 'MruCmd':{}, 'Bookmark':{}, 'Tag':{}, 'TaggedFile':{}}
 &quot;   let g:FuzzyFinderOptions.Base.ignore_case = 1
 &quot;   let g:FuzzyFinderOptions.Base.abbrev_map  = {
 &quot;         \   '\C^VR' : [
@@ -204,11 +216,11 @@
 &quot;   nnoremap &lt;silent&gt; &lt;C-k&gt;      :FuzzyFinderMruCmd&lt;CR&gt;
 &quot;   nnoremap &lt;silent&gt; &lt;C-p&gt;      :FuzzyFinderDir &lt;C-r&gt;=expand('%:p:~')[:-1-len(expand('%:p:~:t'))]&lt;CR&gt;&lt;CR&gt;
 &quot;   nnoremap &lt;silent&gt; &lt;C-f&gt;&lt;C-d&gt; :FuzzyFinderDir&lt;CR&gt;
-&quot;   nnoremap &lt;silent&gt; &lt;C-f&gt;&lt;C-f&gt; :FuzzyFinderFavFile&lt;CR&gt;
+&quot;   nnoremap &lt;silent&gt; &lt;C-b&gt;      :FuzzyFinderBookmark&lt;CR&gt;
 &quot;   nnoremap &lt;silent&gt; &lt;C-f&gt;&lt;C-t&gt; :FuzzyFinderTag!&lt;CR&gt;
 &quot;   nnoremap &lt;silent&gt; &lt;C-f&gt;&lt;C-g&gt; :FuzzyFinderTaggedFile&lt;CR&gt;
 &quot;   noremap  &lt;silent&gt; g]         :FuzzyFinderTag! &lt;C-r&gt;=expand('&lt;cword&gt;')&lt;CR&gt;&lt;CR&gt;
-&quot;   nnoremap &lt;silent&gt; &lt;C-f&gt;F     :FuzzyFinderAddFavFile&lt;CR&gt;
+&quot;   nnoremap &lt;silent&gt; &lt;C-f&gt;b     :FuzzyFinderAddBookmark&lt;CR&gt;
 &quot;   nnoremap &lt;silent&gt; &lt;C-f&gt;&lt;C-e&gt; :FuzzyFinderEditInfo&lt;CR&gt;
 &quot;
 &quot;-----------------------------------------------------------------------------
@@ -222,6 +234,42 @@
 &quot;
 &quot;-----------------------------------------------------------------------------
 &quot; ChangeLog:
+&quot;   2.19:
+&quot;     - Changed MRU-File mode that always formats completion items to be
+&quot;       relative to the home directory.
+&quot;     - Fixed a bug that a file was opened in an unintended window with Tag
+&quot;       List plugin. Thanks Alexey.
+&quot;     - Fixed a bug that garbage characters were entered when switched current
+&quot;       mode. Thanks id:lugecy.
+&quot;
+&quot;   2.18:
+&quot;     - Improved rules for the sorting of completion items.
+&quot;     - Changed not to learn a completion if an entered pattern is empty.
+&quot;     - Fixed a bug that Buffer mode did not work. Thanks ryo7000.
+&quot;
+&quot;   2.17:
+&quot;     - Introduced a learning system for the sorting of completion items.
+&quot;     - Added g:FuzzyFinderOptions.Base.learning_limit option.
+&quot;     - Changed the specification of the information file. Please remove your
+&quot;       information file for Fuzzyfinder.
+&quot;
+&quot;   2.16:
+&quot;     - Improved response time by caching in MRU-File mode.
+&quot;     - Fixed a bug in Bookmark mode that Fuzzyfinder did not jump to the
+&quot;       Bookmarked line number when Bookmarked pattern was not found.
+&quot;
+&quot;   2.15:
+&quot;     - Added Bookmark mode.
+&quot;     - Removed Favorite-file mode. Use Bookmark mode instead.
+&quot;     - Fixed not to record a entry of input() in MRU-Command mode.
+&quot;
+&quot;   2.14:
+&quot;     - Changed to show buffer status in Buffer mode.
+&quot;     - Fixed a bug that an error occurs when nonexistent buffer-name was
+&quot;       entered in Buffer mode. Thanks Maxim Kim.
+&quot;     - Added 'enumerating_limit' option. Thanks id:secondlife.
+&quot;     - Removed 'matching_limit' option. Use 'enumerating_limit' instead.
+&quot;
 &quot;   2.13:
 &quot;     - Fixed a bug that a directory disappeared when a file in that directroy
 &quot;       was being opened in File/Mru-File mode.
@@ -431,9 +479,7 @@ let loaded_fuzzyfinder = 1
 
 &quot; }}}1
 &quot;=============================================================================
-&quot; FUNCTION: {{{1
-&quot;-----------------------------------------------------------------------------
-&quot; LIST FUNCTIONS:
+&quot; FUNCTIONS: LIST ------------------------------------------------------- {{{1
 
 function! s:Unique(in)
   let sorted = sort(a:in)
@@ -494,27 +540,26 @@ function! s:FilterEx(in, expr, limit)
 endfunction
 
 &quot; 
-function! s:FilterMatching(entries, key, pattern, index, limit)
-  return s:FilterEx(a:entries, 'v:val[''' . a:key . '''] =~ ' . string(a:pattern) . ' || v:val.index == ' . a:index, a:limit)
+function! s:FilterMatching(items, key, pattern, index, limit)
+  return s:FilterEx(a:items, 'v:val[''' . a:key . '''] =~ ' . string(a:pattern) . ' || v:val.index == ' . a:index, a:limit)
 endfunction
 
-function! s:ExtendIndexToEach(in, offset)
+function! s:MapToSetSerialIndex(in, offset)
   for i in range(len(a:in))
     let a:in[i].index = i + a:offset
   endfor
   return a:in
 endfunction
 
-function! s:UpdateMruList(mrulist, new_item, key, max_item, excluded)
+function! s:UpdateMruList(mrulist, new_item, max_item, excluded)
   let result = copy(a:mrulist)
-  let result = filter(result,'v:val[a:key] != a:new_item[a:key]')
+  let result = filter(result,'v:val.word != a:new_item.word')
   let result = insert(result, a:new_item)
-  let result = filter(result, 'v:val[a:key] !~ a:excluded')
+  let result = filter(result, 'v:val.word !~ a:excluded')
   return result[0 : a:max_item - 1]
 endfunction
 
-&quot;-----------------------------------------------------------------------------
-&quot; STRING FUNCTIONS:
+&quot; FUNCTIONS: STRING ----------------------------------------------------- {{{1
 
 &quot; trims a:str and add a:mark if a length of a:str is more than a:len
 function! s:TrimLast(str, len)
@@ -557,55 +602,114 @@ function! s:ExpandTailDotSequenceToParentDir(base)
         \           '\=repeat(&quot;..&quot; . s:PATH_SEPARATOR, len(submatch(2)))', '')
 endfunction
 
-&quot;-----------------------------------------------------------------------------
-&quot; FUNCTIONS FOR COMPLETION ITEM:
+function! s:ExistsPrompt(line, prompt)
+  return  strlen(a:line) &gt;= strlen(a:prompt) &amp;&amp; a:line[:strlen(a:prompt) -1] ==# a:prompt
+endfunction
 
-function! s:FormatCompletionItem(expr, number, abbr, trim_len, time, base_pattern, evals_path_tail)
-  if a:evals_path_tail
-    let rate = s:EvaluateMatchingRate(s:SplitPath(matchstr(a:expr, '^.*[^/\\]')).tail,
-          \                           s:SplitPath(a:base_pattern).tail)
-  else
-    let rate = s:EvaluateMatchingRate(a:expr, a:base_pattern)
-  endif
-  return  {
-        \   'word'  : a:expr,
-        \   'abbr'  : s:TrimLast((a:number &gt;= 0 ? printf('%2d: ', a:number) : '') . a:abbr, a:trim_len),
-        \   'menu'  : printf('%s[%s]', (len(a:time) ? a:time . ' ' : ''), s:MakeRateStar(rate, 5)),
-        \   'ranks' : [-rate, (a:number &gt;= 0 ? a:number : a:expr)]
-        \ }
+function! s:RemovePrompt(line, prompt)
+  return a:line[(s:ExistsPrompt(a:line, a:prompt) ? strlen(a:prompt) : 0):]
 endfunction
 
-function! s:EvaluateMatchingRate(expr, pattern)
-  if a:expr == a:pattern
-    return s:MATCHING_RATE_BASE
-  endif
+function! s:RestorePrompt(line, prompt)
+  let i = 0
+  while i &lt; len(a:prompt) &amp;&amp; i &lt; len(a:line) &amp;&amp; a:prompt[i] ==# a:line[i]
+    let i += 1
+  endwhile
+  return a:prompt . a:line[i : ]
+endfunction
+
+&quot; FUNCTIONS: COMPLETION ITEM: ------------------------------------------- {{{1
+
+&quot; returns [v(1), v(2), ..., v(n) ] , v(i) &lt; v(i+1) , v(1) &gt; v(n)/2
+function! s:MakeAscendingValues(n, total)
+  let values = range(a:n, a:n * 2 - 1)
+  let sum = 0
+  for val in values
+    let sum += val
+  endfor
+  return map(values, 'v:val * a:total / sum')
+endfunction
+
+&quot; a range of return value is [0, s:MATCHING_RATE_BASE]
+function! s:EvaluateMatchingRate(word, base)
   let rate = 0
-  let rate_increment = (s:MATCHING_RATE_BASE * 9) / (len(a:pattern) * 10) &quot; zero divide ok
-  let matched = 1
-  let i_pattern = 0
-  for i_expr in range(len(a:expr))
-    if a:expr[i_expr] == a:pattern[i_pattern]
-      let rate += rate_increment
+  let scores = s:MakeAscendingValues(len(a:word), s:MATCHING_RATE_BASE)
+  let matched = 0
+  let skip_penalty = 1
+  let i_base = 0
+  for i_word in range(len(a:word))
+    if i_base &gt;= len(a:base)
+      let skip_penalty = skip_penalty * 2
+      break
+    elseif a:word[i_word] == a:base[i_base]
+      let rate += scores[i_word]
       let matched = 1
-      let i_pattern += 1
-      if i_pattern &gt;= len(a:pattern)
-        break
-      endif
+      let i_base += 1
     elseif matched
-      let rate_increment = rate_increment / 2
+      let skip_penalty = skip_penalty * 2
       let matched = 0
     endif
   endfor
-  return rate
+  return rate / skip_penalty
 endfunction
 
-function! s:MakeRateStar(rate, base)
-  let len = (a:base * a:rate) / s:MATCHING_RATE_BASE
-  return repeat('*', len) . repeat('.', a:base - len)
+&quot; 
+function! s:EvaluateLearningRank(word, stats)
+  for i in range(len(a:stats))
+    if a:stats[i].word ==# a:word
+      return i
+    endif
+  endfor
+  return len(a:stats)
 endfunction
 
-&quot;-----------------------------------------------------------------------------
-&quot; MISC FUNCTIONS:
+
+&quot; FUNCTIONS: COMMANDLINE ------------------------------------------------ {{{1
+
+function! s:EchoWithHl(msg, hl)
+  execute &quot;echohl &quot; . a:hl
+  echo a:msg
+  echohl None
+endfunction
+
+function! s:InputHl(prompt, text, hl)
+  execute &quot;echohl &quot; . a:hl
+  let s = input(a:prompt, a:text)
+  echohl None
+  return s
+endfunction
+
+&quot; FUNCTIONS: FUZZYFIDNER WINDOW ----------------------------------------- {{{1
+
+function! s:HighlightPrompt(prompt, highlight)
+  syntax clear
+  execute printf('syntax match %s /^\V%s/', a:highlight, escape(a:prompt, '\'))
+endfunction
+
+function! s:HighlightError()
+  syntax clear
+  syntax match Error  /^.*$/
+endfunction
+
+&quot; FUNCTIONS: TAG -------------------------------------------------------- {{{1
+
+function! s:GetTagList(tagfile)
+  let result = map(readfile(a:tagfile), 'matchstr(v:val, ''^[^!\t][^\t]*'')')
+  return filter(result, 'v:val =~ ''\S''')
+endfunction
+
+function! s:GetTaggedFileList(tagfile)
+  execute 'cd ' . fnamemodify(a:tagfile, ':h')
+  let result = map(readfile(a:tagfile), 'fnamemodify(matchstr(v:val, ''^[^!\t][^\t]*\t\zs[^\t]\+''), '':p:~'')')
+  cd -
+  return filter(result, 'v:val =~ ''[^/\\ ]$''')
+endfunction
+
+function! s:GetCurrentTagFiles()
+  return sort(filter(map(tagfiles(), 'fnamemodify(v:val, '':p'')'), 'filereadable(v:val)'))
+endfunction
+
+&quot; FUNCTIONS: MISC ------------------------------------------------------- {{{1
 
 function! s:IsAvailableMode(mode)
   return exists('a:mode.mode_available') &amp;&amp; a:mode.mode_available
@@ -631,17 +735,13 @@ function! s:OnCmdCR()
     call m.on_command_pre(getcmdtype() . getcmdline())
   endfor
   &quot; lets last entry become the newest in the history
-  if getcmdtype() =~ '[:/=@]'
-    call histadd(getcmdtype(), getcmdline())
-  endif
-
+  call histadd(getcmdtype(), getcmdline())
   &quot; this is not mapped again (:help recursive_mapping)
   return &quot;\&lt;CR&gt;&quot;
 endfunction
 
 function! s:ExpandAbbrevMap(base, abbrev_map)
   let result = [a:base]
-
   &quot; expand
   for [pattern, sub_list] in items(a:abbrev_map)
     let exprs = result
@@ -650,7 +750,6 @@ function! s:ExpandAbbrevMap(base, abbrev_map)
       let result += map(copy(sub_list), 'substitute(expr, pattern, v:val, &quot;g&quot;)')
     endfor
   endfor
-
   return s:Unique(result)
 endfunction
 
@@ -659,14 +758,12 @@ function! s:ExpandEx(dir)
   if a:dir !~ '\S'
     return ['']
   endif
-
   &quot; [ [&quot;foo/&quot;], [&quot;**/&quot;, &quot;./&quot; ], [&quot;bar/&quot;] ]
   let lists = []
   for i in split(a:dir, '[/\\]\zs')
     let m = matchlist(i, '^\*\{2,}\([/\\]*\)$')
     call add(lists, (empty(m) ? [i] : [i, '.' . m[1]]))
   endfor
-
   &quot; expand wlidcards
   return split(join(map(s:CartesianProduct(lists), 'expand(join(v:val, &quot;&quot;))'), &quot;\n&quot;), &quot;\n&quot;)
 endfunction
@@ -675,10 +772,10 @@ function! s:EnumExpandedDirsEntries(dir, excluded)
   let dirs = s:ExpandEx(a:dir)
   let entries = s:Concat(map(copy(dirs), 'split(glob(v:val . &quot;.*&quot;), &quot;\n&quot;) + ' .
         \                                'split(glob(v:val . &quot;*&quot; ), &quot;\n&quot;)'))
-  if len(dirs) &lt;= 1
-    call map(entries, 'extend(s:SplitPath(v:val), { &quot;suffix&quot; : (isdirectory(v:val) ? s:PATH_SEPARATOR : &quot;&quot;), &quot;head&quot; : a:dir })')
-  else
+  if len(dirs) &gt; 1
     call map(entries, 'extend(s:SplitPath(v:val), { &quot;suffix&quot; : (isdirectory(v:val) ? s:PATH_SEPARATOR : &quot;&quot;) })')
+  else
+    call map(entries, 'extend(s:SplitPath(v:val), { &quot;suffix&quot; : (isdirectory(v:val) ? s:PATH_SEPARATOR : &quot;&quot;), &quot;head&quot; : a:dir })')
   endif
   if len(a:excluded)
     call filter(entries, '(v:val.head . v:val.tail . v:val.suffix) !~ a:excluded')
@@ -686,29 +783,50 @@ function! s:EnumExpandedDirsEntries(dir, excluded)
   return entries
 endfunction
 
-function! s:GetTagList(tagfile)
-  return map(readfile(a:tagfile), 'matchstr(v:val, ''^[^!\t][^\t]*'')')
+function! s:GetBufIndicator(nr)
+  if !getbufvar(a:nr, '&amp;modifiable')
+    return '[-]'
+  elseif getbufvar(a:nr, '&amp;modified')
+    return '[+]'
+  elseif getbufvar(a:nr, '&amp;readonly')
+    return '[R]'
+  else
+    return '   '
+  endif
 endfunction
 
-function! s:GetTaggedFileList(tagfile)
-  execute 'cd ' . fnamemodify(a:tagfile, ':h')
-  let result = map(readfile(a:tagfile), 'fnamemodify(matchstr(v:val, ''^[^!\t][^\t]*\t\zs[^\t]\+''), '':p:~'')')
-  cd -
-  return result
+function! s:ModifyWordAsFilename(item, mods)
+  let a:item.word = fnamemodify(a:item.word, a:mods)
+  return a:item
 endfunction
 
-function! s:HighlightPrompt(prompt, highlight)
-  syntax clear
-  execute printf('syntax match %s /^\V%s/', a:highlight, escape(a:prompt, '\'))
+function! s:SetFormattedTimeToMenu(item, format)
+  let a:item.menu = strftime(a:format, a:item.time)
+  return a:item
 endfunction
 
-function! s:HighlightError()
-  syntax clear
-  syntax match Error  /^.*$/
+function! s:SetRanks(item, eval_word, eval_base, stats)
+  &quot;let eval_word = (a:is_path ? s:SplitPath(matchstr(a:item.word, '^.*[^/\\]')).tail : a:item.word)
+  &quot;let eval_base = (a:is_path ? s:SplitPath(a:base).tail : a:base)
+  let rank_perfect = (a:eval_word == a:eval_base ? 0 : 1)
+  if a:eval_word == a:eval_base
+    let rank_perfect = 1
+    let rank_matching = 0
+  else
+    let rank_perfect = 2
+    let rank_matching = -s:EvaluateMatchingRate(a:eval_word, a:eval_base)
+  endif
+  let a:item.ranks = [ rank_perfect, s:EvaluateLearningRank(a:item.word, a:stats), rank_matching, a:item.index ]
+  return a:item
+endfunction
+
+function! s:SetFormattedAbbr(item, key, trim_len)
+  let a:item.abbr = s:TrimLast(printf('%3d: %s', a:item.index, a:item[a:key]), a:trim_len)
+  return a:item
 endfunction
 
 function! s:CompareTimeDescending(i1, i2)
-      return a:i1.time == a:i2.time ? 0 : a:i1.time &gt; a:i2.time ? -1 : +1
+  return a:i1.time == a:i2.time ? 0 : a:i1.time &gt; a:i2.time ? -1 : +1
 endfunction
 
 function! s:CompareRanks(i1, i2)
@@ -724,40 +842,77 @@ function! s:CompareRanks(i1, i2)
   return 0
 endfunction
 
-function! s:GetCurrentTagFiles()
-  return sort(filter(map(tagfiles(), 'fnamemodify(v:val, '':p'')'), 'filereadable(v:val)'))
+function! s:GetLinePattern(lnum)
+  return '\C\V\^' . escape(getline(a:lnum), '\') . '\$'
+endfunction
+
+&quot; opens a:path and jumps to the line matching to a:pattern from a:lnum within
+&quot; a:range. if not found, jumps to a:lnum.
+function! s:JumpToBookmark(path, mode, pattern, lnum, range)
+  call s:OpenFile(a:path, a:mode)
+  let ln = a:lnum
+  for i in range(0, a:range)
+    if a:lnum + i &lt;= line('$') &amp;&amp; getline(a:lnum + i) =~ a:pattern
+      let ln += i
+      break
+    elseif a:lnum - i &gt;= 1 &amp;&amp; getline(a:lnum - i) =~ a:pattern
+      let ln -= i
+      break
+    endif
+  endfor
+  call cursor(ln, 0)
+  normal! zvzz
+endfunction
+
+function! s:OpenBuffer(nr, mode)
+  execute printf([
+        \   ':%sbuffer',
+        \   ':%ssbuffer',
+        \   ':vertical :%ssbuffer',
+        \   ':tab :%ssbuffer',
+        \ ][a:mode], a:nr)
+endfunction
+
+function! s:OpenFile(path, mode)
+  let nr = bufnr('^' . a:path . '$')
+  if nr &gt; -1
+    call s:OpenBuffer(nr, a:mode)
+  else
+    execute [
+          \   ':edit ',
+          \   ':split ',
+          \   ':vsplit ',
+          \   ':tabedit ',
+          \ ][a:mode] . s:EscapeFilename(a:path)
+  endif
 endfunction
 
 &quot; }}}1
 &quot;=============================================================================
-&quot; OBJECT: {{{1
-&quot;-----------------------------------------------------------------------------
+&quot; OBJECT: g:FuzzyFinderMode.Base ---------------------------------------- {{{1
 let g:FuzzyFinderMode = { 'Base' : {} }
 
-function! g:FuzzyFinderMode.Base.launch(initial_text, partial_matching, prev_bufnr, tag_files)
+function! g:FuzzyFinderMode.Base.launch(initial_text, partial_matching)
   &quot; initializes this object
   call self.extend_options()
   let self.partial_matching = a:partial_matching
-  let self.prev_bufnr = a:prev_bufnr
-  let self.tag_files = a:tag_files &quot; to get local value of current buffer
+  let self.prev_bufnr = bufnr('%')
   let self.last_col = -1
   call s:InfoFileManager.load()
   if !s:IsAvailableMode(self)
     echo 'This mode is not available: ' . self.to_str()
     return
   endif
-
+  call self.on_mode_enter_pre()
   call s:WindowManager.activate(self.make_complete_func('CompleteFunc'))
   call s:OptionManager.set('completeopt', 'menuone')
   call s:OptionManager.set('ignorecase', self.ignore_case)
-
   &quot; local autocommands
   augroup FuzzyfinderLocal
     autocmd!
     execute 'autocmd CursorMovedI &lt;buffer&gt;        call ' . self.to_str('on_cursor_moved_i()')
     execute 'autocmd InsertLeave  &lt;buffer&gt; nested call ' . self.to_str('on_insert_leave()'  )
   augroup END
-
   &quot; local mapping
   for [lhs, rhs] in [
         \   [ self.key_open       , self.to_str('on_cr(0, 0)'            ) ],
@@ -773,50 +928,43 @@ function! g:FuzzyFinderMode.Base.launch(initial_text, partial_matching, prev_buf
     &quot; hacks to be able to use feedkeys().
     execute printf('inoremap &lt;buffer&gt; &lt;silent&gt; %s &lt;C-r&gt;=%s ? &quot;&quot; : &quot;&quot;&lt;CR&gt;', lhs, rhs)
   endfor
-
-  call self.on_mode_enter()
-
   &quot; Starts Insert mode and makes CursorMovedI event now. Command prompt is
   &quot; needed to forces a completion menu to update every typing.
   call setline(1, self.prompt . a:initial_text)
+  call self.on_mode_enter_post()
   call feedkeys(&quot;A&quot;, 'n') &quot; startinsert! does not work in InsertLeave handler
 endfunction
 
 function! g:FuzzyFinderMode.Base.on_cursor_moved_i()
-  let ln = getline('.')
-  let cl = col('.')
-  if !self.exists_prompt(ln)
-    &quot; if command prompt is removed
-    &quot;call setline('.', self.prompt . ln)
-    call setline('.', self.restore_prompt(ln))
-    call feedkeys(repeat(&quot;\&lt;Right&gt;&quot;, len(getline('.')) - len(ln)), 'n')
-  elseif cl &lt;= len(self.prompt)
+  if !s:ExistsPrompt(getline('.'), self.prompt)
+    call setline('.', s:RestorePrompt(getline('.'), self.prompt))
+    call feedkeys(&quot;\&lt;End&gt;&quot;, 'n')
+  elseif col('.') &lt;= len(self.prompt)
     &quot; if the cursor is moved before command prompt
-    call feedkeys(repeat(&quot;\&lt;Right&gt;&quot;, len(self.prompt) - cl + 1), 'n')
-  elseif cl &gt; strlen(ln) &amp;&amp; cl != self.last_col
+    call feedkeys(repeat(&quot;\&lt;Right&gt;&quot;, len(self.prompt) - col('.') + 1), 'n')
+  elseif col('.') &gt; strlen(getline('.')) &amp;&amp; col('.') != self.last_col
     &quot; if the cursor is placed on the end of the line and has been actually moved.
-    let self.last_col = cl
+    let self.last_col = col('.')
+    let self.last_base = s:RemovePrompt(getline('.'), self.prompt)
     call feedkeys(&quot;\&lt;C-x&gt;\&lt;C-u&gt;&quot;, 'n')
   endif
 endfunction
 
 function! g:FuzzyFinderMode.Base.on_insert_leave()
-  let text = getline('.')
-  call self.on_mode_leave()
-  call self.empty_cache_if_existed(0)
+  let last_pattern = s:RemovePrompt(getline('.'), self.prompt)
   call s:OptionManager.restore_all()
   call s:WindowManager.deactivate()
-
+  if exists('s:reserved_command')
+    call self.on_open(s:reserved_command[0], s:reserved_command[1])
+    unlet s:reserved_command
+  endif
+  call self.on_mode_leave_post()
+  call self.empty_cache_if_existed(0)
   &quot; switchs to next mode, or finishes fuzzyfinder.
   if exists('s:reserved_switch_mode')
     let m = self.next_mode(s:reserved_switch_mode &lt; 0)
-    call m.launch(self.remove_prompt(text), self.partial_matching, self.prev_bufnr, self.tag_files)
+    call m.launch(last_pattern, self.partial_matching)
     unlet s:reserved_switch_mode
-  else
-    if exists('s:reserved_command')
-      call feedkeys(self.on_open(s:reserved_command[0], s:reserved_command[1]), 'n')
-      unlet s:reserved_command
-    endif
   endif
 endfunction
 
@@ -829,13 +977,19 @@ endfunction
 function! g:FuzzyFinderMode.Base.on_command_pre(cmd)
 endfunction
 
-function! g:FuzzyFinderMode.Base.on_cr(index, check_dir)
+function! g:FuzzyFinderMode.Base.on_cr(index, dir_check)
   if pumvisible()
     call feedkeys(printf(&quot;\&lt;C-y&gt;\&lt;C-r&gt;=%s(%d, 1) ? '' : ''\&lt;CR&gt;&quot;, self.to_str('on_cr'), a:index), 'n')
-  elseif !a:check_dir || getline('.') !~ '[/\\]$'
-    let s:reserved_command = [self.remove_prompt(getline('.')), a:index]
-    call feedkeys(&quot;\&lt;Esc&gt;&quot;, 'n')
+    return
+  endif
+  if !empty(self.last_base)
+    call self.add_stat(self.last_base, s:RemovePrompt(getline('.'), self.prompt))
+  endif
+  if a:dir_check &amp;&amp; getline('.') =~ '[/\\]$'
+    return
   endif
+  let s:reserved_command = [s:RemovePrompt(getline('.'), self.prompt), a:index]
+  call feedkeys(&quot;\&lt;Esc&gt;&quot;, 'n') &quot; stopinsert behavior is strange...
 endfunction
 
 function! g:FuzzyFinderMode.Base.on_bs()
@@ -846,24 +1000,25 @@ function! g:FuzzyFinderMode.Base.on_bs()
   call feedkeys((pumvisible() ? &quot;\&lt;C-e&gt;&quot; : &quot;&quot;) . repeat(&quot;\&lt;BS&gt;&quot;, bs_count), 'n')
 endfunction
 
-function! g:FuzzyFinderMode.Base.on_mode_enter()
+&quot; Before entering Fuzzyfinder buffer. This function should return in a short time.
+function! g:FuzzyFinderMode.Base.on_mode_enter_pre()
 endfunction
 
-function! g:FuzzyFinderMode.Base.on_mode_leave()
+&quot; After entering Fuzzyfinder buffer.
+function! g:FuzzyFinderMode.Base.on_mode_enter_post()
+endfunction
+
+&quot; After leaving Fuzzyfinder buffer.
+function! g:FuzzyFinderMode.Base.on_mode_leave_post()
 endfunction
 
 function! g:FuzzyFinderMode.Base.on_open(expr, mode)
-  return [
-        \   ':edit ',
-        \   ':split ',
-        \   ':vsplit ',
-        \   ':tabedit ',
-        \ ][a:mode] . s:EscapeFilename(a:expr) . &quot;\&lt;CR&gt;&quot;
+  call s:OpenFile(a:expr, a:mode)
 endfunction
 
 function! g:FuzzyFinderMode.Base.on_switch_mode(next_prev)
   let s:reserved_switch_mode = a:next_prev
-  call feedkeys(&quot;\&lt;Esc&gt;&quot;, 'n')
+  call feedkeys(&quot;\&lt;Esc&gt;&quot;, 'n') &quot; stopinsert behavior is strange...
 endfunction
 
 function! g:FuzzyFinderMode.Base.on_switch_ignore_case()
@@ -873,35 +1028,51 @@ function! g:FuzzyFinderMode.Base.on_switch_ignore_case()
   call self.on_cursor_moved_i()
 endfunction
 
-&quot; export string list
+&quot; export mode-specific information as string list
 function! g:FuzzyFinderMode.Base.serialize_info()
-  let header = self.to_key() . &quot;\t&quot;
-  return map(copy(self.info), 'header . string(v:val)')
+  let header_data  = self.to_key() . &quot;.data\t&quot;
+  let header_stats = self.to_key() . &quot;.stats\t&quot;
+  return  map(copy(self.data ), 'header_data  . string(v:val)') +
+        \ map(copy(self.stats), 'header_stats . string(v:val)')
 endfunction
 
-&quot; import related items from string list
+&quot; import mode-specific information from string list
 function! g:FuzzyFinderMode.Base.deserialize_info(lines)
-  let header = self.to_key() . &quot;\t&quot;
-  let self.info = map(filter(copy(a:lines), 'v:val[: len(header) - 1] ==# header'),
-        \             'eval(v:val[len(header) :])')
+  let header_data  = self.to_key() . &quot;.data\t&quot;
+  let header_stats = self.to_key() . &quot;.stats\t&quot;
+  let self.data  = map(filter(copy(a:lines), 'v:val[: len(header_data ) - 1] ==# header_data '),
+        \              'eval(v:val[len(header_data ) :])')
+  let self.stats = map(filter(copy(a:lines), 'v:val[: len(header_stats) - 1] ==# header_stats'),
+        \              'eval(v:val[len(header_stats) :])')
+  call filter(self.stats, '!empty(v:val.base)') &quot; NOTE: remove this line someday
+endfunction
+
+function! g:FuzzyFinderMode.Base.add_stat(base, word)
+  call s:InfoFileManager.load()
+  let stat = { 'base' : a:base, 'word' : a:word }
+  call filter(self.stats, 'v:val !=# stat')
+  call insert(self.stats, stat)
+  let self.stats = self.stats[0 : self.learning_limit - 1]
+  call s:InfoFileManager.save()
 endfunction
 
 function! g:FuzzyFinderMode.Base.complete(findstart, base)
   if a:findstart
     return 0
-  elseif  !self.exists_prompt(a:base) || len(self.remove_prompt(a:base)) &lt; self.min_length
+  elseif  !s:ExistsPrompt(a:base, self.prompt) || len(s:RemovePrompt(a:base, self.prompt)) &lt; self.min_length
     return []
   endif
   call s:HighlightPrompt(self.prompt, self.prompt_highlight)
   &quot; FIXME: ExpandAbbrevMap duplicates index
   let result = []
-  for expanded_base in s:ExpandAbbrevMap(self.remove_prompt(a:base), self.abbrev_map)
+  for expanded_base in s:ExpandAbbrevMap(s:RemovePrompt(a:base, self.prompt), self.abbrev_map)
     let result += self.on_complete(expanded_base)
   endfor
   call sort(result, 's:CompareRanks')
-  if empty(result)
+  if empty(result) || len(result) &gt;= self.enumerating_limit
     call s:HighlightError()
-  else
+  endif
+  if !empty(result)
     call feedkeys(&quot;\&lt;C-p&gt;\&lt;Down&gt;&quot;, 'n')
   endif
   return result
@@ -932,58 +1103,24 @@ function! g:FuzzyFinderMode.Base.make_pattern(base)
         let wi .= char
       endif
     endfor
-
     if wi !~ '[*?]$'
       let wi .= '*'
     endif
-
     let re = s:ConvertWildcardToRegexp(wi)
-
     if self.migemo_support &amp;&amp; a:base !~ '[^\x01-\x7e]'
       let re .= '\|\m.*' . substitute(migemo(a:base), '\\_s\*', '.*', 'g') . '.*'
     endif
-
     return { 'base': a:base, 'wi':wi, 're': re }
   endif
 endfunction
 
-&quot; glob with caching-feature, etc.
-function! g:FuzzyFinderMode.Base.glob_ex(dir, file, excluded, index, matching_limit)
-  let key = fnamemodify(a:dir, ':p')
-  call extend(self, { 'cache' : {} }, 'keep')
-  if !exists('self.cache[key]')
-    echo 'Caching file list...'
-    let self.cache[key] = s:EnumExpandedDirsEntries(key, a:excluded)
-    call s:ExtendIndexToEach(self.cache[key], 1)
-  endif
-  echo 'Filtering file list...'
-  &quot;return map(s:FilterEx(self.cache[key], 'v:val.tail =~ ' . string(a:file), a:matching_limit),
-  return map(s:FilterMatching(self.cache[key], 'tail', a:file, a:index, a:matching_limit),
-        \ '{ &quot;index&quot; : v:val.index, &quot;path&quot; : (v:val.head == key ? a:dir : v:val.head) . v:val.tail . v:val.suffix }')
-endfunction
-
-function! g:FuzzyFinderMode.Base.glob_dir_ex(dir, file, excluded, index, matching_limit)
-  let key = fnamemodify(a:dir, ':p')
-  call extend(self, { 'cache' : {} }, 'keep')
-  if !exists('self.cache[key]')
-    echo 'Caching file list...'
-    let self.cache[key] = filter(s:EnumExpandedDirsEntries(key, a:excluded), 'len(v:val.suffix)')
-    call insert(self.cache[key], { 'head' : key, 'tail' : '..', 'suffix' : s:PATH_SEPARATOR })
-    call insert(self.cache[key], { 'head' : key, 'tail' : '.' , 'suffix' : '' })
-    call s:ExtendIndexToEach(self.cache[key], 1)
-  endif
-  echo 'Filtering file list...'
-  &quot;return map(s:FilterEx(self.cache[key], 'v:val.tail =~ ' . string(a:file), a:matching_limit),
-  return map(s:FilterMatching(self.cache[key], 'tail', a:file, a:index, a:matching_limit),
-        \ '{ &quot;index&quot; : v:val.index, &quot;path&quot; : (v:val.head == key ? a:dir : v:val.head) . v:val.tail . v:val.suffix }')
+function! g:FuzzyFinderMode.Base.get_filtered_stats(base)
+  return filter(copy(self.stats), 'v:val.base ==# a:base')
 endfunction
 
 function! g:FuzzyFinderMode.Base.empty_cache_if_existed(force)
   if exists('self.cache') &amp;&amp; (a:force || !exists('self.lasting_cache') || !self.lasting_cache)
     unlet self.cache
-    &quot;let self.cache = (type(self.cache) == type({}) ? {} :
-    &quot;      \           type(self.cache) == type([]) ? [] :
-    &quot;      \           type(self.cache) == type('') ? '' : 0)
   endif
 endfunction
 
@@ -998,7 +1135,6 @@ endfunction
 
 &quot; takes in g:FuzzyFinderOptions
 function! g:FuzzyFinderMode.Base.extend_options()
-  let n = filter(keys(g:FuzzyFinderMode), 'g:FuzzyFinderMode[v:val] is self')[0]
   call extend(self, g:FuzzyFinderOptions.Base, 'force')
   call extend(self, g:FuzzyFinderOptions[self.to_key()], 'force')
 endfunction
@@ -1016,47 +1152,32 @@ function! g:FuzzyFinderMode.Base.next_mode(rev)
   &quot; vim crashed using map()
 endfunction
 
-function! g:FuzzyFinderMode.Base.exists_prompt(in)
-  return  strlen(a:in) &gt;= strlen(self.prompt) &amp;&amp; a:in[:strlen(self.prompt) -1] ==# self.prompt
-endfunction
-
-function! g:FuzzyFinderMode.Base.remove_prompt(in)
-  return a:in[(self.exists_prompt(a:in) ? strlen(self.prompt) : 0):]
-endfunction
-
-function! g:FuzzyFinderMode.Base.restore_prompt(in)
-  let i = 0
-  while i &lt; len(self.prompt) &amp;&amp; i &lt; len(a:in) &amp;&amp; self.prompt[i] ==# a:in[i]
-    let i += 1
-  endwhile
-  return self.prompt . a:in[i : ]
-endfunction
-
-&quot;-----------------------------------------------------------------------------
+&quot; OBJECT: g:FuzzyFinderMode.Buffer -------------------------------------- {{{1
 let g:FuzzyFinderMode.Buffer = copy(g:FuzzyFinderMode.Base)
 
 function! g:FuzzyFinderMode.Buffer.on_complete(base)
   let patterns = self.make_pattern(a:base)
-  let result = s:FilterMatching(self.cache, 'path', patterns.re, s:SuffixNumber(patterns.base), 0)
-  return map(result, 's:FormatCompletionItem(v:val.path, v:val.index, v:val.path, self.trim_length, v:val.time, a:base, 1)')
+  let base_tail = s:SplitPath(a:base).tail
+  let stats = self.get_filtered_stats(a:base)
+  let result = s:FilterMatching(self.items, 'word', patterns.re, s:SuffixNumber(patterns.base), self.enumerating_limit)
+  return map(result, 's:SetRanks(v:val, s:SplitPath(matchstr(v:val.word, ''^.*[^/\\]'')).tail, base_tail, stats)')
 endfunction
 
 function! g:FuzzyFinderMode.Buffer.on_open(expr, mode)
-  &quot; attempts to convert the path to the number for handling unnamed buffer
-  return printf([
-        \   ':%sbuffer',
-        \   ':%ssbuffer',
-        \   ':vertical :%ssbuffer',
-        \   ':tab :%ssbuffer',
-        \ ][a:mode] . &quot;\&lt;CR&gt;&quot;, filter(self.cache, 'v:val.path == a:expr')[0].buf_nr)
+  &quot; filter the selected item to get the buffer number for handling unnamed buffer
+  call filter(self.items, 'v:val.word ==# a:expr')
+  if !empty(self.items)
+    call s:OpenBuffer(self.items[0].buf_nr, a:mode)
+  endif
 endfunction
 
-function! g:FuzzyFinderMode.Buffer.on_mode_enter()
-  let self.cache = map(filter(range(1, bufnr('$')), 'buflisted(v:val) &amp;&amp; v:val != self.prev_bufnr'),
+function! g:FuzzyFinderMode.Buffer.on_mode_enter_post()
+  let self.items = map(filter(range(1, bufnr('$')), 'buflisted(v:val) &amp;&amp; v:val != self.prev_bufnr'),
         \              'self.make_item(v:val)')
   if self.mru_order
-    call s:ExtendIndexToEach(sort(self.cache, 's:CompareTimeDescending'), 1)
+    call s:MapToSetSerialIndex(sort(self.items, 's:CompareTimeDescending'), 1)
   endif
+  call map(self.items, 's:SetFormattedAbbr(v:val, &quot;abbr&quot;, self.trim_length)')
 endfunction
 
 function! g:FuzzyFinderMode.Buffer.on_buf_enter()
@@ -1068,69 +1189,101 @@ function! g:FuzzyFinderMode.Buffer.on_buf_write_post()
 endfunction
 
 function! g:FuzzyFinderMode.Buffer.update_buf_times()
-  if !exists('self.buf_times')
-    let self.buf_times = {}
-  endif
+  call extend(self, { 'buf_times' : {} }, 'keep')
   let self.buf_times[bufnr('%')] = localtime()
 endfunction
 
 function! g:FuzzyFinderMode.Buffer.make_item(nr)
+  let path = (empty(bufname(a:nr)) ? '[No Name]' : fnamemodify(bufname(a:nr), ':~:.'))
+  let time = (exists('self.buf_times[a:nr]') ? self.buf_times[a:nr] : 0)
   return  {
         \   'index'  : a:nr,
         \   'buf_nr' : a:nr,
-        \   'path'   : empty(bufname(a:nr)) ? '[No Name]' : fnamemodify(bufname(a:nr), ':~:.'),
-        \   'time'   : (exists('self.buf_times[a:nr]') ? strftime(self.time_format, self.buf_times[a:nr]) : ''),
+        \   'word'   : path,
+        \   'abbr'   : s:GetBufIndicator(a:nr) . ' ' . path,
+        \   'menu'   : strftime(self.time_format, time),
+        \   'time'   : time,
         \ }
 endfunction
 
-&quot;-----------------------------------------------------------------------------
+&quot;  'edit'/'split'/'vsplit'/'tabedit'
+function! g:FuzzyFinderMode.Buffer.jump_to(item, cmd_open)
+endfunction
+
+&quot; OBJECT: g:FuzzyFinderMode.File ---------------------------------------- {{{1
 let g:FuzzyFinderMode.File = copy(g:FuzzyFinderMode.Base)
 
 function! g:FuzzyFinderMode.File.on_complete(base)
   let base = s:ExpandTailDotSequenceToParentDir(a:base)
   let patterns = map(s:SplitPath(base), 'self.make_pattern(v:val)')
-  let result = self.glob_ex(patterns.head.base, patterns.tail.re, self.excluded_path, s:SuffixNumber(patterns.tail.base), self.matching_limit)
-  let result = filter(result, 'bufnr(&quot;^&quot; . v:val.path . &quot;$&quot;) != self.prev_bufnr')
-  if len(result) &gt;= self.matching_limit
-    call s:HighlightError()
+  let stats = self.get_filtered_stats(a:base)
+  let result = self.cached_glob(patterns.head.base, patterns.tail.re, self.excluded_path, s:SuffixNumber(patterns.tail.base), self.enumerating_limit)
+  let result = filter(result, 'bufnr(&quot;^&quot; . v:val.word . &quot;$&quot;) != self.prev_bufnr')
+  return map(result, 's:SetRanks(v:val, s:SplitPath(matchstr(v:val.word, ''^.*[^/\\]'')).tail, patterns.tail.base, stats)')
+endfunction
+
+function! g:FuzzyFinderMode.File.cached_glob(dir, file, excluded, index, limit)
+  let key = fnamemodify(a:dir, ':p')
+  call extend(self, { 'cache' : {} }, 'keep')
+  if !exists('self.cache[key]')
+    echo 'Caching file list...'
+    let self.cache[key] = s:EnumExpandedDirsEntries(key, a:excluded)
+    call s:MapToSetSerialIndex(self.cache[key], 1)
   endif
-  return map(result, 's:FormatCompletionItem(v:val.path, v:val.index, v:val.path, self.trim_length, &quot;&quot;, base, 1)')
+  echo 'Filtering file list...'
+  let result = s:FilterMatching(self.cache[key], 'tail', a:file, a:index, a:limit)
+  call map(result, '{ &quot;index&quot; : v:val.index, &quot;word&quot; : (v:val.head == key ? a:dir : v:val.head) . v:val.tail . v:val.suffix }') 
+  return map(result, 's:SetFormattedAbbr(v:val, &quot;word&quot;, self.trim_length)') 
 endfunction
 
-&quot;-----------------------------------------------------------------------------
+&quot; OBJECT: g:FuzzyFinderMode.Dir ----------------------------------------- {{{1
 let g:FuzzyFinderMode.Dir = copy(g:FuzzyFinderMode.Base)
 
 function! g:FuzzyFinderMode.Dir.on_complete(base)
   let base = s:ExpandTailDotSequenceToParentDir(a:base)
   let patterns = map(s:SplitPath(base), 'self.make_pattern(v:val)')
-  let result = self.glob_dir_ex(patterns.head.base, patterns.tail.re, self.excluded_path, s:SuffixNumber(patterns.tail.base), 0)
-  return map(result, 's:FormatCompletionItem(v:val.path, v:val.index, v:val.path, self.trim_length, &quot;&quot;, base, 1)')
+  let stats = self.get_filtered_stats(a:base)
+  let result = self.cached_glob_dir(patterns.head.base, patterns.tail.re, self.excluded_path, s:SuffixNumber(patterns.tail.base), self.enumerating_limit)
+  return map(result, 's:SetRanks(v:val, s:SplitPath(matchstr(v:val.word, ''^.*[^/\\]'')).tail, patterns.tail.base, stats)')
 endfunction
 
 function! g:FuzzyFinderMode.Dir.on_open(expr, mode)
-  return ':cd ' . escape(a:expr, ' ') . [
-        \   &quot;\&lt;CR&gt;&quot;,
-        \   &quot;&quot;,
-        \   &quot;&quot;,
-        \   &quot;&quot;,
-        \ ][a:mode]
+  execute ':cd ' . s:EscapeFilename(a:expr)
 endfunction
 
-&quot;-----------------------------------------------------------------------------
+function! g:FuzzyFinderMode.Dir.cached_glob_dir(dir, file, excluded, index, limit)
+  let key = fnamemodify(a:dir, ':p')
+  call extend(self, { 'cache' : {} }, 'keep')
+  if !exists('self.cache[key]')
+    echo 'Caching file list...'
+    let self.cache[key] = filter(s:EnumExpandedDirsEntries(key, a:excluded), 'len(v:val.suffix)')
+    call insert(self.cache[key], { 'head' : key, 'tail' : '..', 'suffix' : s:PATH_SEPARATOR })
+    call insert(self.cache[key], { 'head' : key, 'tail' : '.' , 'suffix' : '' })
+    call s:MapToSetSerialIndex(self.cache[key], 1)
+  endif
+  echo 'Filtering file list...'
+  let result = s:FilterMatching(self.cache[key], 'tail', a:file, a:index, a:limit)
+  call map(result, '{ &quot;index&quot; : v:val.index, &quot;word&quot; : (v:val.head == key ? a:dir : v:val.head) . v:val.tail . v:val.suffix }') 
+  return map(result, 's:SetFormattedAbbr(v:val, &quot;word&quot;, self.trim_length)') 
+endfunction
+
+&quot; OBJECT: g:FuzzyFinderMode.MruFile ------------------------------------- {{{1
 let g:FuzzyFinderMode.MruFile = copy(g:FuzzyFinderMode.Base)
 
 function! g:FuzzyFinderMode.MruFile.on_complete(base)
   let patterns = self.make_pattern(a:base)
-  let result = s:FilterMatching(self.cache, 'path', patterns.re, s:SuffixNumber(patterns.base), 0)
-  return map(result, 's:FormatCompletionItem(v:val.path, v:val.index, v:val.path, self.trim_length, v:val.time, a:base, 1)')
+  let base_tail = s:SplitPath(a:base).tail
+  let stats = self.get_filtered_stats(a:base)
+  let result = s:FilterMatching(self.items, 'word', patterns.re, s:SuffixNumber(patterns.base), self.enumerating_limit)
+  return map(result, 's:SetRanks(v:val, s:SplitPath(matchstr(v:val.word, ''^.*[^/\\]'')).tail, base_tail, stats)')
 endfunction
 
-function! g:FuzzyFinderMode.MruFile.on_mode_enter()
-  let self.cache = copy(self.info)
-  let self.cache = filter(self.cache, 'bufnr(&quot;^&quot; . v:val.path . &quot;$&quot;) != self.prev_bufnr')
-  let self.cache = filter(self.cache, 'filereadable(v:val.path)')
-  let self.cache = map(self.cache, '{ &quot;path&quot; : fnamemodify(v:val.path, &quot;:~:.&quot;), &quot;time&quot; : strftime(self.time_format, v:val.time) }')
-  let self.cache = s:ExtendIndexToEach(self.cache, 1)
+function! g:FuzzyFinderMode.MruFile.on_mode_enter_post()
+  let self.items = copy(self.data)
+  let self.items = map(self.items, 'self.format_item_using_cache(v:val)')
+  let self.items = filter(self.items, '!empty(v:val) &amp;&amp; bufnr(&quot;^&quot; . v:val.word . &quot;$&quot;) != self.prev_bufnr')
+  let self.items = s:MapToSetSerialIndex(self.items, 1)
+  let self.items = map(self.items, 's:SetFormattedAbbr(v:val, &quot;word&quot;, self.trim_length)')
 endfunction
 
 function! g:FuzzyFinderMode.MruFile.on_buf_enter()
@@ -1142,161 +1295,203 @@ function! g:FuzzyFinderMode.MruFile.on_buf_write_post()
 endfunction
 
 function! g:FuzzyFinderMode.MruFile.update_info()
-  &quot;if !empty(&amp;buftype) || !filereadable(expand('%'))
-  if !empty(&amp;buftype)
+  if !empty(&amp;buftype) || expand('%') !~ '\S'
     return
   endif
   call s:InfoFileManager.load()
-  let self.info = s:UpdateMruList(self.info, { 'path' : expand('%:p'), 'time' : localtime() },
-        \                         'path', self.max_item, self.excluded_path)
+  let self.data = s:UpdateMruList(self.data, { 'word' : expand('%:p'), 'time' : localtime() },
+        \                         self.max_item, self.excluded_path)
   call s:InfoFileManager.save()
+  call self.remove_item_from_cache(expand('%:p'))
 endfunction
 
-&quot;-----------------------------------------------------------------------------
+&quot; returns empty value if invalid item
+function! g:FuzzyFinderMode.MruFile.format_item_using_cache(item)
+  call extend(self, { 'cache' : {} }, 'keep')
+  if a:item.word !~ '\S'
+    return {}
+  endif
+  if !exists('self.cache[a:item.word]')
+    let self.cache[a:item.word] =
+          \ (filereadable(a:item.word)
+          \  ? s:ModifyWordAsFilename(s:SetFormattedTimeToMenu(copy(a:item), self.time_format), ':p:~')
+          \  : {})
+  endif
+  return self.cache[a:item.word]
+endfunction
+
+function! g:FuzzyFinderMode.MruFile.remove_item_from_cache(word)
+  if !exists('self.cache')
+    return
+  endif
+  for items in values(self.cache)
+    if exists('items[a:word]')
+      unlet items[a:word]
+    endif
+  endfor
+endfunction
+
+&quot; OBJECT: g:FuzzyFinderMode.MruCmd -------------------------------------- {{{1
 let g:FuzzyFinderMode.MruCmd = copy(g:FuzzyFinderMode.Base)
 
 function! g:FuzzyFinderMode.MruCmd.on_complete(base)
   let patterns = self.make_pattern(a:base)
-  let result = s:FilterMatching(self.cache, 'command', patterns.re, s:SuffixNumber(patterns.base), 0)
-  return map(result, 's:FormatCompletionItem(v:val.command, v:val.index, v:val.command, self.trim_length, v:val.time, a:base, 0)')
+  let stats = self.get_filtered_stats(a:base)
+  let result = s:FilterMatching(self.items, 'word', patterns.re, s:SuffixNumber(patterns.base), self.enumerating_limit)
+  return map(result, 's:SetRanks(v:val, v:val.word, a:base, stats)')
 endfunction
 
 function! g:FuzzyFinderMode.MruCmd.on_open(expr, mode)
-  redraw
-  &quot; use feedkeys to remap &lt;CR&gt;
-  return a:expr . [
-        \   &quot;\&lt;C-r&gt;=feedkeys(\&quot;\\&lt;CR&gt;\&quot;, 'm')?'':''\&lt;CR&gt;&quot;,
-        \   &quot;&quot;,
-        \   &quot;&quot;,
-        \   &quot;&quot;,
-        \ ][a:mode]
+  call self.update_info(a:expr)
+  call histadd(a:expr[0], a:expr[1:])
+  call feedkeys(a:expr . &quot;\&lt;CR&gt;&quot;, 'n')
 endfunction
 
-function! g:FuzzyFinderMode.MruCmd.on_mode_enter()
-  let self.cache = s:ExtendIndexToEach(map(copy(self.info),
-        \ '{ &quot;command&quot; : v:val.command, &quot;time&quot; : strftime(self.time_format, v:val.time) }'), 1)
+function! g:FuzzyFinderMode.MruCmd.on_mode_enter_post()
+  let self.items = copy(self.data)
+  let self.items = map(self.items, 's:SetFormattedTimeToMenu(v:val, self.time_format)')
+  let self.items = s:MapToSetSerialIndex(self.items, 1)
+  let self.items = map(self.items, 's:SetFormattedAbbr(v:val, &quot;word&quot;, self.trim_length)')
 endfunction
 
 function! g:FuzzyFinderMode.MruCmd.on_command_pre(cmd)
-  call self.update_info(a:cmd)
+  if getcmdtype() =~ '^[:/?]'
+    call self.update_info(a:cmd)
+  endif
 endfunction
 
 function! g:FuzzyFinderMode.MruCmd.update_info(cmd)
   call s:InfoFileManager.load()
-  let self.info = s:UpdateMruList(self.info, { 'command' : a:cmd, 'time' : localtime() },
-        \                         'command', self.max_item, self.excluded_command)
+  let self.data = s:UpdateMruList(self.data, { 'word' : a:cmd, 'time' : localtime() },
+        \                         self.max_item, self.excluded_command)
   call s:InfoFileManager.save()
 endfunction
 
-&quot;-----------------------------------------------------------------------------
-let g:FuzzyFinderMode.FavFile = copy(g:FuzzyFinderMode.Base)
+&quot; OBJECT: g:FuzzyFinderMode.Bookmark ------------------------------------- {{{1
+let g:FuzzyFinderMode.Bookmark = copy(g:FuzzyFinderMode.Base)
 
-function! g:FuzzyFinderMode.FavFile.on_complete(base)
+function! g:FuzzyFinderMode.Bookmark.on_complete(base)
   let patterns = self.make_pattern(a:base)
-  let result = s:FilterMatching(self.cache, 'path', patterns.re, s:SuffixNumber(patterns.base), 0)
-  return map(result, 's:FormatCompletionItem(v:val.path, v:val.index, v:val.path, self.trim_length, v:val.time, a:base, 1)')
+  let stats = self.get_filtered_stats(a:base)
+  let result = s:FilterMatching(self.items, 'word', patterns.re, s:SuffixNumber(patterns.base), self.enumerating_limit)
+  return map(result, 's:SetRanks(v:val, v:val.word, a:base, stats)')
 endfunction
 
-function! g:FuzzyFinderMode.FavFile.on_mode_enter()
-  let self.cache = copy(self.info)
-  let self.cache = filter(self.cache, 'bufnr(&quot;^&quot; . v:val.path . &quot;$&quot;) != self.prev_bufnr')
-  let self.cache = map(self.cache, '{ &quot;path&quot; : fnamemodify(v:val.path, &quot;:~:.&quot;), &quot;time&quot; : strftime(self.time_format, v:val.time) }')
-  let self.cache = s:ExtendIndexToEach(self.cache, 1)
+function! g:FuzzyFinderMode.Bookmark.on_open(expr, mode)
+  call filter(self.items, 'v:val.word ==# a:expr')
+  if empty(self.items)
+    return ''
+  endif
+  call s:JumpToBookmark(self.items[0].path, a:mode, self.items[0].pattern, self.items[0].lnum, self.searching_range)
 endfunction
 
-function! g:FuzzyFinderMode.FavFile.add(in_file, adds)
-  call s:InfoFileManager.load()
-
-  let file = fnamemodify((empty(a:in_file) ? expand('%') : a:in_file), ':p:~')
+function! g:FuzzyFinderMode.Bookmark.on_mode_enter_post()
+  let self.items = copy(self.data)
+  let self.items = map(self.items, 's:SetFormattedTimeToMenu(v:val, self.time_format)')
+  let self.items = s:MapToSetSerialIndex(self.items, 1)
+  let self.items = map(self.items, 's:SetFormattedAbbr(v:val, &quot;word&quot;, self.trim_length)')
+endfunction
 
-  call filter(self.info, 'v:val.path != file')
-  if a:adds
-    call add(self.info, { 'path' : file, 'time' : localtime() })
+function! g:FuzzyFinderMode.Bookmark.bookmark_here(word)
+  if !empty(&amp;buftype) || expand('%') !~ '\S'
+    call s:EchoWithHl('Can''t bookmark this buffer.', 'WarningMsg')
+    return
   endif
-
+  let item = {
+        \   'word' : (a:word =~ '\S' ? substitute(a:word, '\n', ' ', 'g')
+        \                            : pathshorten(expand('%:p:~')) . '|' . line('.') . '| ' . getline('.')),
+        \   'path' : expand('%:p'),
+        \   'lnum' : line('.'),
+        \   'pattern' : s:GetLinePattern(line('.')),
+        \   'time' : localtime(),
+        \ }
+  let item.word = s:InputHl('Bookmark as:', item.word, 'Question')
+  if item.word !~ '\S'
+    call s:EchoWithHl('Canceled', 'WarningMsg')
+    return
+  endif
+  call s:InfoFileManager.load()
+  call insert(self.data, item)
   call s:InfoFileManager.save()
 endfunction
 
-&quot;-----------------------------------------------------------------------------
+&quot; OBJECT: g:FuzzyFinderMode.Tag ----------------------------------------- {{{1
 let g:FuzzyFinderMode.Tag = copy(g:FuzzyFinderMode.Base)
 
 function! g:FuzzyFinderMode.Tag.on_complete(base)
   let patterns = self.make_pattern(a:base)
-  let result = self.find_tag(patterns.re, self.matching_limit)
-  if len(result) &gt;= self.matching_limit
-    call s:HighlightError()
-  endif
-  return map(result, 's:FormatCompletionItem(v:val, -1, v:val, self.trim_length, &quot;&quot;, a:base, 1)')
+  let stats = self.get_filtered_stats(a:base)
+  let result = self.find_tag(patterns.re, s:SuffixNumber(patterns.base), self.enumerating_limit)
+  return map(result, 's:SetRanks(v:val, v:val.word, a:base, stats)')
 endfunction
 
 function! g:FuzzyFinderMode.Tag.on_open(expr, mode)
-  return [
+  execute [
         \   ':tjump ',
         \   ':stjump ',
         \   ':vertical :stjump ',
         \   ':tab :stjump ',
-        \ ][a:mode] . a:expr . &quot;\&lt;CR&gt;&quot;
+        \ ][a:mode] . a:expr
 endfunction
 
-function! g:FuzzyFinderMode.Tag.find_tag(pattern, matching_limit)
+function! g:FuzzyFinderMode.Tag.on_mode_enter_pre()
+  let self.tag_files = s:GetCurrentTagFiles()
+endfunction
+
+function! g:FuzzyFinderMode.Tag.find_tag(pattern, index, limit)
   if !len(self.tag_files)
     return []
   endif
-
   let key = join(self.tag_files, &quot;\n&quot;)
-
   &quot; cache not created or tags file updated? 
   call extend(self, { 'cache' : {} }, 'keep')
   if !exists('self.cache[key]') || max(map(copy(self.tag_files), 'getftime(v:val) &gt;= self.cache[key].time'))
     echo 'Caching tag list...'
-    let self.cache[key] = {
-          \   'time' : localtime(),
-          \   'data' : s:Unique(s:Concat(map(copy(self.tag_files), 's:GetTagList(v:val)'))),
-          \ }
+    let items = s:Unique(s:Concat(map(copy(self.tag_files), 's:GetTagList(v:val)')))
+    let items = s:MapToSetSerialIndex(map(items, '{ &quot;word&quot; : v:val }'), 1)
+    let self.cache[key] = { 'time'  : localtime(), 'items' : items }
   endif
-
   echo 'Filtering tag list...'
-  return s:FilterEx(self.cache[key].data, 'v:val =~ ' . string(a:pattern), a:matching_limit)
+  let result = s:FilterMatching(self.cache[key].items, 'word', a:pattern, a:index, a:limit)
+  return map(result, 's:SetFormattedAbbr(v:val, &quot;word&quot;, self.trim_length)')
 endfunction
 
-&quot;-----------------------------------------------------------------------------
+&quot; OBJECT: g:FuzzyFinderMode.TaggedFile ---------------------------------- {{{1
 let g:FuzzyFinderMode.TaggedFile = copy(g:FuzzyFinderMode.Base)
 
 function! g:FuzzyFinderMode.TaggedFile.on_complete(base)
   let patterns = self.make_pattern(a:base)
+  let base_tail = s:SplitPath(a:base).tail
+  let stats = self.get_filtered_stats(a:base)
   echo 'Making tagged file list...'
-  let result = self.find_tagged_file(patterns.re, self.matching_limit)
-  if len(result) &gt;= self.matching_limit
-    call s:HighlightError()
-  endif
-  return map(result, 's:FormatCompletionItem(v:val, -1, v:val, self.trim_length, &quot;&quot;, a:base, 1)')
+  let result = self.find_tagged_file(patterns.re, s:SuffixNumber(patterns.base), self.enumerating_limit)
+  return map(result, 's:SetRanks(v:val, s:SplitPath(matchstr(v:val.word, ''^.*[^/\\]'')).tail, base_tail, stats)')
+endfunction
+
+function! g:FuzzyFinderMode.TaggedFile.on_mode_enter_pre()
+  let self.tag_files = s:GetCurrentTagFiles()
 endfunction
 
-function! g:FuzzyFinderMode.TaggedFile.find_tagged_file(pattern, matching_limit)
+function! g:FuzzyFinderMode.TaggedFile.find_tagged_file(pattern, index, limit)
   if !len(self.tag_files)
     return []
   endif
-
   let key = join(self.tag_files, &quot;\n&quot;)
-
   &quot; cache not created or tags file updated? 
   call extend(self, { 'cache' : {} }, 'keep')
   if !exists('self.cache[key]') || max(map(copy(self.tag_files), 'getftime(v:val) &gt;= self.cache[key].time'))
     echo 'Caching tagged-file list...'
-    let self.cache[key] = {
-          \   'time' : localtime(),
-          \   'data' : s:Unique(s:Concat(map(copy(self.tag_files), 's:GetTaggedFileList(v:val)'))),
-          \ }
+    let items = s:Unique(s:Concat(map(copy(self.tag_files), 's:GetTaggedFileList(v:val)')))
+    let items = s:MapToSetSerialIndex(map(items, '{ &quot;word&quot; : v:val }'), 1)
+    let self.cache[key] = { 'time'  : localtime(), 'items' : items }
   endif
-
   echo 'Filtering tagged-file list...'
-  return s:FilterEx(map(self.cache[key].data, 'fnamemodify(v:val, '':.'')'),
-        \               'v:val =~ ' . string(a:pattern),
-        \           a:matching_limit)
-
+  call map(self.cache[key].items, 's:ModifyWordAsFilename(v:val, '':.'')')
+  let result = s:FilterMatching(self.cache[key].items, 'word', a:pattern, a:index, a:limit)
+  return map(result, 's:SetFormattedAbbr(v:val, &quot;word&quot;, self.trim_length)')
 endfunction
 
-&quot;-----------------------------------------------------------------------------
+&quot; OBJECT: s:OptionManager ----------------------------------------------- {{{1
 &quot; sets or restores temporary options
 let s:OptionManager = { 'originals' : {} }
 
@@ -1312,29 +1507,43 @@ function! s:OptionManager.restore_all()
   let self.originals = {}
 endfunction
 
-&quot;-----------------------------------------------------------------------------
+&quot; OBJECT: s:WindowManager ----------------------------------------------- {{{1
 &quot; manages buffer/window for fuzzyfinder
 let s:WindowManager = { 'buf_nr' : -1 }
 
 function! s:WindowManager.activate(complete_func)
-  let self.prev_winnr = winnr()
   let cwd = getcwd()
+  let self.buf_nr = s:Open1LineBuffer(self.buf_nr, '[Fuzzyfinder]')
+  call s:SetLocalOptionsForFuzzyfinder(cwd, a:complete_func)
+  redraw &quot; for 'lazyredraw'
+  if exists(':AutoComplPopLock') | execute ':AutoComplPopLock' | endif
+endfunction
+
+function! s:WindowManager.deactivate()
+  if exists(':AutoComplPopUnlock') | execute ':AutoComplPopUnlock' | endif
+  &quot; must close after returning to previous window
+  wincmd j
+  execute self.buf_nr . 'bdelete'
+endfunction
 
-  if !bufexists(self.buf_nr)
+&quot; Returns a buffer number. Creates new buffer if a:buf_nr is a invalid number
+function! s:Open1LineBuffer(buf_nr, buf_name)
+  if !bufexists(a:buf_nr)
     leftabove 1new
-    file `='[Fuzzyfinder]'`
-    let self.buf_nr = bufnr('%')
-  elseif bufwinnr(self.buf_nr) == -1
+    execute printf('file `=%s`', string(a:buf_name))
+  elseif bufwinnr(a:buf_nr) == -1
     leftabove 1split
-    execute self.buf_nr . 'buffer'
+    execute a:buf_nr . 'buffer'
     delete _
-  elseif bufwinnr(self.buf_nr) != bufwinnr('%')
-    execute bufwinnr(self.buf_nr) . 'wincmd w'
+  elseif bufwinnr(a:buf_nr) != bufwinnr('%')
+    execute bufwinnr(a:buf_nr) . 'wincmd w'
   endif
+  return bufnr('%')
+endfunction
 
+function! s:SetLocalOptionsForFuzzyfinder(cwd, complete_func)
   &quot; countermeasure for auto-cd script
-  execute ':lcd ' . cwd
-
+  execute ':lcd ' . a:cwd
   setlocal filetype=fuzzyfinder
   setlocal bufhidden=delete
   setlocal buftype=nofile
@@ -1344,46 +1553,23 @@ function! s:WindowManager.activate(complete_func)
   setlocal nocursorline   &quot; for highlighting
   setlocal nocursorcolumn &quot; for highlighting
   let &amp;l:completefunc = a:complete_func
-
-  redraw &quot; for 'lazyredraw'
-
-  &quot; suspend autocomplpop.vim
-  if exists(':AutoComplPopLock')
-    :AutoComplPopLock
-  endif
 endfunction
 
-function! s:WindowManager.deactivate()
-  &quot; resume autocomplpop.vim
-  if exists(':AutoComplPopUnlock')
-    :AutoComplPopUnlock
-  endif
-
-  close
-  execute self.prev_winnr . 'wincmd w'
-endfunction
-
-&quot;-----------------------------------------------------------------------------
+&quot; OBJECT: s:InfoFileManager --------------------------------------------- {{{1
 let s:InfoFileManager = { 'originals' : {} }
 
 function! s:InfoFileManager.load()
-  for m in s:GetAvailableModes()
-    let m.info = []
-  endfor
-
   try
     let lines = readfile(expand(self.get_info_file()))
-  catch /.*/ 
-    return
-  endtry
-
-  &quot; compatibility check
-  if !count(lines, self.get_info_version_line())
+    &quot; compatibility check
+    if !count(lines, self.get_info_version_line())
       call self.warn_old_info()
       let g:FuzzyFinderOptions.Base.info_file = ''
-      return
-  endif
-
+      throw 1
+    endif
+  catch /.*/ 
+    let lines = []
+  endtry
   for m in s:GetAvailableModes()
     call m.deserialize_info(lines)
   endfor
@@ -1394,7 +1580,6 @@ function! s:InfoFileManager.save()
   for m in s:GetAvailableModes()
     let lines += m.serialize_info()
   endfor
-
   try
     call writefile(lines, expand(self.get_info_file()))
   catch /.*/ 
@@ -1402,24 +1587,19 @@ function! s:InfoFileManager.save()
 endfunction
 
 function! s:InfoFileManager.edit()
-
   new
   file `='[FuzzyfinderInfo]'`
   let self.bufnr = bufnr('%')
-
   setlocal filetype=vim
   setlocal bufhidden=delete
   setlocal buftype=acwrite
   setlocal noswapfile
-
   augroup FuzzyfinderInfo
     autocmd!
     autocmd BufWriteCmd &lt;buffer&gt; call s:InfoFileManager.on_buf_write_cmd()
   augroup END
-
   execute '0read ' . expand(self.get_info_file())
   setlocal nomodified
-
 endfunction
 
 function! s:InfoFileManager.on_buf_write_cmd()
@@ -1433,7 +1613,7 @@ function! s:InfoFileManager.on_buf_write_cmd()
 endfunction
 
 function! s:InfoFileManager.get_info_version_line()
-  return &quot;VERSION\t206&quot;
+  return &quot;VERSION\t217&quot;
 endfunction
 
 function! s:InfoFileManager.get_info_file()
@@ -1441,13 +1621,15 @@ function! s:InfoFileManager.get_info_file()
 endfunction
 
 function! s:InfoFileManager.warn_old_info()
-  echohl WarningMsg
-  echo printf(&quot;==================================================\n&quot; .
-      \       &quot;  Your Fuzzyfinder information file is no longer  \n&quot; .
-      \       &quot;  supported. Please remove                        \n&quot; .
-      \       &quot;  %-48s\n&quot; .
-      \       &quot;==================================================\n&quot; ,
-      \       '&quot;' . expand(self.get_info_file()) . '&quot;.')
+  call s:EchoWithHl(printf(&quot;=================================================================\n&quot; .
+        \                  &quot;  Sorry, but your information file for Fuzzyfinder is no longer  \n&quot; .
+        \                  &quot;  compatible with this version of Fuzzyfinder. Please remove     \n&quot; .
+        \                  &quot;  %-63s\n&quot; .
+        \                  &quot;=================================================================\n&quot; ,
+        \                  '&quot;' . expand(self.get_info_file()) . '&quot;.'),
+        \           'WarningMsg')
+  echohl Question
+  call input('Press Enter')
   echohl None
 endfunction
 
@@ -1459,7 +1641,7 @@ let user_options = (exists('g:FuzzyFinderOptions') ? g:FuzzyFinderOptions : {})
 &quot; }}}2
 
 &quot; Initializes g:FuzzyFinderOptions.
-let g:FuzzyFinderOptions = { 'Base':{}, 'Buffer':{}, 'File':{}, 'Dir':{}, 'MruFile':{}, 'MruCmd':{}, 'FavFile':{}, 'Tag':{}, 'TaggedFile':{}}
+let g:FuzzyFinderOptions = { 'Base':{}, 'Buffer':{}, 'File':{}, 'Dir':{}, 'MruFile':{}, 'MruCmd':{}, 'Bookmark':{}, 'Tag':{}, 'TaggedFile':{}}
 &quot;-----------------------------------------------------------------------------
 &quot; [All Mode] This is mapped to select completion item or finish input and
 &quot; open a buffer/file in previous window.
@@ -1494,6 +1676,12 @@ let g:FuzzyFinderOptions.Base.ignore_case = 1
 &quot; [All Mode] This is a string to format time string. See :help strftime() for
 &quot; details.
 let g:FuzzyFinderOptions.Base.time_format = '(%x %H:%M:%S)'
+&quot; [All Mode] This is the ceiling for the number of completion statistics to be
+&quot; stored.
+let g:FuzzyFinderOptions.Base.learning_limit = 100
+&quot; [All Mode] To speed up the response time, Fuzzyfinder ends enumerating
+&quot; completion items when found over this.
+let g:FuzzyFinderOptions.Base.enumerating_limit = 50
 &quot; [All Mode] If a length of completion item is more than this, it is trimmed
 &quot; when shown in completion menu.
 let g:FuzzyFinderOptions.Base.trim_length = 80
@@ -1512,12 +1700,12 @@ let g:FuzzyFinderOptions.Buffer.prompt_highlight = 'Question'
 &quot; [Buffer Mode] Pressing &lt;BS&gt; after a path separator deletes one directory
 &quot; name if non-zero is set.
 let g:FuzzyFinderOptions.Buffer.smart_bs = 1
-&quot; [Buffer Mode] The completion items is sorted in the order of recently used
-&quot; if non-zero is set.
-let g:FuzzyFinderOptions.Buffer.mru_order = 1
 &quot; [Buffer Mode] This is used to sort modes for switching to the next/previous
 &quot; mode.
 let g:FuzzyFinderOptions.Buffer.switch_order = 10
+&quot; [Buffer Mode] The completion items is sorted in the order of recently used
+&quot; if non-zero is set.
+let g:FuzzyFinderOptions.Buffer.mru_order = 1
 &quot;-----------------------------------------------------------------------------
 &quot; [File Mode] This disables all functions of this mode if zero was set.
 let g:FuzzyFinderOptions.File.mode_available = 1
@@ -1533,9 +1721,6 @@ let g:FuzzyFinderOptions.File.smart_bs = 1
 let g:FuzzyFinderOptions.File.switch_order = 20
 &quot; [File Mode] The items matching this are excluded from the completion list.
 let g:FuzzyFinderOptions.File.excluded_path = '\v\~$|\.o$|\.exe$|\.bak$|\.swp$|((^|[/\\])\.[/\\]$)'
-&quot; [File Mode] If a number of matched items was over this, the completion
-&quot; process is aborted.
-let g:FuzzyFinderOptions.File.matching_limit = 200
 &quot;-----------------------------------------------------------------------------
 &quot; [Directory Mode] This disables all functions of this mode if zero was set.
 let g:FuzzyFinderOptions.Dir.mode_available = 1
@@ -1568,8 +1753,9 @@ let g:FuzzyFinderOptions.MruFile.switch_order = 40
 &quot; [Mru-File Mode] The items matching this are excluded from the completion
 &quot; list.
 let g:FuzzyFinderOptions.MruFile.excluded_path = '\v\~$|\.bak$|\.swp$'
-&quot; [Mru-File Mode] This is an upper limit of MRU items to be stored.
-let g:FuzzyFinderOptions.MruFile.max_item = 99
+&quot; [Mru-File Mode] This is the ceiling for the number of MRU items to be
+&quot; stored.
+let g:FuzzyFinderOptions.MruFile.max_item = 200
 &quot;-----------------------------------------------------------------------------
 &quot; [Mru-Cmd Mode] This disables all functions of this mode if zero was set.
 let g:FuzzyFinderOptions.MruCmd.mode_available = 1
@@ -1586,22 +1772,24 @@ let g:FuzzyFinderOptions.MruCmd.switch_order = 50
 &quot; [Mru-Cmd Mode] The items matching this are excluded from the completion
 &quot; list.
 let g:FuzzyFinderOptions.MruCmd.excluded_command = '^$'
-&quot; [Mru-Cmd Mode] This is an upper limit of MRU items to be stored.
-let g:FuzzyFinderOptions.MruCmd.max_item = 99
+&quot; [Mru-Cmd Mode] This is the ceiling for the number of MRU items to be stored.
+let g:FuzzyFinderOptions.MruCmd.max_item = 200
 &quot;-----------------------------------------------------------------------------
-&quot; [Favorite-File Mode] This disables all functions of this mode if zero was
-&quot; set.
-let g:FuzzyFinderOptions.FavFile.mode_available = 1
-&quot; [Favorite-File Mode] The prompt string.
-let g:FuzzyFinderOptions.FavFile.prompt = '&gt;FavFile&gt;'
-&quot; [Favorite-File Mode] The highlight group name for a prompt string.
-let g:FuzzyFinderOptions.FavFile.prompt_highlight = 'Question'
-&quot; [Favorite-File Mode] Pressing &lt;BS&gt; after a path separator deletes one
-&quot; directory name if non-zero is set.
-let g:FuzzyFinderOptions.FavFile.smart_bs = 1
-&quot; [Favorite-File Mode] This is used to sort modes for switching to the
+&quot; [Bookmark Mode] This disables all functions of this mode if zero was set.
+let g:FuzzyFinderOptions.Bookmark.mode_available = 1
+&quot; [Bookmark Mode] The prompt string.
+let g:FuzzyFinderOptions.Bookmark.prompt = '&gt;Bookmark&gt;'
+&quot; [Bookmark Mode] The highlight group name for a prompt string.
+let g:FuzzyFinderOptions.Bookmark.prompt_highlight = 'Question'
+&quot; [Bookmark Mode] Pressing &lt;BS&gt; after a path separator deletes one directory
+&quot; name if non-zero is set.
+let g:FuzzyFinderOptions.Bookmark.smart_bs = 0
+&quot; [Bookmark Mode] This is used to sort modes for switching to the
 &quot; next/previous mode.
-let g:FuzzyFinderOptions.FavFile.switch_order = 60
+let g:FuzzyFinderOptions.Bookmark.switch_order = 60
+&quot; [Bookmark Mode] Fuzzyfinder searches a matching line from bookmarked
+&quot; position within this number of lines.
+let g:FuzzyFinderOptions.Bookmark.searching_range = 100
 &quot;-----------------------------------------------------------------------------
 &quot; [Tag Mode] This disables all functions of this mode if zero was set.
 let g:FuzzyFinderOptions.Tag.mode_available = 1
@@ -1617,9 +1805,6 @@ let g:FuzzyFinderOptions.Tag.smart_bs = 0
 let g:FuzzyFinderOptions.Tag.switch_order = 70
 &quot; [Tag Mode] The items matching this are excluded from the completion list.
 let g:FuzzyFinderOptions.Tag.excluded_path = '\v\~$|\.bak$|\.swp$'
-&quot; [Tag Mode] If a number of matched items was over this, the completion
-&quot; process is aborted.
-let g:FuzzyFinderOptions.Tag.matching_limit = 200
 &quot;-----------------------------------------------------------------------------
 &quot; [Tagged-File Mode] This disables all functions of this mode if zero was set.
 let g:FuzzyFinderOptions.TaggedFile.mode_available = 1
@@ -1633,9 +1818,6 @@ let g:FuzzyFinderOptions.TaggedFile.smart_bs = 0
 &quot; [Tagged-File Mode] This is used to sort modes for switching to the
 &quot; next/previous mode.
 let g:FuzzyFinderOptions.TaggedFile.switch_order = 80
-&quot; [Tagged-File Mode] If a number of matched items was over this, the
-&quot; completion process is aborted.
-let g:FuzzyFinderOptions.TaggedFile.matching_limit = 200
 
 &quot; overwrites default values of g:FuzzyFinderOptions with user-defined values - {{{2
 call map(user_options, 'extend(g:FuzzyFinderOptions[v:key], v:val, ''force'')')
@@ -1647,7 +1829,7 @@ call map(copy(g:FuzzyFinderMode), 'v:val.extend_options()')
 &quot; COMMANDS/AUTOCOMMANDS/MAPPINGS/ETC.: {{{1
 
 let s:PATH_SEPARATOR = (has('win32') || has('win64') ? '\' : '/')
-let s:MATCHING_RATE_BASE = 10000000
+let s:MATCHING_RATE_BASE = 1000000
 let s:ABBR_TRIM_MARK = '...'
 
 augroup FuzzyfinderGlobal
@@ -1659,16 +1841,16 @@ augroup END
 &quot; cnoremap has a problem, which doesn't expand cabbrev.
 cmap &lt;silent&gt; &lt;expr&gt; &lt;CR&gt; &lt;SID&gt;OnCmdCR()
 
-command! -bang -narg=? -complete=buffer FuzzyFinderBuffer      call g:FuzzyFinderMode.Buffer.launch    (&lt;q-args&gt;, len(&lt;q-bang&gt;), bufnr('%'), s:GetCurrentTagFiles())
-command! -bang -narg=? -complete=file   FuzzyFinderFile        call g:FuzzyFinderMode.File.launch      (&lt;q-args&gt;, len(&lt;q-bang&gt;), bufnr('%'), s:GetCurrentTagFiles())
-command! -bang -narg=? -complete=dir    FuzzyFinderDir         call g:FuzzyFinderMode.Dir.launch       (&lt;q-args&gt;, len(&lt;q-bang&gt;), bufnr('%'), s:GetCurrentTagFiles())
-command! -bang -narg=? -complete=file   FuzzyFinderMruFile     call g:FuzzyFinderMode.MruFile.launch   (&lt;q-args&gt;, len(&lt;q-bang&gt;), bufnr('%'), s:GetCurrentTagFiles())
-command! -bang -narg=? -complete=file   FuzzyFinderMruCmd      call g:FuzzyFinderMode.MruCmd.launch    (&lt;q-args&gt;, len(&lt;q-bang&gt;), bufnr('%'), s:GetCurrentTagFiles())
-command! -bang -narg=? -complete=file   FuzzyFinderFavFile     call g:FuzzyFinderMode.FavFile.launch   (&lt;q-args&gt;, len(&lt;q-bang&gt;), bufnr('%'), s:GetCurrentTagFiles())
-command! -bang -narg=? -complete=tag    FuzzyFinderTag         call g:FuzzyFinderMode.Tag.launch       (&lt;q-args&gt;, len(&lt;q-bang&gt;), bufnr('%'), s:GetCurrentTagFiles())
-command! -bang -narg=? -complete=file   FuzzyFinderTaggedFile  call g:FuzzyFinderMode.TaggedFile.launch(&lt;q-args&gt;, len(&lt;q-bang&gt;), bufnr('%'), s:GetCurrentTagFiles())
+command! -bang -narg=? -complete=buffer FuzzyFinderBuffer      call g:FuzzyFinderMode.Buffer.launch    (&lt;q-args&gt;, len(&lt;q-bang&gt;))
+command! -bang -narg=? -complete=file   FuzzyFinderFile        call g:FuzzyFinderMode.File.launch      (&lt;q-args&gt;, len(&lt;q-bang&gt;))
+command! -bang -narg=? -complete=dir    FuzzyFinderDir         call g:FuzzyFinderMode.Dir.launch       (&lt;q-args&gt;, len(&lt;q-bang&gt;))
+command! -bang -narg=? -complete=file   FuzzyFinderMruFile     call g:FuzzyFinderMode.MruFile.launch   (&lt;q-args&gt;, len(&lt;q-bang&gt;))
+command! -bang -narg=? -complete=file   FuzzyFinderMruCmd      call g:FuzzyFinderMode.MruCmd.launch    (&lt;q-args&gt;, len(&lt;q-bang&gt;))
+command! -bang -narg=? -complete=file   FuzzyFinderBookmark    call g:FuzzyFinderMode.Bookmark.launch  (&lt;q-args&gt;, len(&lt;q-bang&gt;))
+command! -bang -narg=? -complete=tag    FuzzyFinderTag         call g:FuzzyFinderMode.Tag.launch       (&lt;q-args&gt;, len(&lt;q-bang&gt;))
+command! -bang -narg=? -complete=file   FuzzyFinderTaggedFile  call g:FuzzyFinderMode.TaggedFile.launch(&lt;q-args&gt;, len(&lt;q-bang&gt;))
 command! -bang -narg=? -complete=file   FuzzyFinderEditInfo    call s:InfoFileManager.edit()
-command! -bang -narg=? -complete=file   FuzzyFinderAddFavFile  call g:FuzzyFinderMode.FavFile.add(&lt;q-args&gt;, 1)
+command! -bang -narg=? -complete=file   FuzzyFinderAddBookmark call g:FuzzyFinderMode.Bookmark.bookmark_here(&lt;q-args&gt;)
 command! -bang -narg=0                  FuzzyFinderRemoveCache for m in s:GetAvailableModes() | call m.empty_cache_if_existed(1) | endfor
 
 &quot; }}}1</diff>
      <filename>plugin/fuzzyfinder.vim</filename>
    </modified>
    <modified>
      <diff>@@ -4,25 +4,22 @@ if has(&quot;ruby&quot;)
 &quot; COPIED FROM FUZZYFINDER.VIM {{{
 &quot; since they can't be called from outside fuzzyfinder.vim
 &quot; ====================================================================================
-function! s:GetCurrentTagFiles()
-  return sort(filter(map(tagfiles(), 'fnamemodify(v:val, '':p'')'), 'filereadable(v:val)'))
-endfunction
 
-function! s:HighlightPrompt(prompt, highlight)
-  syntax clear
-  execute printf('syntax match %s /^\V%s/', a:highlight, escape(a:prompt, '\'))
+function! s:ExistsPrompt(line, prompt)
+  return  strlen(a:line) &gt;= strlen(a:prompt) &amp;&amp; a:line[:strlen(a:prompt) -1] ==# a:prompt
 endfunction
 
-function! s:HighlightError()
-  syntax clear
-  syntax match Error  /^.*$/
+function! s:RemovePrompt(line, prompt)
+  return a:line[(s:ExistsPrompt(a:line, a:prompt) ? strlen(a:prompt) : 0):]
 endfunction
+
 &quot; ------------------------------------------------------------------------------------
 &quot; }}}
 &quot; ====================================================================================
 
-command! -bang -narg=? -complete=file   FuzzyFinderTextMate   call FuzzyFinderTextMateLauncher(&lt;q-args&gt;, len(&lt;q-bang&gt;), bufnr('%'), s:GetCurrentTagFiles())
-
+command! -bang -narg=? -complete=file   FuzzyFinderTextMate   call FuzzyFinderTextMateLauncher(&lt;q-args&gt;, len(&lt;q-bang&gt;))
+command! FuzzyFinderTextMateRefreshFiles ruby refresh_finder
+  
 function! InstantiateTextMateMode() &quot;{{{
 ruby &lt;&lt; RUBY
   begin
@@ -55,14 +52,22 @@ RUBY
   endif
 
   &quot; Configuration option: g:fuzzy_ignore
-  &quot; A semi-colon delimited list of file glob patterns to ignore
+  &quot; A delimited list of file glob patterns to ignore. Entries may be delimited
+  &quot; with either commas or semi-colons.
   if !exists('g:fuzzy_ignore')
     let g:fuzzy_ignore = &quot;&quot;
   endif
 
-  &quot; Configuration option: g:fuzzy_matching_limit
+  &quot; Configuration option: g:fuzzy_path_display
+  &quot; Set to `abbr` if you want to display the abbreviated path to a file,
+  &quot; `full` to display the full path
+  if !exists('g:fuzzy_path_display')
+    let g:fuzzy_path_display = 'abbr'
+  endif
+
+  &quot; Configuration option: g:fuzzy_enumerating_limit
   &quot; The maximum number of matches to return at a time. Defaults to 200, via the
-  &quot; g:FuzzyFinderMode.TextMate.matching_limit variable, but using a global variable
+  &quot; g:FuzzyFinderMode.TextMate.enumerating_limit variable, but using a global variable
   &quot; makes it easier to set this value.
 
 ruby &lt;&lt; RUBY
@@ -70,77 +75,60 @@ ruby &lt;&lt; RUBY
     @finder ||= begin
       roots = VIM.evaluate(&quot;g:fuzzy_roots&quot;).split(&quot;\n&quot;)
       ceiling = VIM.evaluate(&quot;g:fuzzy_ceiling&quot;).to_i
-      ignore = VIM.evaluate(&quot;g:fuzzy_ignore&quot;).split(/;/)
+      ignore = VIM.evaluate(&quot;g:fuzzy_ignore&quot;).split(/[;,]/)
       FuzzyFileFinder.new(roots, ceiling, ignore)
     end
   end
+
+  def refresh_finder
+    @finder = nil
+    finder
+    nil
+  end
 RUBY
 
   let g:FuzzyFinderMode.TextMate = copy(g:FuzzyFinderMode.Base)
 
-  &quot; ================================================================================
-  &quot; This function is copied almost whole-sale from fuzzyfinder.vim. Ideally, I could
-  &quot; used the on_complete callback to more cleanly add the new behavior, but the
-  &quot; TextMate-style completion broke a few of fuzzyfinder.vim's assumptions, and the
-  &quot; only way to patch that up was to override Base.complete...which required me to
-  &quot; copy-and-paste much of the original implementation.
-  &quot;
-  &quot; Ugly. But effective.
-  &quot; ================================================================================
-  function! g:FuzzyFinderMode.TextMate.complete(findstart, base)
-    if a:findstart
-      return 0
-    elseif  !self.exists_prompt(a:base) || len(self.remove_prompt(a:base)) &lt; self.min_length
-      return []
-    endif
-    call s:HighlightPrompt(self.prompt, self.prompt_highlight)
-
-    let result = []
-
-    if exists('g:fuzzy_matching_limit')
-      let l:limit = g:fuzzy_matching_limit
+  function! g:FuzzyFinderMode.TextMate.on_complete(base)
+    if exists('g:fuzzy_enumerating_limit')
+      let l:enumerating_limit = g:fuzzy_enumerating_limit
     else
-      let l:limit = self.matching_limit
+      let l:enumerating_limit = self.enumerating_limit
     endif
-
+    let result = []
     ruby &lt;&lt; RUBY
-      text = VIM.evaluate('self.remove_prompt(a:base)')
-      limit = VIM.evaluate('l:limit').to_i
 
-      matches = finder.find(text, limit)
-      matches.sort_by { |a| [-a[:score], a[:path]] }.each_with_index do |match, index|
+      text = VIM.evaluate('s:RemovePrompt(a:base,self.prompt)')
+      enumerating_limit = VIM.evaluate('l:enumerating_limit').to_i
+      path_display = VIM.evaluate(&quot;g:fuzzy_path_display&quot;)
+      ceiling = VIM.evaluate('g:fuzzy_ceiling').to_i
+
+      matches = finder.find(text, ceiling)
+      matches_length = matches.length
+      matches.sort_by { |a| [-a[:score], a[:path]] }[0,enumerating_limit].each_with_index do |match, index|
         word = match[:path]
-        abbr = &quot;%2d: %s&quot; % [index+1, match[:abbr]]
+        abbr = &quot;%2d: %s&quot; % [index+1, match[path_display.to_sym]]
         menu = &quot;[%5d]&quot; % [match[:score] * 10000]
-        VIM.evaluate(&quot;add(result, { 'word' : #{word.inspect}, 'abbr' : #{abbr.inspect}, 'menu' : #{menu.inspect} })&quot;)
+        VIM.evaluate(&quot;add(result, { 'word' : fnamemodify(#{word.inspect},':~:.'), 'abbr' : #{abbr.inspect}, 'menu' : #{menu.inspect}, 'ranks': [#{index}] })&quot;)
       end
 RUBY
-
-    if empty(result) || len(result) &gt;= self.matching_limit
-      call s:HighlightError()
-    endif
-
-    if !empty(result)
-      call feedkeys(&quot;\&lt;C-p&gt;\&lt;Down&gt;&quot;, 'n')
-    endif
-
     return result
   endfunction
 
-  function! FuzzyFinderTextMateLauncher(initial_text, partial_matching, prev_bufnr, tag_files)
-    call g:FuzzyFinderMode.TextMate.launch(a:initial_text, a:partial_matching, a:prev_bufnr, a:tag_files)
+  function! FuzzyFinderTextMateLauncher(initial_text, partial_matching)
+    call g:FuzzyFinderMode.TextMate.launch(a:initial_text, a:partial_matching)
   endfunction
-
+  
   let g:FuzzyFinderOptions.TextMate = copy(g:FuzzyFinderOptions.File)
 endfunction &quot;}}}
 
 if !exists('loaded_fuzzyfinder') &quot;{{{
-  function! FuzzyFinderTextMateLauncher(initial_text, partial_matching, prev_bufnr, tag_files)
+  function! FuzzyFinderTextMateLauncher(initial_text, partial_matching)
     call InstantiateTextMateMode()
-    function! FuzzyFinderTextMateLauncher(initial_text, partial_matching, prev_bufnr, tag_files)
-      call g:FuzzyFinderMode.TextMate.launch(a:initial_text, a:partial_matching, a:prev_bufnr, a:tag_files)
+    function! FuzzyFinderTextMateLauncher(initial_text, partial_matching)
+      call g:FuzzyFinderMode.TextMate.launch(a:initial_text, a:partial_matching)
     endfunction
-    call g:FuzzyFinderMode.TextMate.launch(a:initial_text, a:partial_matching, a:prev_bufnr, a:tag_files)
+    call g:FuzzyFinderMode.TextMate.launch(a:initial_text, a:partial_matching)
   endfunction
   finish
 end &quot;}}}
@@ -148,4 +136,3 @@ end &quot;}}}
 call InstantiateTextMateMode()
 
 endif
-</diff>
      <filename>plugin/fuzzyfinder_textmate.vim</filename>
    </modified>
    <modified>
      <diff>@@ -37,7 +37,7 @@ function! s:error(str)
 endfunction
 
 function! s:autoload(...)
-  if !exists(&quot;g:autoloaded_rails&quot;)
+  if !exists(&quot;g:autoloaded_rails&quot;) &amp;&amp; v:version &gt;= 700
     runtime! autoload/rails.vim
   endif
   if exists(&quot;g:autoloaded_rails&quot;)
@@ -48,7 +48,11 @@ function! s:autoload(...)
   endif
   if !exists(&quot;g:rails_no_autoload_warning&quot;)
     let g:rails_no_autoload_warning = 1
-    call s:error(&quot;Disabling rails.vim: autoload/rails.vim is missing&quot;)
+    if v:version &gt;= 700
+      call s:error(&quot;Disabling rails.vim: autoload/rails.vim is missing&quot;)
+    else
+      call s:error(&quot;Disabling rails.vim: Vim version 7 or higher required&quot;)
+    endif
   endif
   return &quot;&quot;
 endfunction
@@ -62,24 +66,20 @@ function! s:SetOptDefault(opt,val)
   endif
 endfunction
 
-call s:SetOptDefault(&quot;rails_level&quot;,3)
 call s:SetOptDefault(&quot;rails_statusline&quot;,1)
 call s:SetOptDefault(&quot;rails_syntax&quot;,1)
 call s:SetOptDefault(&quot;rails_mappings&quot;,1)
 call s:SetOptDefault(&quot;rails_abbreviations&quot;,1)
-call s:SetOptDefault(&quot;rails_expensive&quot;,1+0*(has(&quot;win32&quot;)||has(&quot;win32unix&quot;)))
+call s:SetOptDefault(&quot;rails_expensive&quot;,1)
 call s:SetOptDefault(&quot;rails_dbext&quot;,g:rails_expensive)
-call s:SetOptDefault(&quot;rails_subversion&quot;,0)
 call s:SetOptDefault(&quot;rails_default_file&quot;,&quot;README&quot;)
 call s:SetOptDefault(&quot;rails_default_database&quot;,&quot;&quot;)
 call s:SetOptDefault(&quot;rails_root_url&quot;,'http://localhost:3000/')
-call s:SetOptDefault(&quot;rails_modelines&quot;,1)
+call s:SetOptDefault(&quot;rails_modelines&quot;,0)
 call s:SetOptDefault(&quot;rails_menu&quot;,1)
 call s:SetOptDefault(&quot;rails_gnu_screen&quot;,1)
 call s:SetOptDefault(&quot;rails_history_size&quot;,5)
-call s:SetOptDefault(&quot;rails_debug&quot;,0)
 call s:SetOptDefault(&quot;rails_generators&quot;,&quot;controller\nintegration_test\nmailer\nmigration\nmodel\nobserver\nplugin\nresource\nscaffold\nsession_migration&quot;)
-call s:SetOptDefault(&quot;rails_rake_tasks&quot;,&quot;db:charset\ndb:collation\ndb:create\ndb:create:all\ndb:drop\ndb:drop:all\ndb:fixtures:identify\ndb:fixtures:load\ndb:migrate\ndb:reset\ndb:rollback\ndb:schema:dump\ndb:schema:load\ndb:sessions:clear\ndb:sessions:create\ndb:structure:dump\ndb:test:clone\ndb:test:clone_structure\ndb:test:prepare\ndb:test:purge\ndb:version\ndoc:app\ndoc:clobber_app\ndoc:clobber_plugins\ndoc:clobber_rails\ndoc:plugins\ndoc:rails\ndoc:reapp\ndoc:rerails\nlog:clear\nnotes\nnotes:fixme\nnotes:optimize\nnotes:todo\nrails:freeze:edge\nrails:freeze:gems\nrails:unfreeze\nrails:update\nrails:update:configs\nrails:update:javascripts\nrails:update:scripts\nroutes\nstats\ntest\ntest:functionals\ntest:integration\ntest:plugins\ntest:recent\ntest:uncommitted\ntest:units\ntmp:cache:clear\ntmp:clear\ntmp:create\ntmp:pids:clear\ntmp:sessions:clear\ntmp:sockets:clear&quot;)
 if g:rails_dbext
   if exists(&quot;g:loaded_dbext&quot;) &amp;&amp; executable(&quot;sqlite3&quot;) &amp;&amp; ! executable(&quot;sqlite&quot;)
     &quot; Since dbext can't find it by itself
@@ -99,11 +99,15 @@ endfunction
 
 function! s:Detect(filename)
   let fn = substitute(fnamemodify(a:filename,&quot;:p&quot;),'\c^file://','','')
+  let sep = matchstr(fn,'^[^\\/]\{3,\}\zs[\\/]')
+  if sep != &quot;&quot;
+    let fn = getcwd().sep.fn
+  endif
   if fn =~ '[\/]config[\/]environment\.rb$'
     return s:BufInit(strpart(fn,0,strlen(fn)-22))
   endif
   if isdirectory(fn)
-    let fn = fnamemodify(fn,&quot;:s?[\/]$??&quot;)
+    let fn = fnamemodify(fn,':s?[\/]$??')
   else
     let fn = fnamemodify(fn,':s?\(.*\)[\/][^\/]*$?\1?')
   endif
@@ -122,7 +126,7 @@ function! s:Detect(filename)
       return s:BufInit(fn)
     endif
     let ofn = fn
-    let fn = fnamemodify(ofn,':s?\(.*\)[\/]\(app\|config\|db\|doc\|lib\|log\|public\|script\|spec\|test\|tmp\|vendor\)\($\|[\/].*$\)?\1?')
+    let fn = fnamemodify(ofn,':s?\(.*\)[\/]\(app\|config\|db\|doc\|features\|lib\|log\|public\|script\|spec\|stories\|test\|tmp\|vendor\)\($\|[\/].*$\)?\1?')
   endwhile
   return 0
 endfunction
@@ -144,10 +148,10 @@ augroup railsPluginDetect
   autocmd FileType netrw if !exists(&quot;b:rails_root&quot;) | call s:Detect(expand(&quot;&lt;afile&gt;:p&quot;)) | endif | if exists(&quot;b:rails_root&quot;) | silent doau User BufEnterRails | endif
   autocmd BufEnter * if exists(&quot;b:rails_root&quot;)|silent doau User BufEnterRails|endif
   autocmd BufLeave * if exists(&quot;b:rails_root&quot;)|silent doau User BufLeaveRails|endif
-  autocmd FileType railslog if s:autoload()|call RailslogSyntax()|endif
+  autocmd Syntax railslog if s:autoload()|call rails#log_syntax()|endif
 augroup END
 
-command! -bar -bang -nargs=* -complete=dir Rails :if s:autoload()|call RailsNewApp(&lt;bang&gt;0,&lt;f-args&gt;)|endif
+command! -bar -bang -nargs=* -complete=dir Rails :if s:autoload()|call rails#new_app_command(&lt;bang&gt;0,&lt;f-args&gt;)|endif
 
 &quot; }}}1
 &quot; Menus {{{1
@@ -164,6 +168,10 @@ function! s:gsub(str,pat,rep)
   return substitute(a:str,'\v\C'.a:pat,a:rep,'g')
 endfunction
 
+function! s:menucmd(priority)
+  return 'anoremenu &lt;script&gt; '.(exists(&quot;$CREAM&quot;) ? 87 : '').s:gsub(g:rails_installed_menu,'[^.]','').'.'.a:priority.' '
+endfunction
+
 function! s:CreateMenus() abort
   if exists(&quot;g:rails_installed_menu&quot;) &amp;&amp; g:rails_installed_menu != &quot;&quot;
     exe &quot;aunmenu &quot;.s:gsub(g:rails_installed_menu,'\&amp;','')
@@ -175,14 +183,14 @@ function! s:CreateMenus() abort
     else
       let g:rails_installed_menu = '&amp;Plugin.&amp;Rails'
     endif
+    let dots = s:gsub(g:rails_installed_menu,'[^.]','')
+    let menucmd = s:menucmd(200)
     if exists(&quot;$CREAM&quot;)
-      let menucmd = '87anoremenu &lt;script&gt; '
       exe menucmd.g:rails_installed_menu.'.-PSep- :'
       exe menucmd.g:rails_installed_menu.'.&amp;Related\ file\	:R\ /\ Alt+] :R&lt;CR&gt;'
       exe menucmd.g:rails_installed_menu.'.&amp;Alternate\ file\	:A\ /\ Alt+[ :A&lt;CR&gt;'
       exe menucmd.g:rails_installed_menu.'.&amp;File\ under\ cursor\	Ctrl+Enter :Rfind&lt;CR&gt;'
     else
-      let menucmd = 'anoremenu &lt;script&gt; '
       exe menucmd.g:rails_installed_menu.'.-PSep- :'
       exe menucmd.g:rails_installed_menu.'.&amp;Related\ file\	:R\ /\ ]f :R&lt;CR&gt;'
       exe menucmd.g:rails_installed_menu.'.&amp;Alternate\ file\	:A\ /\ [f :A&lt;CR&gt;'
@@ -195,36 +203,24 @@ function! s:CreateMenus() abort
     exe menucmd.g:rails_installed_menu.'.&amp;Other\ files.Application\ &amp;README :find doc/README_FOR_APP&lt;CR&gt;'
     exe menucmd.g:rails_installed_menu.'.&amp;Other\ files.&amp;Environment :find config/environment.rb&lt;CR&gt;'
     exe menucmd.g:rails_installed_menu.'.&amp;Other\ files.&amp;Database\ Configuration :find config/database.yml&lt;CR&gt;'
-    exe menucmd.g:rails_installed_menu.'.&amp;Other\ files.Database\ &amp;Schema :call &lt;SID&gt;findschema()&lt;CR&gt;'
+    exe menucmd.g:rails_installed_menu.'.&amp;Other\ files.Database\ &amp;Schema :Rmigration 0&lt;CR&gt;'
     exe menucmd.g:rails_installed_menu.'.&amp;Other\ files.R&amp;outes :find config/routes.rb&lt;CR&gt;'
     exe menucmd.g:rails_installed_menu.'.&amp;Other\ files.&amp;Test\ Helper :find test/test_helper.rb&lt;CR&gt;'
     exe menucmd.g:rails_installed_menu.'.-FSep- :'
     exe menucmd.g:rails_installed_menu.'.Ra&amp;ke\	:Rake :Rake&lt;CR&gt;'
-    let tasks = g:rails_rake_tasks
-    while tasks != ''
-      let task = matchstr(tasks,'.\{-\}\ze\%(\n\|$\)')
-      let tasks = s:sub(tasks,'.{-}%(\n|$)','')
-      exe menucmd.g:rails_installed_menu.'.Rake\ &amp;tasks\	:Rake.'.s:sub(s:sub(task,'^[^:]*$','&amp;:all'),':','.').' :Rake '.task.'&lt;CR&gt;'
-    endwhile
-    let tasks = g:rails_generators
-    while tasks != ''
-      let task = matchstr(tasks,'.\{-\}\ze\%(\n\|$\)')
-      let tasks = s:sub(tasks,'.{-}%(\n|$)','')
-      exe menucmd.'&lt;silent&gt; '.g:rails_installed_menu.'.&amp;Generate\	:Rgen.'.s:gsub(task,'_','\\ ').' :call &lt;SID&gt;menuprompt(&quot;Rgenerate '.task.'&quot;,&quot;Arguments for script/generate '.task.': &quot;)&lt;CR&gt;'
-      exe menucmd.'&lt;silent&gt; '.g:rails_installed_menu.'.&amp;Destroy\	:Rdestroy.'.s:gsub(task,'_','\\ ').' :call &lt;SID&gt;menuprompt(&quot;Rdestroy '.task.'&quot;,&quot;Arguments for script/destroy '.task.': &quot;)&lt;CR&gt;'
-    endwhile
+    let menucmd = substitute(menucmd,'200 $','500 ','')
     exe menucmd.g:rails_installed_menu.'.&amp;Server\	:Rserver.&amp;Start\	:Rserver :Rserver&lt;CR&gt;'
     exe menucmd.g:rails_installed_menu.'.&amp;Server\	:Rserver.&amp;Force\ start\	:Rserver! :Rserver!&lt;CR&gt;'
     exe menucmd.g:rails_installed_menu.'.&amp;Server\	:Rserver.&amp;Kill\	:Rserver!\ - :Rserver! -&lt;CR&gt;'
-    exe menucmd.'&lt;silent&gt; '.g:rails_installed_menu.'.&amp;Evaluate\ Ruby\.\.\.\	:Rp :call &lt;SID&gt;menuprompt(&quot;Rp&quot;,&quot;Code to execute and output: &quot;)&lt;CR&gt;'
-    exe menucmd.g:rails_installed_menu.'.&amp;Console\	:Rconsole :Rconsole&lt;CR&gt;'
+    exe substitute(menucmd,'&lt;script&gt;','&lt;script&gt; &lt;silent&gt;','').g:rails_installed_menu.'.&amp;Evaluate\ Ruby\.\.\.\	:Rp :call &lt;SID&gt;menuprompt(&quot;Rp&quot;,&quot;Code to execute and output: &quot;)&lt;CR&gt;'
+    exe menucmd.g:rails_installed_menu.'.&amp;Console\	:Rscript :Rscript console&lt;CR&gt;'
     exe menucmd.g:rails_installed_menu.'.&amp;Preview\	:Rpreview :Rpreview&lt;CR&gt;'
     exe menucmd.g:rails_installed_menu.'.&amp;Log\ file\	:Rlog :Rlog&lt;CR&gt;'
-    exe s:sub(menucmd,'anoremenu','vnoremenu').' &lt;silent&gt; '.g:rails_installed_menu.'.E&amp;xtract\ as\ partial\	:Rextract :call &lt;SID&gt;menuprompt(&quot;'.&quot;'&quot;.'&lt;,'.&quot;'&quot;.'&gt;Rextract&quot;,&quot;Partial name (e.g., template or /controller/template): &quot;)&lt;CR&gt;'
+    exe substitute(s:sub(menucmd,'anoremenu','vnoremenu'),'&lt;script&gt;','&lt;script&gt; &lt;silent&gt;','').g:rails_installed_menu.'.E&amp;xtract\ as\ partial\	:Rextract :call &lt;SID&gt;menuprompt(&quot;'.&quot;'&quot;.'&lt;,'.&quot;'&quot;.'&gt;Rextract&quot;,&quot;Partial name (e.g., template or /controller/template): &quot;)&lt;CR&gt;'
     exe menucmd.g:rails_installed_menu.'.&amp;Migration\ writer\	:Rinvert :Rinvert&lt;CR&gt;'
     exe menucmd.'         '.g:rails_installed_menu.'.-HSep- :'
-    exe menucmd.'&lt;silent&gt; '.g:rails_installed_menu.'.&amp;Help\	:help\ rails :if &lt;SID&gt;autoload()&lt;Bar&gt;exe RailsHelpCommand(&quot;&quot;)&lt;Bar&gt;endif&lt;CR&gt;'
-    exe menucmd.'&lt;silent&gt; '.g:rails_installed_menu.'.Abo&amp;ut\	 :if &lt;SID&gt;autoload()&lt;Bar&gt;exe RailsHelpCommand(&quot;about&quot;)&lt;Bar&gt;endif&lt;CR&gt;'
+    exe substitute(menucmd,'&lt;script&gt;','&lt;script&gt; &lt;silent&gt;','').g:rails_installed_menu.'.&amp;Help\	:help\ rails :if &lt;SID&gt;autoload()&lt;Bar&gt;exe RailsHelpCommand(&quot;&quot;)&lt;Bar&gt;endif&lt;CR&gt;'
+    exe substitute(menucmd,'&lt;script&gt;','&lt;script&gt; &lt;silent&gt;','').g:rails_installed_menu.'.Abo&amp;ut\	 :if &lt;SID&gt;autoload()&lt;Bar&gt;exe RailsHelpCommand(&quot;about&quot;)&lt;Bar&gt;endif&lt;CR&gt;'
     let g:rails_did_menus = 1
     call s:ProjectMenu()
     call s:menuBufLeave()
@@ -264,6 +260,27 @@ function! s:menuBufEnter()
       exe 'amenu disable '.menu.'.Migration\ writer'
     endif
     call s:ProjectMenu()
+    silent! exe 'aunmenu       '.menu.'.Rake\ tasks'
+    silent! exe 'aunmenu       '.menu.'.Generate'
+    silent! exe 'aunmenu       '.menu.'.Destroy'
+    if rails#app().cache.needs('rake_tasks') || empty(rails#app().rake_tasks())
+      exe substitute(s:menucmd(300),'&lt;script&gt;','&lt;script&gt; &lt;silent&gt;','').g:rails_installed_menu.'.Rake\ &amp;tasks\	:Rake.Fill\ this\ menu :call rails#app().rake_tasks()&lt;Bar&gt;call &lt;SID&gt;menuBufLeave()&lt;Bar&gt;call &lt;SID&gt;menuBufEnter()&lt;CR&gt;'
+    else
+      let i = 0
+      while i &lt; len(rails#app().rake_tasks())
+        let task = rails#app().rake_tasks()[i]
+        exe s:menucmd(300).g:rails_installed_menu.'.Rake\ &amp;tasks\	:Rake.'.s:sub(task,':',':.').' :Rake '.task.'&lt;CR&gt;'
+        let i += 1
+      endwhile
+    endif
+    let i = 0
+    let menucmd = substitute(s:menucmd(400),'&lt;script&gt;','&lt;script&gt; &lt;silent&gt;','').g:rails_installed_menu
+    while i &lt; len(rails#app().generators())
+      let generator = rails#app().generators()[i]
+      exe menucmd.'.&amp;Generate\	:Rgen.'.s:gsub(generator,'_','\\ ').' :call &lt;SID&gt;menuprompt(&quot;Rgenerate '.generator.'&quot;,&quot;Arguments for script/generate '.generator.': &quot;)&lt;CR&gt;'
+      exe menucmd.'.&amp;Destroy\	:Rdestroy.'.s:gsub(generator,'_','\\ ').' :call &lt;SID&gt;menuprompt(&quot;Rdestroy '.generator.'&quot;,&quot;Arguments for script/destroy '.generator.': &quot;)&lt;CR&gt;'
+      let i += 1
+    endwhile
   endif
 endfunction
 
@@ -274,6 +291,12 @@ function! s:menuBufLeave()
     exe 'amenu enable  '.menu.'.Help\	'
     exe 'amenu enable  '.menu.'.About\	'
     exe 'amenu enable  '.menu.'.Projects'
+    silent! exe 'aunmenu       '.menu.'.Rake\ tasks'
+    silent! exe 'aunmenu       '.menu.'.Generate'
+    silent! exe 'aunmenu       '.menu.'.Destroy'
+    exe s:menucmd(300).g:rails_installed_menu.'.Rake\ tasks\	:Rake.-TSep- :'
+    exe s:menucmd(400).g:rails_installed_menu.'.&amp;Generate\	:Rgen.-GSep- :'
+    exe s:menucmd(400).g:rails_installed_menu.'.&amp;Destroy\	:Rdestroy.-DSep- :'
   endif
 endfunction
 
@@ -285,17 +308,6 @@ function! s:menuprompt(vimcmd,prompt)
   exe a:vimcmd.&quot; &quot;.res
 endfunction
 
-function! s:findschema()
-  let env = exists('$RAILS_ENV') ? $RAILS_ENV : &quot;development&quot;
-  if filereadable(b:rails_root.&quot;/db/schema.rb&quot;)
-    edit `=b:rails_root.'/db/schema.rb'`
-  elseif filereadable(b:rails_root.'/db/'.env.'_structure.sql')
-    edit `=b:rails_root.'/db/'.env.'_structure.sql'`
-  else
-    return s:error(&quot;Schema not found: try :Rake db:schema:dump&quot;)
-  endif
-endfunction
-
 call s:CreateMenus()
 
 augroup railsPluginMenu</diff>
      <filename>plugin/rails.vim</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>bc4eb1616314ffe78bf2dd719e715d5c86b59eb3</id>
    </parent>
  </parents>
  <author>
    <name>Rich Manalang</name>
    <email>rich.manalang@gmail.com</email>
  </author>
  <url>http://github.com/manalang/vim-config/commit/b3afbc62a2709a0ec2e97f6ed965b2cddf86276f</url>
  <id>b3afbc62a2709a0ec2e97f6ed965b2cddf86276f</id>
  <committed-date>2009-05-06T09:49:33-07:00</committed-date>
  <authored-date>2009-05-06T09:49:33-07:00</authored-date>
  <message>updates</message>
  <tree>f9d6405ffbe1c11cb3462370e234d6b0cd7dfd38</tree>
  <committer>
    <name>Rich Manalang</name>
    <email>rich.manalang@gmail.com</email>
  </committer>
</commit>
