nerdtree

A tree explorer plugin for vim.
Index Commits Files Refs
commit 23dfc6d8181da7d423ca89a950c69ee5694ea2c6
parent f982f61e8a1dfdd66e6741f79bb71eb8408875fb
Author: Martin Grenfell <martin.grenfell@gmail.com>
Date:   Mon,  7 Jul 2014 22:59:28 +0100

continue breaking down the epic autoload module

Add 2 new classes and move code into them from autoload:

* NERDTree. Each nerdtree buffer now has a NERDTree object that holds
  the root node and will old other util functions
* UI. Each NERDTree object holds a UI object which is responsible for
  rendering, getting the current node, etc

Still a fair few methods to sort through in autoload (many of which will
end up in the above classes) - need sleep though.

Diffstat:
Mautoload/nerdtree.vim | 192++-----------------------------------------------------------------------------
Mautoload/nerdtree/ui_glue.vim | 32++++++++++++++++----------------
Mlib/nerdtree/bookmark.vim | 4++--
Mlib/nerdtree/creator.vim | 35++++++++++++++++++++---------------
Alib/nerdtree/nerdtree.vim | 23+++++++++++++++++++++++
Mlib/nerdtree/opener.vim | 2+-
Mlib/nerdtree/tree_dir_node.vim | 4++--
Mlib/nerdtree/tree_file_node.vim | 67++++---------------------------------------------------------------
Alib/nerdtree/ui.vim | 262+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mnerdtree_plugin/fs_menu.vim | 4++--
Mplugin/NERD_tree.vim | 2+-
11 files changed, 337 insertions(+), 290 deletions(-)
diff --git a/autoload/nerdtree.vim b/autoload/nerdtree.vim
@@ -74,6 +74,8 @@ function! nerdtree#loadClassFiles()
     runtime lib/nerdtree/tree_dir_node.vim
     runtime lib/nerdtree/opener.vim
     runtime lib/nerdtree/creator.vim
+    runtime lib/nerdtree/nerdtree.vim
+    runtime lib/nerdtree/ui.vim
 endfunction
 
 " FUNCTION: nerdtree#postSourceActions() {{{2
@@ -321,75 +323,6 @@ function! nerdtree#echoWarning(msg)
     echohl normal
 endfunction
 
-"FUNCTION: nerdtree#getPath(ln) {{{2
-"Gets the full path to the node that is rendered on the given line number
-"
-"Args:
-"ln: the line number to get the path for
-"
-"Return:
-"A path if a node was selected, {} if nothing is selected.
-"If the 'up a dir' line was selected then the path to the parent of the
-"current root is returned
-function! nerdtree#getPath(ln)
-    let line = getline(a:ln)
-
-    let rootLine = g:NERDTreeFileNode.GetRootLineNum()
-
-    "check to see if we have the root node
-    if a:ln == rootLine
-        return b:NERDTreeRoot.path
-    endif
-
-    if !g:NERDTreeDirArrows
-        " in case called from outside the tree
-        if line !~# '^ *[|`▸▾ ]' || line =~# '^$'
-            return {}
-        endif
-    endif
-
-    if line ==# nerdtree#treeUpDirLine()
-        return b:NERDTreeRoot.path.getParent()
-    endif
-
-    let indent = nerdtree#indentLevelFor(line)
-
-    "remove the tree parts and the leading space
-    let curFile = nerdtree#stripMarkupFromLine(line, 0)
-
-    let wasdir = 0
-    if curFile =~# '/$'
-        let wasdir = 1
-        let curFile = substitute(curFile, '/\?$', '/', "")
-    endif
-
-    let dir = ""
-    let lnum = a:ln
-    while lnum > 0
-        let lnum = lnum - 1
-        let curLine = getline(lnum)
-        let curLineStripped = nerdtree#stripMarkupFromLine(curLine, 1)
-
-        "have we reached the top of the tree?
-        if lnum == rootLine
-            let dir = b:NERDTreeRoot.path.str({'format': 'UI'}) . dir
-            break
-        endif
-        if curLineStripped =~# '/$'
-            let lpindent = nerdtree#indentLevelFor(curLine)
-            if lpindent < indent
-                let indent = indent - 1
-
-                let dir = substitute (curLineStripped,'^\\', "", "") . dir
-                continue
-            endif
-        endif
-    endwhile
-    let curFile = b:NERDTreeRoot.path.drive . dir . curFile
-    let toReturn = g:NERDTreePath.New(curFile)
-    return toReturn
-endfunction
-
 "FUNCTION: nerdtree#getTreeWinNum() {{{2
 "gets the nerd tree window number for this tab
 function! nerdtree#getTreeWinNum()
@@ -400,17 +333,6 @@ function! nerdtree#getTreeWinNum()
     endif
 endfunction
 
-"FUNCTION: nerdtree#indentLevelFor(line) {{{2
-function! nerdtree#indentLevelFor(line)
-    let level = match(a:line, '[^ \-+~▸▾`|]') / nerdtree#treeWid()
-    " check if line includes arrows
-    if match(a:line, '[▸▾]') > -1
-        " decrement level as arrow uses 3 ascii chars
-        let level = level - 1
-    endif
-    return level
-endfunction
-
 "FUNCTION: nerdtree#isTreeOpen() {{{2
 function! nerdtree#isTreeOpen()
     return nerdtree#getTreeWinNum() != -1
@@ -427,7 +349,7 @@ function! nerdtree#putCursorOnBookmarkTable()
         return cursor(1, 2)
     endif
 
-    let rootNodeLine = g:NERDTreeFileNode.GetRootLineNum()
+    let rootNodeLine = b:NERDTree.ui.getRootLineNum()
 
     let line = 1
     while getline(line) !~# '^>-\+Bookmarks-\+$'
@@ -467,116 +389,10 @@ function! nerdtree#renderBookmarks()
 endfunction
 
 "FUNCTION: nerdtree#renderView {{{2
-"The entry function for rendering the tree
 function! nerdtree#renderView()
-    setlocal modifiable
-
-    "remember the top line of the buffer and the current line so we can
-    "restore the view exactly how it was
-    let curLine = line(".")
-    let curCol = col(".")
-    let topLine = line("w0")
-
-    "delete all lines in the buffer (being careful not to clobber a register)
-    silent 1,$delete _
-
-    call nerdtree#dumpHelp()
-
-    "delete the blank line before the help and add one after it
-    if g:NERDTreeMinimalUI == 0
-        call setline(line(".")+1, "")
-        call cursor(line(".")+1, col("."))
-    endif
-
-    if b:NERDTreeShowBookmarks
-        call nerdtree#renderBookmarks()
-    endif
-
-    "add the 'up a dir' line
-    if !g:NERDTreeMinimalUI
-        call setline(line(".")+1, nerdtree#treeUpDirLine())
-        call cursor(line(".")+1, col("."))
-    endif
-
-    "draw the header line
-    let header = b:NERDTreeRoot.path.str({'format': 'UI', 'truncateTo': winwidth(0)})
-    call setline(line(".")+1, header)
-    call cursor(line(".")+1, col("."))
-
-    "draw the tree
-    let old_o = @o
-    let @o = b:NERDTreeRoot.renderToString()
-    silent put o
-    let @o = old_o
-
-    "delete the blank line at the top of the buffer
-    silent 1,1delete _
-
-    "restore the view
-    let old_scrolloff=&scrolloff
-    let &scrolloff=0
-    call cursor(topLine, 1)
-    normal! zt
-    call cursor(curLine, curCol)
-    let &scrolloff = old_scrolloff
-
-    setlocal nomodifiable
-endfunction
-
-"FUNCTION: nerdtree#renderViewSavingPosition {{{2
-"Renders the tree and ensures the cursor stays on the current node or the
-"current nodes parent if it is no longer available upon re-rendering
-function! nerdtree#renderViewSavingPosition()
-    let currentNode = g:NERDTreeFileNode.GetSelected()
-
-    "go up the tree till we find a node that will be visible or till we run
-    "out of nodes
-    while currentNode != {} && !currentNode.isVisible() && !currentNode.isRoot()
-        let currentNode = currentNode.parent
-    endwhile
-
-    call nerdtree#renderView()
-
-    if currentNode != {}
-        call currentNode.putCursorHere(0, 0)
-    endif
+    call b:NERDTree.render()
 endfunction
 "
-"FUNCTION: nerdtree#restoreScreenState() {{{2
-"
-"Sets the screen state back to what it was when nerdtree#saveScreenState was last
-"called.
-"
-"Assumes the cursor is in the NERDTree window
-function! nerdtree#restoreScreenState()
-    if !exists("b:NERDTreeOldTopLine") || !exists("b:NERDTreeOldPos") || !exists("b:NERDTreeOldWindowSize")
-        return
-    endif
-    exec("silent vertical resize ".b:NERDTreeOldWindowSize)
-
-    let old_scrolloff=&scrolloff
-    let &scrolloff=0
-    call cursor(b:NERDTreeOldTopLine, 0)
-    normal! zt
-    call setpos(".", b:NERDTreeOldPos)
-    let &scrolloff=old_scrolloff
-endfunction
-
-"FUNCTION: nerdtree#saveScreenState() {{{2
-"Saves the current cursor position in the current buffer and the window
-"scroll position
-function! nerdtree#saveScreenState()
-    let win = winnr()
-    try
-        call nerdtree#putCursorInTreeWin()
-        let b:NERDTreeOldPos = getpos(".")
-        let b:NERDTreeOldTopLine = line("w0")
-        let b:NERDTreeOldWindowSize = winwidth("")
-        call nerdtree#exec(win . "wincmd w")
-    catch /^NERDTree.InvalidOperationError/
-    endtry
-endfunction
-
 "FUNCTION: nerdtree#stripMarkupFromLine(line, removeLeadingSpaces){{{2
 "returns the given line with all the tree parts stripped off
 "
diff --git a/autoload/nerdtree/ui_glue.vim b/autoload/nerdtree/ui_glue.vim
@@ -119,7 +119,7 @@ function! nerdtree#ui_glue#bookmarkNode(...)
         endif
         try
             call currentNode.bookmark(name)
-            call nerdtree#renderView()
+            call b:NERDTree.render()
         catch /^NERDTree.IllegalBookmarkNameError/
             call nerdtree#echo("bookmark names must not contain spaces")
         endtry
@@ -141,7 +141,7 @@ endfunction
 " changes the current root to the selected one
 function! s:chRoot(node)
     call a:node.makeRoot()
-    call nerdtree#renderView()
+    call b:NERDTree.render()
     call b:NERDTreeRoot.putCursorHere(0, 0)
 endfunction
 
@@ -173,14 +173,14 @@ function! nerdtree#ui_glue#clearBookmarks(bookmarks)
             call bookmark.delete()
         endfor
     endif
-    call nerdtree#renderView()
+    call b:NERDTree.render()
 endfunction
 
 " FUNCTION: s:closeChildren(node) {{{1
 " closes all childnodes of the current node
 function! s:closeChildren(node)
     call a:node.closeChildren()
-    call nerdtree#renderView()
+    call b:NERDTree.render()
     call a:node.putCursorHere(0, 0)
 endfunction
 
@@ -200,7 +200,7 @@ function! s:closeCurrentDir(node)
             endif
         endwhile
         call parent.close()
-        call nerdtree#renderView()
+        call b:NERDTree.render()
         call parent.putCursorHere(0, 0)
     endif
 endfunction
@@ -227,7 +227,7 @@ function! s:deleteBookmark(bm)
     if  nr2char(getchar()) ==# 'y'
         try
             call a:bm.delete()
-            call nerdtree#renderView()
+            call b:NERDTree.render()
             redraw
         catch /^NERDTree/
             call nerdtree#echoWarning("Could not remove bookmark")
@@ -242,7 +242,7 @@ endfunction
 " toggles the help display
 function! s:displayHelp()
     let b:treeShowHelp = b:treeShowHelp ? 0 : 1
-    call nerdtree#renderView()
+    call b:NERDTree.render()
     call nerdtree#centerView()
 endfunction
 
@@ -486,7 +486,7 @@ endfunction
 function! s:openNodeRecursively(node)
     call nerdtree#echo("Recursively opening node. Please wait...")
     call a:node.openRecursively()
-    call nerdtree#renderView()
+    call b:NERDTree.render()
     redraw
     call nerdtree#echo("Recursively opening node. Please wait... DONE")
 endfunction
@@ -523,7 +523,7 @@ endfunction
 function! s:refreshRoot()
     call nerdtree#echo("Refreshing the root node. This could take a while...")
     call b:NERDTreeRoot.refresh()
-    call nerdtree#renderView()
+    call b:NERDTree.render()
     redraw
     call nerdtree#echo("Refreshing the root node. This could take a while... DONE")
 endfunction
@@ -538,7 +538,7 @@ function! s:refreshCurrent(node)
 
     call nerdtree#echo("Refreshing node. This could take a while...")
     call node.refresh()
-    call nerdtree#renderView()
+    call b:NERDTree.render()
     redraw
     call nerdtree#echo("Refreshing node. This could take a while... DONE")
 endfunction
@@ -573,7 +573,7 @@ endfunction
 " toggles the use of the NERDTreeIgnore option
 function! s:toggleIgnoreFilter()
     let b:NERDTreeIgnoreEnabled = !b:NERDTreeIgnoreEnabled
-    call nerdtree#renderViewSavingPosition()
+    call b:NERDTree.ui.renderViewSavingPosition()
     call nerdtree#centerView()
 endfunction
 
@@ -582,10 +582,10 @@ endfunction
 function! s:toggleShowBookmarks()
     let b:NERDTreeShowBookmarks = !b:NERDTreeShowBookmarks
     if b:NERDTreeShowBookmarks
-        call nerdtree#renderView()
+        call b:NERDTree.render()
         call nerdtree#putCursorOnBookmarkTable()
     else
-        call nerdtree#renderViewSavingPosition()
+        call b:NERDTree.ui.renderViewSavingPosition()
     endif
     call nerdtree#centerView()
 endfunction
@@ -594,7 +594,7 @@ endfunction
 " toggles the display of hidden files
 function! s:toggleShowFiles()
     let b:NERDTreeShowFiles = !b:NERDTreeShowFiles
-    call nerdtree#renderViewSavingPosition()
+    call b:NERDTree.ui.renderViewSavingPosition()
     call nerdtree#centerView()
 endfunction
 
@@ -602,7 +602,7 @@ endfunction
 " toggles the display of hidden files
 function! s:toggleShowHidden()
     let b:NERDTreeShowHidden = !b:NERDTreeShowHidden
-    call nerdtree#renderViewSavingPosition()
+    call b:NERDTree.ui.renderViewSavingPosition()
     call nerdtree#centerView()
 endfunction
 
@@ -650,7 +650,7 @@ function! nerdtree#ui_glue#upDir(keepState)
             call b:NERDTreeRoot.path.changeToDir()
         endif
 
-        call nerdtree#renderView()
+        call b:NERDTree.render()
         call oldRoot.putCursorHere(0, 0)
     endif
 endfunction
diff --git a/lib/nerdtree/bookmark.vim b/lib/nerdtree/bookmark.vim
@@ -275,7 +275,7 @@ function! s:Bookmark.toRoot()
             let targetNode = g:NERDTreeFileNode.New(s:Bookmark.BookmarkFor(self.name).path)
         endtry
         call targetNode.makeRoot()
-        call nerdtree#renderView()
+        call b:NERDTree.render()
         call targetNode.putCursorHere(0, 0)
     endif
 endfunction
@@ -293,7 +293,7 @@ function! s:Bookmark.validate()
         return 1
     else
         call s:Bookmark.CacheBookmarks(1)
-        call nerdtree#renderView()
+        call b:NERDTree.render()
         call nerdtree#echo(self.name . "now points to an invalid location. See :help NERDTreeInvalidBookmarks for info.")
         return 0
     endif
diff --git a/lib/nerdtree/creator.vim b/lib/nerdtree/creator.vim
@@ -17,8 +17,8 @@ function! s:Creator._bindMappings()
     command! -buffer -complete=customlist,nerdtree#completeBookmarks -nargs=1 OpenBookmark :call nerdtree#ui_glue#openBookmark('<args>')
     command! -buffer -complete=customlist,nerdtree#completeBookmarks -nargs=* ClearBookmarks call nerdtree#ui_glue#clearBookmarks('<args>')
     command! -buffer -complete=customlist,nerdtree#completeBookmarks -nargs=+ BookmarkToRoot call g:NERDTreeBookmark.ToRoot('<args>')
-    command! -buffer -nargs=0 ClearAllBookmarks call g:NERDTreeBookmark.ClearAll() <bar> call nerdtree#renderView()
-    command! -buffer -nargs=0 ReadBookmarks call g:NERDTreeBookmark.CacheBookmarks(0) <bar> call nerdtree#renderView()
+    command! -buffer -nargs=0 ClearAllBookmarks call g:NERDTreeBookmark.ClearAll() <bar> call b:NERDTree.render()
+    command! -buffer -nargs=0 ReadBookmarks call g:NERDTreeBookmark.CacheBookmarks(0) <bar> call b:NERDTree.render()
     command! -buffer -nargs=0 WriteBookmarks call g:NERDTreeBookmark.Write()
 endfunction
 
@@ -56,19 +56,16 @@ function! s:Creator.createPrimary(name)
         unlet t:NERDTreeBufName
     endif
 
-    let newRoot = g:NERDTreeDirNode.New(path)
-    call newRoot.open()
-
     call self._createTreeWin()
+    call self._createNERDTree(path)
     let b:treeShowHelp = 0
     let b:NERDTreeIgnoreEnabled = 1
     let b:NERDTreeShowFiles = g:NERDTreeShowFiles
     let b:NERDTreeShowHidden = g:NERDTreeShowHidden
     let b:NERDTreeShowBookmarks = g:NERDTreeShowBookmarks
-    let b:NERDTreeRoot = newRoot
     let b:NERDTreeType = "primary"
 
-    call nerdtree#renderView()
+    call b:NERDTree.render()
     call b:NERDTreeRoot.putCursorHere(0, 0)
 
     call self._broadcastInitEvent()
@@ -99,18 +96,26 @@ function! s:Creator.createSecondary(dir)
     exec "silent edit " . self._nextBufferName()
 
     let b:NERDTreePreviousBuf = bufnr(previousBuf)
-
-    let b:NERDTreeRoot = g:NERDTreeDirNode.New(path)
-    call b:NERDTreeRoot.open()
-
+    call self._createNERDTree(path)
     call self._setCommonBufOptions()
     let b:NERDTreeType = "secondary"
 
-    call nerdtree#renderView()
+    call b:NERDTree.render()
 
     call self._broadcastInitEvent()
 endfunction
 
+" FUNCTION: s:Creator._createNERDTree(path) {{{1
+function! s:Creator._createNERDTree(path)
+    let b:NERDTree = g:NERDTree.New(a:path)
+    call b:NERDTree.root.open()
+
+    "TODO: This is kept for compatability only since many things use
+    "b:NERDTreeRoot instead of the new NERDTree.root
+    "Remove this one day
+    let b:NERDTreeRoot = b:NERDTree.root
+endfunction
+
 " FUNCTION: s:Creator.CreateMirror() {{{1
 function! s:Creator.CreateMirror()
     let creator = s:Creator.New()
@@ -166,7 +171,7 @@ function! s:Creator.createMirror()
     call self._createTreeWin()
     exec 'buffer ' .  bufferName
     if !&hidden
-        call nerdtree#renderView()
+        call b:NERDTree.render()
     endif
 endfunction
 
@@ -326,9 +331,9 @@ function! s:Creator.togglePrimary(dir)
         if !nerdtree#isTreeOpen()
             call self._createTreeWin()
             if !&hidden
-                call nerdtree#renderView()
+                call b:NERDTree.render()
             endif
-            call nerdtree#restoreScreenState()
+            call b:NERDTree.ui.restoreScreenState()
         else
             call nerdtree#closeTree()
         endif
diff --git a/lib/nerdtree/nerdtree.vim b/lib/nerdtree/nerdtree.vim
@@ -0,0 +1,23 @@
+"CLASS: NERDTree
+"============================================================
+let s:NERDTree = {}
+let g:NERDTree = s:NERDTree
+
+function! s:NERDTree.ForCurrentBuf()
+    return b:NERDTree
+endfunction
+
+function! s:NERDTree.New(path)
+    let newObj = copy(self)
+    let newObj.ui = g:NERDTreeUI.New(newObj)
+    let newObj.root = g:NERDTreeDirNode.New(a:path)
+
+    return newObj
+endfunction
+
+"FUNCTION: s:NERDTree.render() {{{1
+"A convenience function - since this is called often
+function! s:NERDTree.render()
+    call self.ui.render()
+endfunction
+
diff --git a/lib/nerdtree/opener.vim b/lib/nerdtree/opener.vim
@@ -262,7 +262,7 @@ function! s:Opener._openDirectory(node)
         call self._gotoTargetWin()
         if empty(self._where)
             call a:node.makeRoot()
-            call nerdtree#renderView()
+            call b:NERDTree.render()
             call a:node.putCursorHere(0, 0)
         elseif self._where == 't'
             call g:NERDTreeCreator.CreatePrimary(a:node.path.str())
diff --git a/lib/nerdtree/tree_dir_node.vim b/lib/nerdtree/tree_dir_node.vim
@@ -21,7 +21,7 @@ unlet s:TreeDirNode.activate
 function! s:TreeDirNode.activate(...)
     let opts = a:0 ? a:1 : {}
     call self.toggleOpen(opts)
-    call nerdtree#renderView()
+    call b:NERDTree.render()
     call self.putCursorHere(0, 0)
 endfunction
 
@@ -450,7 +450,7 @@ function! s:TreeDirNode.reveal(path)
 
     if self.path.equals(a:path.getParent())
         let n = self.findNode(a:path)
-        call nerdtree#renderView()
+        call b:NERDTree.render()
         call n.putCursorHere(1,0)
         return
     endif
diff --git a/lib/nerdtree/tree_file_node.vim b/lib/nerdtree/tree_file_node.vim
@@ -178,55 +178,6 @@ function! s:TreeFileNode.findSibling(direction)
     return {}
 endfunction
 
-"FUNCTION: TreeFileNode.getLineNum(){{{1
-"returns the line number this node is rendered on, or -1 if it isnt rendered
-function! s:TreeFileNode.getLineNum()
-    "if the node is the root then return the root line no.
-    if self.isRoot()
-        return s:TreeFileNode.GetRootLineNum()
-    endif
-
-    let totalLines = line("$")
-
-    "the path components we have matched so far
-    let pathcomponents = [substitute(b:NERDTreeRoot.path.str({'format': 'UI'}), '/ *$', '', '')]
-    "the index of the component we are searching for
-    let curPathComponent = 1
-
-    let fullpath = self.path.str({'format': 'UI'})
-
-
-    let lnum = s:TreeFileNode.GetRootLineNum()
-    while lnum > 0
-        let lnum = lnum + 1
-        "have we reached the bottom of the tree?
-        if lnum ==# totalLines+1
-            return -1
-        endif
-
-        let curLine = getline(lnum)
-
-        let indent = nerdtree#indentLevelFor(curLine)
-        if indent ==# curPathComponent
-            let curLine = nerdtree#stripMarkupFromLine(curLine, 1)
-
-            let curPath =  join(pathcomponents, '/') . '/' . curLine
-            if stridx(fullpath, curPath, 0) ==# 0
-                if fullpath ==# curPath || strpart(fullpath, len(curPath)-1,1) ==# '/'
-                    let curLine = substitute(curLine, '/ *$', '', '')
-                    call add(pathcomponents, curLine)
-                    let curPathComponent = curPathComponent + 1
-
-                    if fullpath ==# curPath
-                        return lnum
-                    endif
-                endif
-            endif
-        endif
-    endwhile
-    return -1
-endfunction
-
 "FUNCTION: TreeFileNode.GetRootForTab(){{{1
 "get the root node for this tab
 function! s:TreeFileNode.GetRootForTab()
@@ -236,21 +187,11 @@ function! s:TreeFileNode.GetRootForTab()
     return {}
 endfunction
 
-"FUNCTION: TreeFileNode.GetRootLineNum(){{{1
-"gets the line number of the root node
-function! s:TreeFileNode.GetRootLineNum()
-    let rootLine = 1
-    while getline(rootLine) !~# '^\(/\|<\)'
-        let rootLine = rootLine + 1
-    endwhile
-    return rootLine
-endfunction
-
 "FUNCTION: TreeFileNode.GetSelected() {{{1
 "gets the treenode that the cursor is currently over
 function! s:TreeFileNode.GetSelected()
     try
-        let path = nerdtree#getPath(line("."))
+        let path = b:NERDTree.ui.getPath(line("."))
         if path ==# {}
             return {}
         endif
@@ -348,7 +289,7 @@ endfunction
 "recurseUpward: try to put the cursor on the parent if the this node isnt
 "visible
 function! s:TreeFileNode.putCursorHere(isJump, recurseUpward)
-    let ln = self.getLineNum()
+    let ln = b:NERDTree.ui.getLineNum(self)
     if ln != -1
         if a:isJump
             mark '
@@ -357,11 +298,11 @@ function! s:TreeFileNode.putCursorHere(isJump, recurseUpward)
     else
         if a:recurseUpward
             let node = self
-            while node != {} && node.getLineNum() ==# -1
+            while node != {} && b:NERDTree.ui.getLineNum(node) ==# -1
                 let node = node.parent
                 call node.open()
             endwhile
-            call nerdtree#renderView()
+            call b:NERDTree.render()
             call node.putCursorHere(a:isJump, 0)
         endif
     endif
diff --git a/lib/nerdtree/ui.vim b/lib/nerdtree/ui.vim
@@ -0,0 +1,262 @@
+"CLASS: UI
+"============================================================
+let s:UI = {}
+let g:NERDTreeUI = s:UI
+
+function! s:UI.New(nerdtree)
+    let newObj = copy(self)
+    let newObj.nerdtree = a:nerdtree
+    return newObj
+endfunction
+
+"FUNCTION: s:UI.getPath(ln) {{{1
+"Gets the full path to the node that is rendered on the given line number
+"
+"Args:
+"ln: the line number to get the path for
+"
+"Return:
+"A path if a node was selected, {} if nothing is selected.
+"If the 'up a dir' line was selected then the path to the parent of the
+"current root is returned
+function! s:UI.getPath(ln)
+    let line = getline(a:ln)
+
+    let rootLine = self.getRootLineNum()
+
+    "check to see if we have the root node
+    if a:ln == rootLine
+        return b:NERDTreeRoot.path
+    endif
+
+    if !g:NERDTreeDirArrows
+        " in case called from outside the tree
+        if line !~# '^ *[|`▸▾ ]' || line =~# '^$'
+            return {}
+        endif
+    endif
+
+    if line ==# nerdtree#treeUpDirLine()
+        return b:NERDTreeRoot.path.getParent()
+    endif
+
+    let indent = self._indentLevelFor(line)
+
+    "remove the tree parts and the leading space
+    let curFile = nerdtree#stripMarkupFromLine(line, 0)
+
+    let wasdir = 0
+    if curFile =~# '/$'
+        let wasdir = 1
+        let curFile = substitute(curFile, '/\?$', '/', "")
+    endif
+
+    let dir = ""
+    let lnum = a:ln
+    while lnum > 0
+        let lnum = lnum - 1
+        let curLine = getline(lnum)
+        let curLineStripped = nerdtree#stripMarkupFromLine(curLine, 1)
+
+        "have we reached the top of the tree?
+        if lnum == rootLine
+            let dir = b:NERDTreeRoot.path.str({'format': 'UI'}) . dir
+            break
+        endif
+        if curLineStripped =~# '/$'
+            let lpindent = self._indentLevelFor(curLine)
+            if lpindent < indent
+                let indent = indent - 1
+
+                let dir = substitute (curLineStripped,'^\\', "", "") . dir
+                continue
+            endif
+        endif
+    endwhile
+    let curFile = b:NERDTreeRoot.path.drive . dir . curFile
+    let toReturn = g:NERDTreePath.New(curFile)
+    return toReturn
+endfunction
+
+"FUNCTION: s:UI.getLineNum(file_node){{{1
+"returns the line number this node is rendered on, or -1 if it isnt rendered
+function! s:UI.getLineNum(file_node)
+    "if the node is the root then return the root line no.
+    if a:file_node.isRoot()
+        return b:NERDTree.ui.getRootLineNum()
+    endif
+
+    let totalLines = line("$")
+
+    "the path components we have matched so far
+    let pathcomponents = [substitute(b:NERDTreeRoot.path.str({'format': 'UI'}), '/ *$', '', '')]
+    "the index of the component we are searching for
+    let curPathComponent = 1
+
+    let fullpath = a:file_node.path.str({'format': 'UI'})
+
+    let lnum = b:NERDTree.ui.getRootLineNum()
+    while lnum > 0
+        let lnum = lnum + 1
+        "have we reached the bottom of the tree?
+        if lnum ==# totalLines+1
+            return -1
+        endif
+
+        let curLine = getline(lnum)
+
+        let indent = self._indentLevelFor(curLine)
+        if indent ==# curPathComponent
+            let curLine = nerdtree#stripMarkupFromLine(curLine, 1)
+
+            let curPath =  join(pathcomponents, '/') . '/' . curLine
+            if stridx(fullpath, curPath, 0) ==# 0
+                if fullpath ==# curPath || strpart(fullpath, len(curPath)-1,1) ==# '/'
+                    let curLine = substitute(curLine, '/ *$', '', '')
+                    call add(pathcomponents, curLine)
+                    let curPathComponent = curPathComponent + 1
+
+                    if fullpath ==# curPath
+                        return lnum
+                    endif
+                endif
+            endif
+        endif
+    endwhile
+    return -1
+endfunction
+
+
+"FUNCTION: s:UI.getRootLineNum(){{{1
+"gets the line number of the root node
+function! s:UI.getRootLineNum()
+    let rootLine = 1
+    while getline(rootLine) !~# '^\(/\|<\)'
+        let rootLine = rootLine + 1
+    endwhile
+    return rootLine
+endfunction
+
+"FUNCTION: s:UI._indentLevelFor(line) {{{2
+function! s:UI._indentLevelFor(line)
+    let level = match(a:line, '[^ \-+~▸▾`|]') / nerdtree#treeWid()
+    " check if line includes arrows
+    if match(a:line, '[▸▾]') > -1
+        " decrement level as arrow uses 3 ascii chars
+        let level = level - 1
+    endif
+    return level
+endfunction
+
+
+"FUNCTION: s:UI.restoreScreenState() {{{2
+"
+"Sets the screen state back to what it was when nerdtree#saveScreenState was last
+"called.
+"
+"Assumes the cursor is in the NERDTree window
+function! s:UI.restoreScreenState()
+    if !has_key(self, '_screenState')
+        return
+    endif
+    exec("silent vertical resize " . self._screenState['oldWindowSize'])
+
+    let old_scrolloff=&scrolloff
+    let &scrolloff=0
+    call cursor(self._screenState['oldTopLine'], 0)
+    normal! zt
+    call setpos(".", self._screenState['oldPos'])
+    let &scrolloff=old_scrolloff
+endfunction
+
+"FUNCTION: s:UI.saveScreenState() {{{2
+"Saves the current cursor position in the current buffer and the window
+"scroll position
+function! s:UI.saveScreenState()
+    let win = winnr()
+    try
+        call nerdtree#putCursorInTreeWin()
+        let self._screenState = {}
+        let self._screenState['oldPos'] = getpos(".")
+        let self._screenState['oldTopLine'] = line("w0")
+        let self._screenState['oldWindowSize']= winwidth("")
+        call nerdtree#exec(win . "wincmd w")
+    catch /^NERDTree.InvalidOperationError/
+    endtry
+endfunction
+
+"FUNCTION: s:UI.render() {{{2
+function! s:UI.render()
+    setlocal modifiable
+
+    "remember the top line of the buffer and the current line so we can
+    "restore the view exactly how it was
+    let curLine = line(".")
+    let curCol = col(".")
+    let topLine = line("w0")
+
+    "delete all lines in the buffer (being careful not to clobber a register)
+    silent 1,$delete _
+
+    call nerdtree#dumpHelp()
+
+    "delete the blank line before the help and add one after it
+    if g:NERDTreeMinimalUI == 0
+        call setline(line(".")+1, "")
+        call cursor(line(".")+1, col("."))
+    endif
+
+    if b:NERDTreeShowBookmarks
+        call nerdtree#renderBookmarks()
+    endif
+
+    "add the 'up a dir' line
+    if !g:NERDTreeMinimalUI
+        call setline(line(".")+1, nerdtree#treeUpDirLine())
+        call cursor(line(".")+1, col("."))
+    endif
+
+    "draw the header line
+    let header = b:NERDTreeRoot.path.str({'format': 'UI', 'truncateTo': winwidth(0)})
+    call setline(line(".")+1, header)
+    call cursor(line(".")+1, col("."))
+
+    "draw the tree
+    let old_o = @o
+    let @o = b:NERDTreeRoot.renderToString()
+    silent put o
+    let @o = old_o
+
+    "delete the blank line at the top of the buffer
+    silent 1,1delete _
+
+    "restore the view
+    let old_scrolloff=&scrolloff
+    let &scrolloff=0
+    call cursor(topLine, 1)
+    normal! zt
+    call cursor(curLine, curCol)
+    let &scrolloff = old_scrolloff
+
+    setlocal nomodifiable
+endfunction
+
+
+"FUNCTION: UI.renderViewSavingPosition {{{1
+"Renders the tree and ensures the cursor stays on the current node or the
+"current nodes parent if it is no longer available upon re-rendering
+function! s:UI.renderViewSavingPosition()
+    let currentNode = g:NERDTreeFileNode.GetSelected()
+
+    "go up the tree till we find a node that will be visible or till we run
+    "out of nodes
+    while currentNode != {} && !currentNode.isVisible() && !currentNode.isRoot()
+        let currentNode = currentNode.parent
+    endwhile
+
+    call b:NERDTree.render()
+
+    if currentNode != {}
+        call currentNode.putCursorHere(0, 0)
+    endif
+endfunction
diff --git a/nerdtree_plugin/fs_menu.vim b/nerdtree_plugin/fs_menu.vim
@@ -117,7 +117,7 @@ function! NERDTreeAddNode()
         let newTreeNode = g:NERDTreeFileNode.New(newPath)
         if empty(parentNode)
             call b:NERDTreeRoot.refresh()
-            call nerdtree#renderView()
+            call b:NERDTree.render()
         elseif parentNode.isOpen || !empty(parentNode.children)
             call parentNode.addChild(newTreeNode, 1)
             call NERDTreeRender()
@@ -230,7 +230,7 @@ function! NERDTreeCopyNode()
                 let newNode = currentNode.copy(newNodePath)
                 if empty(newNode)
                     call b:NERDTreeRoot.refresh()
-                    call nerdtree#renderView()
+                    call b:NERDTree.render()
                 else
                     call NERDTreeRender()
                     call newNode.putCursorHere(0, 0)
diff --git a/plugin/NERD_tree.vim b/plugin/NERD_tree.vim
@@ -148,7 +148,7 @@ call nerdtree#ui_glue#setupCommands()
 "============================================================
 augroup NERDTree
     "Save the cursor position whenever we close the nerd tree
-    exec "autocmd BufWinLeave ". g:NERDTreeCreator.BufNamePrefix() ."* call nerdtree#saveScreenState()"
+    exec "autocmd BufWinLeave ". g:NERDTreeCreator.BufNamePrefix() ."* call b:NERDTree.ui.saveScreenState()"
 
     "disallow insert mode in the NERDTree
     exec "autocmd BufEnter ". g:NERDTreeCreator.BufNamePrefix() ."* stopinsert"