├── .gitignore ├── lib └── nerdtree │ ├── event.vim │ ├── notifier.vim │ ├── flag_set.vim │ ├── menu_item.vim │ ├── key_map.vim │ ├── menu_controller.vim │ ├── nerdtree.vim │ ├── opener.vim │ ├── bookmark.vim │ ├── creator.vim │ ├── tree_file_node.vim │ ├── ui.vim │ ├── tree_dir_node.vim │ └── path.vim ├── LICENCE ├── nerdtree_plugin ├── exec_menuitem.vim └── fs_menu.vim ├── syntax └── nerdtree.vim ├── autoload ├── nerdtree.vim └── nerdtree │ └── ui_glue.vim ├── README.markdown ├── CHANGELOG └── plugin └── NERD_tree.vim /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.swp 3 | tags 4 | -------------------------------------------------------------------------------- /lib/nerdtree/event.vim: -------------------------------------------------------------------------------- 1 | "CLASS: Event 2 | "============================================================ 3 | let s:Event = {} 4 | let g:NERDTreeEvent = s:Event 5 | 6 | function! s:Event.New(nerdtree, subject, action, params) abort 7 | let newObj = copy(self) 8 | let newObj.nerdtree = a:nerdtree 9 | let newObj.subject = a:subject 10 | let newObj.action = a:action 11 | let newObj.params = a:params 12 | return newObj 13 | endfunction 14 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2004 Sam Hocevar 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | -------------------------------------------------------------------------------- /lib/nerdtree/notifier.vim: -------------------------------------------------------------------------------- 1 | "CLASS: Notifier 2 | "============================================================ 3 | let s:Notifier = {} 4 | 5 | function! s:Notifier.AddListener(event, funcname) 6 | let listeners = s:Notifier.GetListenersForEvent(a:event) 7 | if listeners == [] 8 | let listenersMap = s:Notifier.GetListenersMap() 9 | let listenersMap[a:event] = listeners 10 | endif 11 | call add(listeners, a:funcname) 12 | endfunction 13 | 14 | function! s:Notifier.NotifyListeners(event, path, nerdtree, params) 15 | let event = g:NERDTreeEvent.New(a:nerdtree, a:path, a:event, a:params) 16 | 17 | for listener in s:Notifier.GetListenersForEvent(a:event) 18 | call {listener}(event) 19 | endfor 20 | endfunction 21 | 22 | function! s:Notifier.GetListenersMap() 23 | if !exists("s:refreshListenersMap") 24 | let s:refreshListenersMap = {} 25 | endif 26 | return s:refreshListenersMap 27 | endfunction 28 | 29 | function! s:Notifier.GetListenersForEvent(name) 30 | let listenersMap = s:Notifier.GetListenersMap() 31 | return get(listenersMap, a:name, []) 32 | endfunction 33 | 34 | let g:NERDTreePathNotifier = deepcopy(s:Notifier) 35 | 36 | -------------------------------------------------------------------------------- /lib/nerdtree/flag_set.vim: -------------------------------------------------------------------------------- 1 | "CLASS: FlagSet 2 | "============================================================ 3 | let s:FlagSet = {} 4 | let g:NERDTreeFlagSet = s:FlagSet 5 | 6 | "FUNCTION: FlagSet.addFlag(scope, flag) {{{1 7 | function! s:FlagSet.addFlag(scope, flag) 8 | let flags = self._flagsForScope(a:scope) 9 | if index(flags, a:flag) == -1 10 | call add(flags, a:flag) 11 | end 12 | endfunction 13 | 14 | "FUNCTION: FlagSet.clearFlags(scope) {{{1 15 | function! s:FlagSet.clearFlags(scope) 16 | let self._flags[a:scope] = [] 17 | endfunction 18 | 19 | "FUNCTION: FlagSet._flagsForScope(scope) {{{1 20 | function! s:FlagSet._flagsForScope(scope) 21 | if !has_key(self._flags, a:scope) 22 | let self._flags[a:scope] = [] 23 | endif 24 | return self._flags[a:scope] 25 | endfunction 26 | 27 | "FUNCTION: FlagSet.New() {{{1 28 | function! s:FlagSet.New() 29 | let newObj = copy(self) 30 | let newObj._flags = {} 31 | return newObj 32 | endfunction 33 | 34 | "FUNCTION: FlagSet.removeFlag(scope, flag) {{{1 35 | function! s:FlagSet.removeFlag(scope, flag) 36 | let flags = self._flagsForScope(a:scope) 37 | 38 | let i = index(flags, a:flag) 39 | if i >= 0 40 | call remove(flags, i) 41 | endif 42 | endfunction 43 | 44 | "FUNCTION: FlagSet.renderToString() {{{1 45 | function! s:FlagSet.renderToString() 46 | let flagstring = "" 47 | for i in values(self._flags) 48 | let flagstring .= join(i) 49 | endfor 50 | 51 | if len(flagstring) == 0 52 | return "" 53 | endif 54 | 55 | return '[' . flagstring . ']' 56 | endfunction 57 | -------------------------------------------------------------------------------- /nerdtree_plugin/exec_menuitem.vim: -------------------------------------------------------------------------------- 1 | " ============================================================================ 2 | " File: exec_menuitem.vim 3 | " Description: plugin for NERD Tree that provides an execute file menu item 4 | " Maintainer: Martin Grenfell 5 | " License: This program is free software. It comes without any warranty, 6 | " to the extent permitted by applicable law. You can redistribute 7 | " it and/or modify it under the terms of the Do What The Fuck You 8 | " Want To Public License, Version 2, as published by Sam Hocevar. 9 | " See http://sam.zoy.org/wtfpl/COPYING for more details. 10 | " 11 | " ============================================================================ 12 | if exists("g:loaded_nerdtree_exec_menuitem") 13 | finish 14 | endif 15 | let g:loaded_nerdtree_exec_menuitem = 1 16 | 17 | call NERDTreeAddMenuItem({ 18 | \ 'text': '(!)Execute file', 19 | \ 'shortcut': '!', 20 | \ 'callback': 'NERDTreeExecFile', 21 | \ 'isActiveCallback': 'NERDTreeExecFileActive' }) 22 | 23 | function! NERDTreeExecFileActive() 24 | let node = g:NERDTreeFileNode.GetSelected() 25 | return !node.path.isDirectory && node.path.isExecutable 26 | endfunction 27 | 28 | function! NERDTreeExecFile() 29 | let treenode = g:NERDTreeFileNode.GetSelected() 30 | echo "==========================================================\n" 31 | echo "Complete the command to execute (add arguments etc):\n" 32 | let cmd = treenode.path.str({'escape': 1}) 33 | let cmd = input(':!', cmd . ' ') 34 | 35 | if cmd != '' 36 | exec ':!' . cmd 37 | else 38 | echo "Aborted" 39 | endif 40 | endfunction 41 | -------------------------------------------------------------------------------- /syntax/nerdtree.vim: -------------------------------------------------------------------------------- 1 | let s:tree_up_dir_line = '.. (up a dir)' 2 | syn match NERDTreeIgnore #\~# 3 | exec 'syn match NERDTreeIgnore #\['.g:NERDTreeGlyphReadOnly.'\]#' 4 | 5 | "highlighting for the .. (up dir) line at the top of the tree 6 | execute "syn match NERDTreeUp #\\V". s:tree_up_dir_line ."#" 7 | 8 | "quickhelp syntax elements 9 | syn match NERDTreeHelpKey #" \{1,2\}[^ ]*:#ms=s+2,me=e-1 10 | syn match NERDTreeHelpKey #" \{1,2\}[^ ]*,#ms=s+2,me=e-1 11 | syn match NERDTreeHelpTitle #" .*\~#ms=s+2,me=e-1 12 | syn match NERDTreeToggleOn #(on)#ms=s+1,he=e-1 13 | syn match NERDTreeToggleOff #(off)#ms=e-3,me=e-1 14 | syn match NERDTreeHelpCommand #" :.\{-}\>#hs=s+3 15 | syn match NERDTreeHelp #^".*# contains=NERDTreeHelpKey,NERDTreeHelpTitle,NERDTreeIgnore,NERDTreeToggleOff,NERDTreeToggleOn,NERDTreeHelpCommand 16 | 17 | "highlighting for sym links 18 | syn match NERDTreeLinkTarget #->.*# containedin=NERDTreeDir,NERDTreeFile 19 | syn match NERDTreeLinkFile #.* ->#me=e-3 containedin=NERDTreeFile 20 | syn match NERDTreeLinkDir #.*/ ->#me=e-3 containedin=NERDTreeDir 21 | 22 | "highlighing for directory nodes and file nodes 23 | syn match NERDTreeDirSlash #/# containedin=NERDTreeDir 24 | 25 | exec 'syn match NERDTreeClosable #'.escape(g:NERDTreeDirArrowCollapsible, '~').'# containedin=NERDTreeDir,NERDTreeFile' 26 | exec 'syn match NERDTreeOpenable #'.escape(g:NERDTreeDirArrowExpandable, '~').'# containedin=NERDTreeDir,NERDTreeFile' 27 | 28 | let s:dirArrows = escape(g:NERDTreeDirArrowCollapsible, '~').escape(g:NERDTreeDirArrowExpandable, '~') 29 | exec 'syn match NERDTreeDir #[^'.s:dirArrows.' ].*/#' 30 | syn match NERDTreeExecFile #^ .*\*\($\| \)# contains=NERDTreeRO,NERDTreeBookmark 31 | exec 'syn match NERDTreeFile #^[^"\.'.s:dirArrows.'] *[^'.s:dirArrows.']*# contains=NERDTreeLink,NERDTreeRO,NERDTreeBookmark,NERDTreeExecFile' 32 | 33 | "highlighting for readonly files 34 | exec 'syn match NERDTreeRO # *\zs.*\ze \['.g:NERDTreeGlyphReadOnly.'\]# contains=NERDTreeIgnore,NERDTreeBookmark,NERDTreeFile' 35 | 36 | syn match NERDTreeFlags #^ *\zs\[.\]# containedin=NERDTreeFile,NERDTreeExecFile 37 | syn match NERDTreeFlags #\[.\]# containedin=NERDTreeDir 38 | 39 | syn match NERDTreeCWD #^[# 46 | syn match NERDTreeBookmarksHeader #^>-\+Bookmarks-\+$# contains=NERDTreeBookmarksLeader 47 | syn match NERDTreeBookmarkName #^>.\{-} #he=e-1 contains=NERDTreeBookmarksLeader 48 | syn match NERDTreeBookmark #^>.*$# contains=NERDTreeBookmarksLeader,NERDTreeBookmarkName,NERDTreeBookmarksHeader 49 | 50 | hi def link NERDTreePart Special 51 | hi def link NERDTreePartFile Type 52 | hi def link NERDTreeExecFile Title 53 | hi def link NERDTreeDirSlash Identifier 54 | 55 | hi def link NERDTreeBookmarksHeader statement 56 | hi def link NERDTreeBookmarksLeader ignore 57 | hi def link NERDTreeBookmarkName Identifier 58 | hi def link NERDTreeBookmark normal 59 | 60 | hi def link NERDTreeHelp String 61 | hi def link NERDTreeHelpKey Identifier 62 | hi def link NERDTreeHelpCommand Identifier 63 | hi def link NERDTreeHelpTitle Macro 64 | hi def link NERDTreeToggleOn Question 65 | hi def link NERDTreeToggleOff WarningMsg 66 | 67 | hi def link NERDTreeLinkTarget Type 68 | hi def link NERDTreeLinkFile Macro 69 | hi def link NERDTreeLinkDir Macro 70 | 71 | hi def link NERDTreeDir Directory 72 | hi def link NERDTreeUp Directory 73 | hi def link NERDTreeFile Normal 74 | hi def link NERDTreeCWD Statement 75 | hi def link NERDTreeOpenable Title 76 | hi def link NERDTreeClosable Title 77 | hi def link NERDTreeIgnore ignore 78 | hi def link NERDTreeRO WarningMsg 79 | hi def link NERDTreeBookmark Statement 80 | hi def link NERDTreeFlags Number 81 | 82 | hi def link NERDTreeCurrentNode Search 83 | -------------------------------------------------------------------------------- /lib/nerdtree/menu_item.vim: -------------------------------------------------------------------------------- 1 | "CLASS: MenuItem 2 | "============================================================ 3 | let s:MenuItem = {} 4 | let g:NERDTreeMenuItem = s:MenuItem 5 | 6 | "FUNCTION: MenuItem.All() {{{1 7 | "get all top level menu items 8 | function! s:MenuItem.All() 9 | if !exists("s:menuItems") 10 | let s:menuItems = [] 11 | endif 12 | return s:menuItems 13 | endfunction 14 | 15 | "FUNCTION: MenuItem.AllEnabled() {{{1 16 | "get all top level menu items that are currently enabled 17 | function! s:MenuItem.AllEnabled() 18 | let toReturn = [] 19 | for i in s:MenuItem.All() 20 | if i.enabled() 21 | call add(toReturn, i) 22 | endif 23 | endfor 24 | return toReturn 25 | endfunction 26 | 27 | "FUNCTION: MenuItem.Create(options) {{{1 28 | "make a new menu item and add it to the global list 29 | function! s:MenuItem.Create(options) 30 | let newMenuItem = copy(self) 31 | 32 | let newMenuItem.text = a:options['text'] 33 | let newMenuItem.shortcut = a:options['shortcut'] 34 | let newMenuItem.children = [] 35 | 36 | let newMenuItem.isActiveCallback = -1 37 | if has_key(a:options, 'isActiveCallback') 38 | let newMenuItem.isActiveCallback = a:options['isActiveCallback'] 39 | endif 40 | 41 | let newMenuItem.callback = -1 42 | if has_key(a:options, 'callback') 43 | let newMenuItem.callback = a:options['callback'] 44 | endif 45 | 46 | if has_key(a:options, 'parent') 47 | call add(a:options['parent'].children, newMenuItem) 48 | else 49 | call add(s:MenuItem.All(), newMenuItem) 50 | endif 51 | 52 | return newMenuItem 53 | endfunction 54 | 55 | "FUNCTION: MenuItem.CreateSeparator(options) {{{1 56 | "make a new separator menu item and add it to the global list 57 | function! s:MenuItem.CreateSeparator(options) 58 | let standard_options = { 'text': '--------------------', 59 | \ 'shortcut': -1, 60 | \ 'callback': -1 } 61 | let options = extend(a:options, standard_options, "force") 62 | 63 | return s:MenuItem.Create(options) 64 | endfunction 65 | 66 | "FUNCTION: MenuItem.CreateSubmenu(options) {{{1 67 | "make a new submenu and add it to global list 68 | function! s:MenuItem.CreateSubmenu(options) 69 | let standard_options = { 'callback': -1 } 70 | let options = extend(a:options, standard_options, "force") 71 | 72 | return s:MenuItem.Create(options) 73 | endfunction 74 | 75 | "FUNCTION: MenuItem.enabled() {{{1 76 | "return 1 if this menu item should be displayed 77 | " 78 | "delegates off to the isActiveCallback, and defaults to 1 if no callback was 79 | "specified 80 | function! s:MenuItem.enabled() 81 | if self.isActiveCallback != -1 82 | return {self.isActiveCallback}() 83 | endif 84 | return 1 85 | endfunction 86 | 87 | "FUNCTION: MenuItem.execute() {{{1 88 | "perform the action behind this menu item, if this menuitem has children then 89 | "display a new menu for them, otherwise deletegate off to the menuitem's 90 | "callback 91 | function! s:MenuItem.execute() 92 | if len(self.children) 93 | let mc = g:NERDTreeMenuController.New(self.children) 94 | call mc.showMenu() 95 | else 96 | if self.callback != -1 97 | call {self.callback}() 98 | endif 99 | endif 100 | endfunction 101 | 102 | "FUNCTION: MenuItem.isSeparator() {{{1 103 | "return 1 if this menuitem is a separator 104 | function! s:MenuItem.isSeparator() 105 | return self.callback == -1 && self.children == [] 106 | endfunction 107 | 108 | "FUNCTION: MenuItem.isSubmenu() {{{1 109 | "return 1 if this menuitem is a submenu 110 | function! s:MenuItem.isSubmenu() 111 | return self.callback == -1 && !empty(self.children) 112 | endfunction 113 | 114 | " vim: set sw=4 sts=4 et fdm=marker: 115 | -------------------------------------------------------------------------------- /autoload/nerdtree.vim: -------------------------------------------------------------------------------- 1 | if exists("g:loaded_nerdtree_autoload") 2 | finish 3 | endif 4 | let g:loaded_nerdtree_autoload = 1 5 | 6 | function! nerdtree#version() 7 | return '5.0.0' 8 | endfunction 9 | 10 | " SECTION: General Functions {{{1 11 | "============================================================ 12 | 13 | "FUNCTION: nerdtree#checkForBrowse(dir) {{{2 14 | "inits a window tree in the current buffer if appropriate 15 | function! nerdtree#checkForBrowse(dir) 16 | if a:dir != '' && isdirectory(a:dir) 17 | call g:NERDTreeCreator.CreateWindowTree(a:dir) 18 | endif 19 | endfunction 20 | 21 | " FUNCTION: nerdtree#completeBookmarks(A,L,P) {{{2 22 | " completion function for the bookmark commands 23 | function! nerdtree#completeBookmarks(A,L,P) 24 | return filter(g:NERDTreeBookmark.BookmarkNames(), 'v:val =~# "^' . a:A . '"') 25 | endfunction 26 | 27 | "FUNCTION: nerdtree#compareBookmarks(dir) {{{2 28 | function! nerdtree#compareBookmarks(first, second) 29 | return a:first.compareTo(a:second) 30 | endfunction 31 | 32 | "FUNCTION: nerdtree#compareNodes(dir) {{{2 33 | function! nerdtree#compareNodes(n1, n2) 34 | return a:n1.path.compareTo(a:n2.path) 35 | endfunction 36 | 37 | "FUNCTION: nerdtree#compareNodesBySortKey(n1, n2) {{{2 38 | function! nerdtree#compareNodesBySortKey(n1, n2) 39 | if a:n1.path.getSortKey() <# a:n2.path.getSortKey() 40 | return -1 41 | elseif a:n1.path.getSortKey() ># a:n2.path.getSortKey() 42 | return 1 43 | else 44 | return 0 45 | endif 46 | endfunction 47 | 48 | " FUNCTION: nerdtree#deprecated(func, [msg]) {{{2 49 | " Issue a deprecation warning for a:func. If a second arg is given, use this 50 | " as the deprecation message 51 | function! nerdtree#deprecated(func, ...) 52 | let msg = a:0 ? a:func . ' ' . a:1 : a:func . ' is deprecated' 53 | 54 | if !exists('s:deprecationWarnings') 55 | let s:deprecationWarnings = {} 56 | endif 57 | if !has_key(s:deprecationWarnings, a:func) 58 | let s:deprecationWarnings[a:func] = 1 59 | echomsg msg 60 | endif 61 | endfunction 62 | 63 | " FUNCTION: nerdtree#exec(cmd) {{{2 64 | " same as :exec cmd but eventignore=all is set for the duration 65 | function! nerdtree#exec(cmd) 66 | let old_ei = &ei 67 | set ei=all 68 | exec a:cmd 69 | let &ei = old_ei 70 | endfunction 71 | 72 | " FUNCTION: nerdtree#has_opt(options, name) {{{2 73 | function! nerdtree#has_opt(options, name) 74 | return has_key(a:options, a:name) && a:options[a:name] == 1 75 | endfunction 76 | 77 | " FUNCTION: nerdtree#loadClassFiles() {{{2 78 | function! nerdtree#loadClassFiles() 79 | runtime lib/nerdtree/path.vim 80 | runtime lib/nerdtree/menu_controller.vim 81 | runtime lib/nerdtree/menu_item.vim 82 | runtime lib/nerdtree/key_map.vim 83 | runtime lib/nerdtree/bookmark.vim 84 | runtime lib/nerdtree/tree_file_node.vim 85 | runtime lib/nerdtree/tree_dir_node.vim 86 | runtime lib/nerdtree/opener.vim 87 | runtime lib/nerdtree/creator.vim 88 | runtime lib/nerdtree/flag_set.vim 89 | runtime lib/nerdtree/nerdtree.vim 90 | runtime lib/nerdtree/ui.vim 91 | runtime lib/nerdtree/event.vim 92 | runtime lib/nerdtree/notifier.vim 93 | endfunction 94 | 95 | " FUNCTION: nerdtree#postSourceActions() {{{2 96 | function! nerdtree#postSourceActions() 97 | call g:NERDTreeBookmark.CacheBookmarks(1) 98 | call nerdtree#ui_glue#createDefaultBindings() 99 | 100 | "load all nerdtree plugins 101 | runtime! nerdtree_plugin/**/*.vim 102 | endfunction 103 | 104 | "FUNCTION: nerdtree#runningWindows(dir) {{{2 105 | function! nerdtree#runningWindows() 106 | return has("win16") || has("win32") || has("win64") 107 | endfunction 108 | 109 | " SECTION: View Functions {{{1 110 | "============================================================ 111 | 112 | "FUNCTION: nerdtree#echo {{{2 113 | "A wrapper for :echo. Appends 'NERDTree:' on the front of all messages 114 | " 115 | "Args: 116 | "msg: the message to echo 117 | function! nerdtree#echo(msg) 118 | redraw 119 | echomsg "NERDTree: " . a:msg 120 | endfunction 121 | 122 | "FUNCTION: nerdtree#echoError {{{2 123 | "Wrapper for nerdtree#echo, sets the message type to errormsg for this message 124 | "Args: 125 | "msg: the message to echo 126 | function! nerdtree#echoError(msg) 127 | echohl errormsg 128 | call nerdtree#echo(a:msg) 129 | echohl normal 130 | endfunction 131 | 132 | "FUNCTION: nerdtree#echoWarning {{{2 133 | "Wrapper for nerdtree#echo, sets the message type to warningmsg for this message 134 | "Args: 135 | "msg: the message to echo 136 | function! nerdtree#echoWarning(msg) 137 | echohl warningmsg 138 | call nerdtree#echo(a:msg) 139 | echohl normal 140 | endfunction 141 | 142 | "FUNCTION: nerdtree#renderView {{{2 143 | function! nerdtree#renderView() 144 | call b:NERDTree.render() 145 | endfunction 146 | 147 | " vim: set sw=4 sts=4 et fdm=marker: 148 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | The NERD Tree 2 | ============= 3 | 4 | Intro 5 | ----- 6 | 7 | The NERD tree allows you to explore your filesystem and to open files and 8 | directories. It presents the filesystem to you in the form of a tree which you 9 | manipulate with the keyboard and/or mouse. It also allows you to perform 10 | simple filesystem operations. 11 | 12 | The following features and functionality are provided by the NERD tree: 13 | 14 | * Files and directories are displayed in a hierarchical tree structure 15 | * Different highlighting is provided for the following types of nodes: 16 | * files 17 | * directories 18 | * sym-links 19 | * windows .lnk files 20 | * read-only files 21 | * executable files 22 | * Many (customisable) mappings are provided to manipulate the tree: 23 | * Mappings to open/close/explore directory nodes 24 | * Mappings to open files in new/existing windows/tabs 25 | * Mappings to change the current root of the tree 26 | * Mappings to navigate around the tree 27 | * ... 28 | * Directories and files can be bookmarked. 29 | * Most NERD tree navigation can also be done with the mouse 30 | * Filtering of tree content (can be toggled at runtime) 31 | * custom file filters to prevent e.g. vim backup files being displayed 32 | * optional displaying of hidden files (. files) 33 | * files can be "turned off" so that only directories are displayed 34 | * The position and size of the NERD tree window can be customised 35 | * The order in which the nodes in the tree are listed can be customised. 36 | * A model of your filesystem is created/maintained as you explore it. This 37 | has several advantages: 38 | * All filesystem information is cached and is only re-read on demand 39 | * If you revisit a part of the tree that you left earlier in your 40 | session, the directory nodes will be opened/closed as you left them 41 | * The script remembers the cursor position and window position in the NERD 42 | tree so you can toggle it off (or just close the tree window) and then 43 | reopen it (with NERDTreeToggle) the NERD tree window will appear exactly 44 | as you left it 45 | * You can have a separate NERD tree for each tab, share trees across tabs, 46 | or a mix of both. 47 | * By default the script overrides the default file browser (netrw), so if 48 | you :edit a directory a (slightly modified) NERD tree will appear in the 49 | current window 50 | * A programmable menu system is provided (simulates right clicking on a node) 51 | * one default menu plugin is provided to perform basic filesystem 52 | operations (create/delete/move/copy files/directories) 53 | * There's an API for adding your own keymappings 54 | 55 | Installation 56 | ------------ 57 | 58 | ####[pathogen.vim](https://github.com/tpope/vim-pathogen) 59 | 60 | cd ~/.vim/bundle 61 | git clone https://github.com/scrooloose/nerdtree.git 62 | 63 | Then reload vim, run `:Helptags`, and check out `:help NERD_tree.txt`. 64 | 65 | 66 | ####[apt-vim](https://github.com/egalpin/apt-vim) 67 | 68 | apt-vim install -y https://github.com/scrooloose/nerdtree.git 69 | 70 | 71 | 72 | Faq 73 | --- 74 | 75 | > Is there any support for `git` flags? 76 | 77 | Yes, install [nerdtree-git-plugin](https://github.com/Xuyuanp/nerdtree-git-plugin). 78 | 79 | --- 80 | 81 | > Can I have the nerdtree on every tab automatically? 82 | 83 | Nope. If this is something you want then chances are you aren't using tabs and 84 | buffers as they were intended to be used. Read this 85 | http://stackoverflow.com/questions/102384/using-vims-tabs-like-buffers 86 | 87 | If you are interested in this behaviour then consider [vim-nerdtree-tabs](https://github.com/jistr/vim-nerdtree-tabs) 88 | 89 | --- 90 | > How can I open a NERDTree automatically when vim starts up? 91 | 92 | Stick this in your vimrc: `autocmd vimenter * NERDTree` 93 | 94 | --- 95 | > How can I open a NERDTree automatically when vim starts up if no files were specified? 96 | 97 | Stick this in your vimrc: 98 | 99 | autocmd StdinReadPre * let s:std_in=1 100 | autocmd VimEnter * if argc() == 0 && !exists("s:std_in") | NERDTree | endif 101 | 102 | Note: Now start vim with plain `vim`, not `vim .` 103 | 104 | --- 105 | > How can I map a specific key or shortcut to open NERDTree? 106 | 107 | Stick this in your vimrc to open NERDTree with `Ctrl+n` (you can set whatever key you want): 108 | 109 | map :NERDTreeToggle 110 | 111 | --- 112 | > How can I close vim if the only window left open is a NERDTree? 113 | 114 | Stick this in your vimrc: 115 | 116 | autocmd bufenter * if (winnr("$") == 1 && exists("b:NERDTree") && b:NERDTree.isTabTree()) | q | endif 117 | 118 | --- 119 | > Can I have different highlighting for different file extensions? 120 | 121 | See here: https://github.com/scrooloose/nerdtree/issues/433#issuecomment-92590696 122 | 123 | --- 124 | > How can I change default arrows? 125 | 126 | Use these variables in your vimrc. Note that below are default arrow symbols 127 | 128 | let g:NERDTreeDirArrowExpandable = '▸' 129 | let g:NERDTreeDirArrowCollapsible = '▾' 130 | -------------------------------------------------------------------------------- /lib/nerdtree/key_map.vim: -------------------------------------------------------------------------------- 1 | "CLASS: KeyMap 2 | "============================================================ 3 | let s:KeyMap = {} 4 | let g:NERDTreeKeyMap = s:KeyMap 5 | 6 | "FUNCTION: KeyMap.All() {{{1 7 | function! s:KeyMap.All() 8 | if !exists("s:keyMaps") 9 | let s:keyMaps = [] 10 | endif 11 | return s:keyMaps 12 | endfunction 13 | 14 | "FUNCTION: KeyMap.FindFor(key, scope) {{{1 15 | function! s:KeyMap.FindFor(key, scope) 16 | for i in s:KeyMap.All() 17 | if i.key ==# a:key && i.scope ==# a:scope 18 | return i 19 | endif 20 | endfor 21 | return {} 22 | endfunction 23 | 24 | "FUNCTION: KeyMap.BindAll() {{{1 25 | function! s:KeyMap.BindAll() 26 | for i in s:KeyMap.All() 27 | call i.bind() 28 | endfor 29 | endfunction 30 | 31 | "FUNCTION: KeyMap.bind() {{{1 32 | function! s:KeyMap.bind() 33 | " If the key sequence we're trying to map contains any '<>' notation, we 34 | " must replace each of the '<' characters with '' to ensure the string 35 | " is not translated into its corresponding keycode during the later part 36 | " of the map command below 37 | " :he <> 38 | let specialNotationRegex = '\m<\([[:alnum:]_-]\+>\)' 39 | if self.key =~# specialNotationRegex 40 | let keymapInvokeString = substitute(self.key, specialNotationRegex, '\1', 'g') 41 | else 42 | let keymapInvokeString = self.key 43 | endif 44 | 45 | let premap = self.key == "" ? " " : " " 46 | 47 | exec 'nnoremap '. self.key . premap . ':call nerdtree#ui_glue#invokeKeyMap("'. keymapInvokeString .'")' 48 | endfunction 49 | 50 | "FUNCTION: KeyMap.Remove(key, scope) {{{1 51 | function! s:KeyMap.Remove(key, scope) 52 | let maps = s:KeyMap.All() 53 | for i in range(len(maps)) 54 | if maps[i].key ==# a:key && maps[i].scope ==# a:scope 55 | return remove(maps, i) 56 | endif 57 | endfor 58 | endfunction 59 | 60 | "FUNCTION: KeyMap.invoke() {{{1 61 | "Call the KeyMaps callback function 62 | function! s:KeyMap.invoke(...) 63 | let Callback = function(self.callback) 64 | if a:0 65 | call Callback(a:1) 66 | else 67 | call Callback() 68 | endif 69 | endfunction 70 | 71 | "FUNCTION: KeyMap.Invoke() {{{1 72 | "Find a keymapping for a:key and the current scope invoke it. 73 | " 74 | "Scope is determined as follows: 75 | " * if the cursor is on a dir node then "DirNode" 76 | " * if the cursor is on a file node then "FileNode" 77 | " * if the cursor is on a bookmark then "Bookmark" 78 | " 79 | "If a keymap has the scope of "all" then it will be called if no other keymap 80 | "is found for a:key and the scope. 81 | function! s:KeyMap.Invoke(key) 82 | 83 | "required because clicking the command window below another window still 84 | "invokes the mapping - but changes the window cursor 85 | "is in first 86 | " 87 | "TODO: remove this check when the vim bug is fixed 88 | if !g:NERDTree.ExistsForBuf() 89 | return {} 90 | endif 91 | 92 | let node = g:NERDTreeFileNode.GetSelected() 93 | if !empty(node) 94 | 95 | "try file node 96 | if !node.path.isDirectory 97 | let km = s:KeyMap.FindFor(a:key, "FileNode") 98 | if !empty(km) 99 | return km.invoke(node) 100 | endif 101 | endif 102 | 103 | "try dir node 104 | if node.path.isDirectory 105 | let km = s:KeyMap.FindFor(a:key, "DirNode") 106 | if !empty(km) 107 | return km.invoke(node) 108 | endif 109 | endif 110 | 111 | "try generic node 112 | let km = s:KeyMap.FindFor(a:key, "Node") 113 | if !empty(km) 114 | return km.invoke(node) 115 | endif 116 | 117 | endif 118 | 119 | "try bookmark 120 | let bm = g:NERDTreeBookmark.GetSelected() 121 | if !empty(bm) 122 | let km = s:KeyMap.FindFor(a:key, "Bookmark") 123 | if !empty(km) 124 | return km.invoke(bm) 125 | endif 126 | endif 127 | 128 | "try all 129 | let km = s:KeyMap.FindFor(a:key, "all") 130 | if !empty(km) 131 | return km.invoke() 132 | endif 133 | endfunction 134 | 135 | "FUNCTION: KeyMap.Create(options) {{{1 136 | function! s:KeyMap.Create(options) 137 | let opts = extend({'scope': 'all', 'quickhelpText': ''}, copy(a:options)) 138 | 139 | "dont override other mappings unless the 'override' option is given 140 | if get(opts, 'override', 0) == 0 && !empty(s:KeyMap.FindFor(opts['key'], opts['scope'])) 141 | return 142 | end 143 | 144 | let newKeyMap = copy(self) 145 | let newKeyMap.key = opts['key'] 146 | let newKeyMap.quickhelpText = opts['quickhelpText'] 147 | let newKeyMap.callback = opts['callback'] 148 | let newKeyMap.scope = opts['scope'] 149 | 150 | call s:KeyMap.Add(newKeyMap) 151 | endfunction 152 | 153 | "FUNCTION: KeyMap.Add(keymap) {{{1 154 | function! s:KeyMap.Add(keymap) 155 | call s:KeyMap.Remove(a:keymap.key, a:keymap.scope) 156 | call add(s:KeyMap.All(), a:keymap) 157 | endfunction 158 | 159 | " vim: set sw=4 sts=4 et fdm=marker: 160 | -------------------------------------------------------------------------------- /lib/nerdtree/menu_controller.vim: -------------------------------------------------------------------------------- 1 | "CLASS: MenuController 2 | "============================================================ 3 | let s:MenuController = {} 4 | let g:NERDTreeMenuController = s:MenuController 5 | 6 | "FUNCTION: MenuController.New(menuItems) {{{1 7 | "create a new menu controller that operates on the given menu items 8 | function! s:MenuController.New(menuItems) 9 | let newMenuController = copy(self) 10 | if a:menuItems[0].isSeparator() 11 | let newMenuController.menuItems = a:menuItems[1:-1] 12 | else 13 | let newMenuController.menuItems = a:menuItems 14 | endif 15 | return newMenuController 16 | endfunction 17 | 18 | "FUNCTION: MenuController.showMenu() {{{1 19 | "start the main loop of the menu and get the user to choose/execute a menu 20 | "item 21 | function! s:MenuController.showMenu() 22 | call self._saveOptions() 23 | 24 | try 25 | let self.selection = 0 26 | 27 | let done = 0 28 | while !done 29 | redraw! 30 | call self._echoPrompt() 31 | let key = nr2char(getchar()) 32 | let done = self._handleKeypress(key) 33 | endwhile 34 | finally 35 | call self._restoreOptions() 36 | endtry 37 | 38 | if self.selection != -1 39 | let m = self._current() 40 | call m.execute() 41 | endif 42 | endfunction 43 | 44 | "FUNCTION: MenuController._echoPrompt() {{{1 45 | function! s:MenuController._echoPrompt() 46 | echo "NERDTree Menu. Use j/k/enter and the shortcuts indicated" 47 | echo "==========================================================" 48 | 49 | for i in range(0, len(self.menuItems)-1) 50 | if self.selection == i 51 | echo "> " . self.menuItems[i].text 52 | else 53 | echo " " . self.menuItems[i].text 54 | endif 55 | endfor 56 | endfunction 57 | 58 | "FUNCTION: MenuController._current(key) {{{1 59 | "get the MenuItem that is currently selected 60 | function! s:MenuController._current() 61 | return self.menuItems[self.selection] 62 | endfunction 63 | 64 | "FUNCTION: MenuController._handleKeypress(key) {{{1 65 | "change the selection (if appropriate) and return 1 if the user has made 66 | "their choice, 0 otherwise 67 | function! s:MenuController._handleKeypress(key) 68 | if a:key == 'j' 69 | call self._cursorDown() 70 | elseif a:key == 'k' 71 | call self._cursorUp() 72 | elseif a:key == nr2char(27) "escape 73 | let self.selection = -1 74 | return 1 75 | elseif a:key == "\r" || a:key == "\n" "enter and ctrl-j 76 | return 1 77 | else 78 | let index = self._nextIndexFor(a:key) 79 | if index != -1 80 | let self.selection = index 81 | if len(self._allIndexesFor(a:key)) == 1 82 | return 1 83 | endif 84 | endif 85 | endif 86 | 87 | return 0 88 | endfunction 89 | 90 | "FUNCTION: MenuController._allIndexesFor(shortcut) {{{1 91 | "get indexes to all menu items with the given shortcut 92 | function! s:MenuController._allIndexesFor(shortcut) 93 | let toReturn = [] 94 | 95 | for i in range(0, len(self.menuItems)-1) 96 | if self.menuItems[i].shortcut == a:shortcut 97 | call add(toReturn, i) 98 | endif 99 | endfor 100 | 101 | return toReturn 102 | endfunction 103 | 104 | "FUNCTION: MenuController._nextIndexFor(shortcut) {{{1 105 | "get the index to the next menu item with the given shortcut, starts from the 106 | "current cursor location and wraps around to the top again if need be 107 | function! s:MenuController._nextIndexFor(shortcut) 108 | for i in range(self.selection+1, len(self.menuItems)-1) 109 | if self.menuItems[i].shortcut == a:shortcut 110 | return i 111 | endif 112 | endfor 113 | 114 | for i in range(0, self.selection) 115 | if self.menuItems[i].shortcut == a:shortcut 116 | return i 117 | endif 118 | endfor 119 | 120 | return -1 121 | endfunction 122 | 123 | "FUNCTION: MenuController._setCmdheight() {{{1 124 | "sets &cmdheight to whatever is needed to display the menu 125 | function! s:MenuController._setCmdheight() 126 | let &cmdheight = len(self.menuItems) + 3 127 | endfunction 128 | 129 | "FUNCTION: MenuController._saveOptions() {{{1 130 | "set any vim options that are required to make the menu work (saving their old 131 | "values) 132 | function! s:MenuController._saveOptions() 133 | let self._oldLazyredraw = &lazyredraw 134 | let self._oldCmdheight = &cmdheight 135 | set nolazyredraw 136 | call self._setCmdheight() 137 | endfunction 138 | 139 | "FUNCTION: MenuController._restoreOptions() {{{1 140 | "restore the options we saved in _saveOptions() 141 | function! s:MenuController._restoreOptions() 142 | let &cmdheight = self._oldCmdheight 143 | let &lazyredraw = self._oldLazyredraw 144 | endfunction 145 | 146 | "FUNCTION: MenuController._cursorDown() {{{1 147 | "move the cursor to the next menu item, skipping separators 148 | function! s:MenuController._cursorDown() 149 | let done = 0 150 | while !done 151 | if self.selection < len(self.menuItems)-1 152 | let self.selection += 1 153 | else 154 | let self.selection = 0 155 | endif 156 | 157 | if !self._current().isSeparator() 158 | let done = 1 159 | endif 160 | endwhile 161 | endfunction 162 | 163 | "FUNCTION: MenuController._cursorUp() {{{1 164 | "move the cursor to the previous menu item, skipping separators 165 | function! s:MenuController._cursorUp() 166 | let done = 0 167 | while !done 168 | if self.selection > 0 169 | let self.selection -= 1 170 | else 171 | let self.selection = len(self.menuItems)-1 172 | endif 173 | 174 | if !self._current().isSeparator() 175 | let done = 1 176 | endif 177 | endwhile 178 | endfunction 179 | 180 | " vim: set sw=4 sts=4 et fdm=marker: 181 | -------------------------------------------------------------------------------- /lib/nerdtree/nerdtree.vim: -------------------------------------------------------------------------------- 1 | "CLASS: NERDTree 2 | "============================================================ 3 | let s:NERDTree = {} 4 | let g:NERDTree = s:NERDTree 5 | 6 | "FUNCTION: s:NERDTree.AddPathFilter() {{{1 7 | function! s:NERDTree.AddPathFilter(callback) 8 | call add(s:NERDTree.PathFilters(), a:callback) 9 | endfunction 10 | 11 | "FUNCTION: s:NERDTree.changeRoot(node) {{{1 12 | function! s:NERDTree.changeRoot(node) 13 | if a:node.path.isDirectory 14 | let self.root = a:node 15 | else 16 | call a:node.cacheParent() 17 | let self.root = a:node.parent 18 | endif 19 | 20 | call self.root.open() 21 | 22 | "change dir to the dir of the new root if instructed to 23 | if g:NERDTreeChDirMode ==# 2 24 | exec "cd " . self.root.path.str({'format': 'Edit'}) 25 | endif 26 | 27 | call self.render() 28 | call self.root.putCursorHere(0, 0) 29 | 30 | silent doautocmd User NERDTreeNewRoot 31 | endfunction 32 | 33 | "FUNCTION: s:NERDTree.Close() {{{1 34 | "Closes the tab tree window for this tab 35 | function! s:NERDTree.Close() 36 | if !s:NERDTree.IsOpen() 37 | return 38 | endif 39 | 40 | if winnr("$") != 1 41 | if winnr() == s:NERDTree.GetWinNum() 42 | call nerdtree#exec("wincmd p") 43 | let bufnr = bufnr("") 44 | call nerdtree#exec("wincmd p") 45 | else 46 | let bufnr = bufnr("") 47 | endif 48 | 49 | call nerdtree#exec(s:NERDTree.GetWinNum() . " wincmd w") 50 | close 51 | call nerdtree#exec(bufwinnr(bufnr) . " wincmd w") 52 | else 53 | close 54 | endif 55 | endfunction 56 | 57 | "FUNCTION: s:NERDTree.CloseIfQuitOnOpen() {{{1 58 | "Closes the NERD tree window if the close on open option is set 59 | function! s:NERDTree.CloseIfQuitOnOpen() 60 | if g:NERDTreeQuitOnOpen && s:NERDTree.IsOpen() 61 | call s:NERDTree.Close() 62 | endif 63 | endfunction 64 | 65 | "FUNCTION: s:NERDTree.CursorToBookmarkTable(){{{1 66 | "Places the cursor at the top of the bookmarks table 67 | function! s:NERDTree.CursorToBookmarkTable() 68 | if !b:NERDTree.ui.getShowBookmarks() 69 | throw "NERDTree.IllegalOperationError: cant find bookmark table, bookmarks arent active" 70 | endif 71 | 72 | if g:NERDTreeMinimalUI 73 | return cursor(1, 2) 74 | endif 75 | 76 | let rootNodeLine = b:NERDTree.ui.getRootLineNum() 77 | 78 | let line = 1 79 | while getline(line) !~# '^>-\+Bookmarks-\+$' 80 | let line = line + 1 81 | if line >= rootNodeLine 82 | throw "NERDTree.BookmarkTableNotFoundError: didnt find the bookmarks table" 83 | endif 84 | endwhile 85 | call cursor(line, 2) 86 | endfunction 87 | 88 | "FUNCTION: s:NERDTree.CursorToTreeWin(){{{1 89 | "Places the cursor in the nerd tree window 90 | function! s:NERDTree.CursorToTreeWin() 91 | call g:NERDTree.MustBeOpen() 92 | call nerdtree#exec(g:NERDTree.GetWinNum() . "wincmd w") 93 | endfunction 94 | 95 | " Function: s:NERDTree.ExistsForBuffer() {{{1 96 | " Returns 1 if a nerd tree root exists in the current buffer 97 | function! s:NERDTree.ExistsForBuf() 98 | return exists("b:NERDTree") 99 | endfunction 100 | 101 | " Function: s:NERDTree.ExistsForTab() {{{1 102 | " Returns 1 if a nerd tree root exists in the current tab 103 | function! s:NERDTree.ExistsForTab() 104 | if !exists("t:NERDTreeBufName") 105 | return 106 | end 107 | 108 | "check b:NERDTree is still there and hasn't been e.g. :bdeleted 109 | return !empty(getbufvar(bufnr(t:NERDTreeBufName), 'NERDTree')) 110 | endfunction 111 | 112 | function! s:NERDTree.ForCurrentBuf() 113 | if s:NERDTree.ExistsForBuf() 114 | return b:NERDTree 115 | else 116 | return {} 117 | endif 118 | endfunction 119 | 120 | "FUNCTION: s:NERDTree.ForCurrentTab() {{{1 121 | function! s:NERDTree.ForCurrentTab() 122 | if !s:NERDTree.ExistsForTab() 123 | return 124 | endif 125 | 126 | let bufnr = bufnr(t:NERDTreeBufName) 127 | return getbufvar(bufnr, "NERDTree") 128 | endfunction 129 | 130 | "FUNCTION: s:NERDTree.getRoot() {{{1 131 | function! s:NERDTree.getRoot() 132 | return self.root 133 | endfunction 134 | 135 | "FUNCTION: s:NERDTree.GetWinNum() {{{1 136 | "gets the nerd tree window number for this tab 137 | function! s:NERDTree.GetWinNum() 138 | if exists("t:NERDTreeBufName") 139 | return bufwinnr(t:NERDTreeBufName) 140 | endif 141 | 142 | return -1 143 | endfunction 144 | 145 | "FUNCTION: s:NERDTree.IsOpen() {{{1 146 | function! s:NERDTree.IsOpen() 147 | return s:NERDTree.GetWinNum() != -1 148 | endfunction 149 | 150 | "FUNCTION: s:NERDTree.isTabTree() {{{1 151 | function! s:NERDTree.isTabTree() 152 | return self._type == "tab" 153 | endfunction 154 | 155 | "FUNCTION: s:NERDTree.isWinTree() {{{1 156 | function! s:NERDTree.isWinTree() 157 | return self._type == "window" 158 | endfunction 159 | 160 | "FUNCTION: s:NERDTree.MustBeOpen() {{{1 161 | function! s:NERDTree.MustBeOpen() 162 | if !s:NERDTree.IsOpen() 163 | throw "NERDTree.TreeNotOpen" 164 | endif 165 | endfunction 166 | 167 | "FUNCTION: s:NERDTree.New() {{{1 168 | function! s:NERDTree.New(path, type) 169 | let newObj = copy(self) 170 | let newObj.ui = g:NERDTreeUI.New(newObj) 171 | let newObj.root = g:NERDTreeDirNode.New(a:path, newObj) 172 | let newObj._type = a:type 173 | return newObj 174 | endfunction 175 | 176 | "FUNCTION: s:NERDTree.PathFilters() {{{1 177 | function! s:NERDTree.PathFilters() 178 | if !exists('s:NERDTree._PathFilters') 179 | let s:NERDTree._PathFilters = [] 180 | endif 181 | return s:NERDTree._PathFilters 182 | endfunction 183 | 184 | "FUNCTION: s:NERDTree.previousBuf() {{{1 185 | function! s:NERDTree.previousBuf() 186 | return self._previousBuf 187 | endfunction 188 | 189 | "FUNCTION: s:NERDTree.render() {{{1 190 | "A convenience function - since this is called often 191 | function! s:NERDTree.render() 192 | call self.ui.render() 193 | endfunction 194 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | Next 2 | - Rename "primary" and "secondary" trees to "tab" and "window" trees. 3 | - Move a bunch of buffer level variables into the NERDTree and UI classes. 4 | - Display cascading dirs on one line to save vertical/horizontal space (@matt-gardner: brainstorming/testing) 5 | - Remove the old style UI - Remove 'NERDTreeDirArrows' option. 6 | - On windows default to + and ~ for expand/collapse directory symbols. 7 | - Lots more refactoring. Move a bunch of b: level vars into b:NERDTree and friends. 8 | 9 | 5.0.0 10 | - Refactor the code significantly: 11 | * Break the classes out into their own files. 12 | * Make the majority of the code OO - previously large parts were 13 | effectively a tangle of "global" methods. 14 | - Add an API to assign flags to nodes. This allows VCS plugins like 15 | https://github.com/Xuyuanp/nerdtree-git-plugin to exist. Thanks to 16 | Xuyuanp for helping design/test/build said API. 17 | - add 'scope' argument to the key map API see :help NERDTreeAddKeyMap() 18 | - add magic [[dir]] and [[file]] flags to NERDTreeIgnore 19 | - add support for custom path filters. See :help NERDTreeAddPathFilter() 20 | - add path listener API. See :help NERDTreePathListenerAPI. 21 | - expand the fs menu functionality to list file properties (PhilRunninger, 22 | apbarrero, JESii) 23 | - make bookmarks work with `~` home shortcuts (hiberabyss) 24 | - show OSX specific fsmenu options in regular vim on mac (evindor) 25 | - make dir arrow icons configurable (PickRelated) 26 | - optimise node sorting performance when opening large dirs (vtsang) 27 | - make the root note render prettier by truncating it at a path slash (gcmt) 28 | - remove NERDChristmasTree option - its always christmas now 29 | - add "cascade" open and closing for dirs containing only another single 30 | dir. See :help NERDTreeCascadeOpenSingleChildDir (pendulm) 31 | 32 | Many other fixes, doc updates and contributions from: 33 | actionshrimp 34 | SchDen 35 | egalpin 36 | cperl82 - many small fixes 37 | toiffel 38 | WoLpH 39 | handcraftedbits 40 | devmanhinton 41 | xiaodili 42 | zhangoose 43 | gastropoda 44 | mixvin 45 | alvan 46 | lucascaton 47 | kelaban 48 | shanesmith 49 | staeff 50 | pendulm 51 | stephenprater 52 | franksort 53 | agrussellknives 54 | AndrewRadev 55 | Twinside 56 | 57 | 4.2.0 58 | - Add NERDTreeDirArrows option to make the UI use pretty arrow chars 59 | instead of the old +~| chars to define the tree structure (sickill) 60 | - shift the syntax highlighting out into its own syntax file (gnap) 61 | - add some mac specific options to the filesystem menu - for macvim 62 | only (andersonfreitas) 63 | - Add NERDTreeMinimalUI option to remove some non functional parts of the 64 | nerdtree ui (camthompson) 65 | - tweak the behaviour of :NERDTreeFind - see :help :NERDTreeFind for the 66 | new behaviour (benjamingeiger) 67 | - if no name is given to :Bookmark, make it default to the name of the 68 | target file/dir (minyoung) 69 | - use 'file' completion when doing copying, create, and move 70 | operations (EvanDotPro) 71 | - lots of misc bug fixes (paddyoloughlin, sdewald, camthompson, Vitaly 72 | Bogdanov, AndrewRadev, mathias, scottstvnsn, kml, wycats, me RAWR!) 73 | 74 | 4.1.0 75 | features: 76 | - NERDTreeFind to reveal the node for the current buffer in the tree, 77 | see |NERDTreeFind|. This effectively merges the FindInNERDTree plugin (by 78 | Doug McInnes) into the script. 79 | - make NERDTreeQuitOnOpen apply to the t/T keymaps too. Thanks to Stefan 80 | Ritter and Rémi Prévost. 81 | - truncate the root node if wider than the tree window. Thanks to Victor 82 | Gonzalez. 83 | 84 | bugfixes: 85 | - really fix window state restoring 86 | - fix some win32 path escaping issues. Thanks to Stephan Baumeister, Ricky, 87 | jfilip1024, and Chris Chambers 88 | 89 | 4.0.0 90 | - add a new programmable menu system (see :help NERDTreeMenu). 91 | - add new APIs to add menus/menu-items to the menu system as well as 92 | custom key mappings to the NERD tree buffer (see :help NERDTreeAPI). 93 | - removed the old API functions 94 | - added a mapping to maximize/restore the size of nerd tree window, thanks 95 | to Guillaume Duranceau for the patch. See :help NERDTree-A for details. 96 | 97 | - fix a bug where secondary nerd trees (netrw hijacked trees) and 98 | NERDTreeQuitOnOpen didnt play nicely, thanks to Curtis Harvey. 99 | - fix a bug where the script ignored directories whose name ended in a dot, 100 | thanks to Aggelos Orfanakos for the patch. 101 | - fix a bug when using the x mapping on the tree root, thanks to Bryan 102 | Venteicher for the patch. 103 | - fix a bug where the cursor position/window size of the nerd tree buffer 104 | wasnt being stored on closing the window, thanks to Richard Hart. 105 | - fix a bug where NERDTreeMirror would mirror the wrong tree 106 | 107 | 3.1.1 108 | - fix a bug where a non-listed no-name buffer was getting created every 109 | time the tree windows was created, thanks to Derek Wyatt and owen1 110 | - make behave the same as the 'o' mapping 111 | - some helptag fixes in the doc, thanks strull 112 | - fix a bug when using :set nohidden and opening a file where the previous 113 | buf was modified. Thanks iElectric 114 | - other minor fixes 115 | 116 | 3.1.0 117 | New features: 118 | - add mappings to open files in a vsplit, see :help NERDTree-s and :help 119 | NERDTree-gs 120 | - make the statusline for the nerd tree window default to something 121 | hopefully more useful. See :help 'NERDTreeStatusline' 122 | Bugfixes: 123 | - make the hijack netrw functionality work when vim is started with "vim 124 | " (thanks to Alf Mikula for the patch). 125 | - fix a bug where the CWD wasnt being changed for some operations even when 126 | NERDTreeChDirMode==2 (thanks to Lucas S. Buchala) 127 | - add -bar to all the nerd tree :commands so they can chain with other 128 | :commands (thanks to tpope) 129 | - fix bugs when ignorecase was set (thanks to nach) 130 | - fix a bug with the relative path code (thanks to nach) 131 | - fix a bug where doing a :cd would cause :NERDTreeToggle to fail (thanks nach) 132 | 133 | 134 | 3.0.1 135 | Bugfixes: 136 | - fix bugs with :NERDTreeToggle and :NERDTreeMirror when 'hidden 137 | was not set 138 | - fix a bug where :NERDTree would fail if was relative and 139 | didnt start with a ./ or ../ Thanks to James Kanze. 140 | - make the q mapping work with secondary (:e style) trees, 141 | thanks to jamessan 142 | - fix a bunch of small bugs with secondary trees 143 | 144 | More insane refactoring. 145 | 146 | 3.0.0 147 | - hijack netrw so that doing an :edit will put a NERD tree in 148 | the window rather than a netrw browser. See :help 'NERDTreeHijackNetrw' 149 | - allow sharing of trees across tabs, see :help :NERDTreeMirror 150 | - remove "top" and "bottom" as valid settings for NERDTreeWinPos 151 | - change the '' mapping to 'i' 152 | - change the 'H' mapping to 'I' 153 | - lots of refactoring 154 | -------------------------------------------------------------------------------- /plugin/NERD_tree.vim: -------------------------------------------------------------------------------- 1 | " ============================================================================ 2 | " File: NERD_tree.vim 3 | " Maintainer: Martin Grenfell 4 | " License: This program is free software. It comes without any warranty, 5 | " to the extent permitted by applicable law. You can redistribute 6 | " it and/or modify it under the terms of the Do What The Fuck You 7 | " Want To Public License, Version 2, as published by Sam Hocevar. 8 | " See http://sam.zoy.org/wtfpl/COPYING for more details. 9 | " 10 | " ============================================================================ 11 | " 12 | " SECTION: Script init stuff {{{1 13 | "============================================================ 14 | if exists("loaded_nerd_tree") 15 | finish 16 | endif 17 | if v:version < 700 18 | echoerr "NERDTree: this plugin requires vim >= 7. DOWNLOAD IT! You'll thank me later!" 19 | finish 20 | endif 21 | let loaded_nerd_tree = 1 22 | 23 | "for line continuation - i.e dont want C in &cpo 24 | let s:old_cpo = &cpo 25 | set cpo&vim 26 | 27 | "Function: s:initVariable() function {{{2 28 | "This function is used to initialise a given variable to a given value. The 29 | "variable is only initialised if it does not exist prior 30 | " 31 | "Args: 32 | "var: the name of the var to be initialised 33 | "value: the value to initialise var to 34 | " 35 | "Returns: 36 | "1 if the var is set, 0 otherwise 37 | function! s:initVariable(var, value) 38 | if !exists(a:var) 39 | exec 'let ' . a:var . ' = ' . "'" . substitute(a:value, "'", "''", "g") . "'" 40 | return 1 41 | endif 42 | return 0 43 | endfunction 44 | 45 | "SECTION: Init variable calls and other random constants {{{2 46 | call s:initVariable("g:NERDTreeAutoCenter", 1) 47 | call s:initVariable("g:NERDTreeAutoCenterThreshold", 3) 48 | call s:initVariable("g:NERDTreeCaseSensitiveSort", 0) 49 | call s:initVariable("g:NERDTreeSortHiddenFirst", 1) 50 | call s:initVariable("g:NERDTreeChDirMode", 0) 51 | call s:initVariable("g:NERDTreeCreatePrefix", "silent") 52 | call s:initVariable("g:NERDTreeMinimalUI", 0) 53 | if !exists("g:NERDTreeIgnore") 54 | let g:NERDTreeIgnore = ['\~$'] 55 | endif 56 | call s:initVariable("g:NERDTreeBookmarksFile", expand('$HOME') . '/.NERDTreeBookmarks') 57 | call s:initVariable("g:NERDTreeBookmarksSort", 1) 58 | call s:initVariable("g:NERDTreeHighlightCursorline", 1) 59 | call s:initVariable("g:NERDTreeHijackNetrw", 1) 60 | call s:initVariable("g:NERDTreeMouseMode", 1) 61 | call s:initVariable("g:NERDTreeNotificationThreshold", 100) 62 | call s:initVariable("g:NERDTreeQuitOnOpen", 0) 63 | call s:initVariable("g:NERDTreeRespectWildIgnore", 0) 64 | call s:initVariable("g:NERDTreeShowBookmarks", 0) 65 | call s:initVariable("g:NERDTreeShowFiles", 1) 66 | call s:initVariable("g:NERDTreeShowHidden", 0) 67 | call s:initVariable("g:NERDTreeShowLineNumbers", 0) 68 | call s:initVariable("g:NERDTreeSortDirs", 1) 69 | 70 | if !nerdtree#runningWindows() 71 | call s:initVariable("g:NERDTreeDirArrowExpandable", "▸") 72 | call s:initVariable("g:NERDTreeDirArrowCollapsible", "▾") 73 | else 74 | call s:initVariable("g:NERDTreeDirArrowExpandable", "+") 75 | call s:initVariable("g:NERDTreeDirArrowCollapsible", "~") 76 | endif 77 | call s:initVariable("g:NERDTreeCascadeOpenSingleChildDir", 1) 78 | 79 | if !exists("g:NERDTreeSortOrder") 80 | let g:NERDTreeSortOrder = ['\/$', '*', '\.swp$', '\.bak$', '\~$'] 81 | else 82 | "if there isnt a * in the sort sequence then add one 83 | if count(g:NERDTreeSortOrder, '*') < 1 84 | call add(g:NERDTreeSortOrder, '*') 85 | endif 86 | endif 87 | 88 | call s:initVariable("g:NERDTreeGlyphReadOnly", "RO") 89 | 90 | if !exists('g:NERDTreeStatusline') 91 | 92 | "the exists() crap here is a hack to stop vim spazzing out when 93 | "loading a session that was created with an open nerd tree. It spazzes 94 | "because it doesnt store b:NERDTree(its a b: var, and its a hash) 95 | let g:NERDTreeStatusline = "%{exists('b:NERDTree')?b:NERDTree.root.path.str():''}" 96 | 97 | endif 98 | call s:initVariable("g:NERDTreeWinPos", "left") 99 | call s:initVariable("g:NERDTreeWinSize", 31) 100 | 101 | "init the shell commands that will be used to copy nodes, and remove dir trees 102 | " 103 | "Note: the space after the command is important 104 | if nerdtree#runningWindows() 105 | call s:initVariable("g:NERDTreeRemoveDirCmd", 'rmdir /s /q ') 106 | else 107 | call s:initVariable("g:NERDTreeRemoveDirCmd", 'rm -rf ') 108 | call s:initVariable("g:NERDTreeCopyCmd", 'cp -r ') 109 | endif 110 | 111 | 112 | "SECTION: Init variable calls for key mappings {{{2 113 | call s:initVariable("g:NERDTreeMapActivateNode", "o") 114 | call s:initVariable("g:NERDTreeMapChangeRoot", "C") 115 | call s:initVariable("g:NERDTreeMapChdir", "cd") 116 | call s:initVariable("g:NERDTreeMapCloseChildren", "X") 117 | call s:initVariable("g:NERDTreeMapCloseDir", "x") 118 | call s:initVariable("g:NERDTreeMapDeleteBookmark", "D") 119 | call s:initVariable("g:NERDTreeMapMenu", "m") 120 | call s:initVariable("g:NERDTreeMapHelp", "?") 121 | call s:initVariable("g:NERDTreeMapJumpFirstChild", "K") 122 | call s:initVariable("g:NERDTreeMapJumpLastChild", "J") 123 | call s:initVariable("g:NERDTreeMapJumpNextSibling", "") 124 | call s:initVariable("g:NERDTreeMapJumpParent", "p") 125 | call s:initVariable("g:NERDTreeMapJumpPrevSibling", "") 126 | call s:initVariable("g:NERDTreeMapJumpRoot", "P") 127 | call s:initVariable("g:NERDTreeMapOpenExpl", "e") 128 | call s:initVariable("g:NERDTreeMapOpenInTab", "t") 129 | call s:initVariable("g:NERDTreeMapOpenInTabSilent", "T") 130 | call s:initVariable("g:NERDTreeMapOpenRecursively", "O") 131 | call s:initVariable("g:NERDTreeMapOpenSplit", "i") 132 | call s:initVariable("g:NERDTreeMapOpenVSplit", "s") 133 | call s:initVariable("g:NERDTreeMapPreview", "g" . NERDTreeMapActivateNode) 134 | call s:initVariable("g:NERDTreeMapPreviewSplit", "g" . NERDTreeMapOpenSplit) 135 | call s:initVariable("g:NERDTreeMapPreviewVSplit", "g" . NERDTreeMapOpenVSplit) 136 | call s:initVariable("g:NERDTreeMapQuit", "q") 137 | call s:initVariable("g:NERDTreeMapRefresh", "r") 138 | call s:initVariable("g:NERDTreeMapRefreshRoot", "R") 139 | call s:initVariable("g:NERDTreeMapToggleBookmarks", "B") 140 | call s:initVariable("g:NERDTreeMapToggleFiles", "F") 141 | call s:initVariable("g:NERDTreeMapToggleFilters", "f") 142 | call s:initVariable("g:NERDTreeMapToggleHidden", "I") 143 | call s:initVariable("g:NERDTreeMapToggleZoom", "A") 144 | call s:initVariable("g:NERDTreeMapUpdir", "u") 145 | call s:initVariable("g:NERDTreeMapUpdirKeepOpen", "U") 146 | call s:initVariable("g:NERDTreeMapCWD", "CD") 147 | 148 | "SECTION: Load class files{{{2 149 | call nerdtree#loadClassFiles() 150 | 151 | " SECTION: Commands {{{1 152 | "============================================================ 153 | call nerdtree#ui_glue#setupCommands() 154 | 155 | " SECTION: Auto commands {{{1 156 | "============================================================ 157 | augroup NERDTree 158 | "Save the cursor position whenever we close the nerd tree 159 | exec "autocmd BufLeave ". g:NERDTreeCreator.BufNamePrefix() ."* if g:NERDTree.IsOpen() | call b:NERDTree.ui.saveScreenState() | endif" 160 | 161 | "disallow insert mode in the NERDTree 162 | exec "autocmd BufEnter ". g:NERDTreeCreator.BufNamePrefix() ."* stopinsert" 163 | augroup END 164 | 165 | if g:NERDTreeHijackNetrw 166 | augroup NERDTreeHijackNetrw 167 | autocmd VimEnter * silent! autocmd! FileExplorer 168 | au BufEnter,VimEnter * call nerdtree#checkForBrowse(expand("")) 169 | augroup END 170 | endif 171 | 172 | " SECTION: Public API {{{1 173 | "============================================================ 174 | function! NERDTreeAddMenuItem(options) 175 | call g:NERDTreeMenuItem.Create(a:options) 176 | endfunction 177 | 178 | function! NERDTreeAddMenuSeparator(...) 179 | let opts = a:0 ? a:1 : {} 180 | call g:NERDTreeMenuItem.CreateSeparator(opts) 181 | endfunction 182 | 183 | function! NERDTreeAddSubmenu(options) 184 | return g:NERDTreeMenuItem.Create(a:options) 185 | endfunction 186 | 187 | function! NERDTreeAddKeyMap(options) 188 | call g:NERDTreeKeyMap.Create(a:options) 189 | endfunction 190 | 191 | function! NERDTreeRender() 192 | call nerdtree#renderView() 193 | endfunction 194 | 195 | function! NERDTreeFocus() 196 | if g:NERDTree.IsOpen() 197 | call g:NERDTree.CursorToTreeWin() 198 | else 199 | call g:NERDTreeCreator.ToggleTabTree("") 200 | endif 201 | endfunction 202 | 203 | function! NERDTreeCWD() 204 | call NERDTreeFocus() 205 | call nerdtree#ui_glue#chRootCwd() 206 | endfunction 207 | 208 | function! NERDTreeAddPathFilter(callback) 209 | call g:NERDTree.AddPathFilter(a:callback) 210 | endfunction 211 | 212 | " SECTION: Post Source Actions {{{1 213 | call nerdtree#postSourceActions() 214 | 215 | "reset &cpo back to users setting 216 | let &cpo = s:old_cpo 217 | 218 | " vim: set sw=4 sts=4 et fdm=marker: 219 | -------------------------------------------------------------------------------- /lib/nerdtree/opener.vim: -------------------------------------------------------------------------------- 1 | "CLASS: Opener 2 | "============================================================ 3 | let s:Opener = {} 4 | let g:NERDTreeOpener = s:Opener 5 | 6 | "FUNCTION: s:Opener._bufInWindows(bnum){{{1 7 | "[[STOLEN FROM VTREEEXPLORER.VIM]] 8 | "Determine the number of windows open to this buffer number. 9 | "Care of Yegappan Lakshman. Thanks! 10 | " 11 | "Args: 12 | "bnum: the subject buffers buffer number 13 | function! s:Opener._bufInWindows(bnum) 14 | let cnt = 0 15 | let winnum = 1 16 | while 1 17 | let bufnum = winbufnr(winnum) 18 | if bufnum < 0 19 | break 20 | endif 21 | if bufnum ==# a:bnum 22 | let cnt = cnt + 1 23 | endif 24 | let winnum = winnum + 1 25 | endwhile 26 | 27 | return cnt 28 | endfunction 29 | "FUNCTION: Opener._checkToCloseTree(newtab) {{{1 30 | "Check the class options and global options (i.e. NERDTreeQuitOnOpen) to see 31 | "if the tree should be closed now. 32 | " 33 | "Args: 34 | "a:newtab - boolean. If set, only close the tree now if we are opening the 35 | "target in a new tab. This is needed because we have to close tree before we 36 | "leave the tab 37 | function! s:Opener._checkToCloseTree(newtab) 38 | if self._keepopen 39 | return 40 | endif 41 | 42 | if (a:newtab && self._where == 't') || !a:newtab 43 | call g:NERDTree.CloseIfQuitOnOpen() 44 | endif 45 | endfunction 46 | 47 | 48 | "FUNCTION: s:Opener._firstUsableWindow(){{{1 49 | "find the window number of the first normal window 50 | function! s:Opener._firstUsableWindow() 51 | let i = 1 52 | while i <= winnr("$") 53 | let bnum = winbufnr(i) 54 | if bnum != -1 && getbufvar(bnum, '&buftype') ==# '' 55 | \ && !getwinvar(i, '&previewwindow') 56 | \ && (!getbufvar(bnum, '&modified') || &hidden) 57 | return i 58 | endif 59 | 60 | let i += 1 61 | endwhile 62 | return -1 63 | endfunction 64 | 65 | "FUNCTION: Opener._gotoTargetWin() {{{1 66 | function! s:Opener._gotoTargetWin() 67 | if b:NERDTree.isWinTree() 68 | if self._where == 'v' 69 | vsplit 70 | elseif self._where == 'h' 71 | split 72 | elseif self._where == 't' 73 | tabnew 74 | endif 75 | else 76 | call self._checkToCloseTree(1) 77 | 78 | if self._where == 'v' 79 | call self._newVSplit() 80 | elseif self._where == 'h' 81 | call self._newSplit() 82 | elseif self._where == 't' 83 | tabnew 84 | elseif self._where == 'p' 85 | call self._previousWindow() 86 | endif 87 | 88 | call self._checkToCloseTree(0) 89 | endif 90 | endfunction 91 | 92 | "FUNCTION: s:Opener._isWindowUsable(winnumber) {{{1 93 | "Returns 0 if opening a file from the tree in the given window requires it to 94 | "be split, 1 otherwise 95 | " 96 | "Args: 97 | "winnumber: the number of the window in question 98 | function! s:Opener._isWindowUsable(winnumber) 99 | "gotta split if theres only one window (i.e. the NERD tree) 100 | if winnr("$") ==# 1 101 | return 0 102 | endif 103 | 104 | let oldwinnr = winnr() 105 | call nerdtree#exec(a:winnumber . "wincmd p") 106 | let specialWindow = getbufvar("%", '&buftype') != '' || getwinvar('%', '&previewwindow') 107 | let modified = &modified 108 | call nerdtree#exec(oldwinnr . "wincmd p") 109 | 110 | "if its a special window e.g. quickfix or another explorer plugin then we 111 | "have to split 112 | if specialWindow 113 | return 0 114 | endif 115 | 116 | if &hidden 117 | return 1 118 | endif 119 | 120 | return !modified || self._bufInWindows(winbufnr(a:winnumber)) >= 2 121 | endfunction 122 | 123 | "FUNCTION: Opener.New(path, opts) {{{1 124 | "Args: 125 | " 126 | "a:path: The path object that is to be opened. 127 | " 128 | "a:opts: 129 | " 130 | "A dictionary containing the following keys (all optional): 131 | " 'where': Specifies whether the node should be opened in new split/tab or in 132 | " the previous window. Can be either 'v' or 'h' or 't' (for open in 133 | " new tab) 134 | " 'reuse': if a window is displaying the file then jump the cursor there. Can 135 | " 'all', 'currenttab' or empty to not reuse. 136 | " 'keepopen': dont close the tree window 137 | " 'stay': open the file, but keep the cursor in the tree win 138 | function! s:Opener.New(path, opts) 139 | let newObj = copy(self) 140 | 141 | let newObj._path = a:path 142 | let newObj._stay = nerdtree#has_opt(a:opts, 'stay') 143 | 144 | if has_key(a:opts, 'reuse') 145 | let newObj._reuse = a:opts['reuse'] 146 | else 147 | let newObj._reuse = '' 148 | endif 149 | 150 | let newObj._keepopen = nerdtree#has_opt(a:opts, 'keepopen') 151 | let newObj._where = has_key(a:opts, 'where') ? a:opts['where'] : '' 152 | let newObj._nerdtree = b:NERDTree 153 | call newObj._saveCursorPos() 154 | 155 | return newObj 156 | endfunction 157 | 158 | "FUNCTION: Opener._newSplit() {{{1 159 | function! s:Opener._newSplit() 160 | " Save the user's settings for splitbelow and splitright 161 | let savesplitbelow=&splitbelow 162 | let savesplitright=&splitright 163 | 164 | " 'there' will be set to a command to move from the split window 165 | " back to the explorer window 166 | " 167 | " 'back' will be set to a command to move from the explorer window 168 | " back to the newly split window 169 | " 170 | " 'right' and 'below' will be set to the settings needed for 171 | " splitbelow and splitright IF the explorer is the only window. 172 | " 173 | let there= g:NERDTreeWinPos ==# "left" ? "wincmd h" : "wincmd l" 174 | let back = g:NERDTreeWinPos ==# "left" ? "wincmd l" : "wincmd h" 175 | let right= g:NERDTreeWinPos ==# "left" 176 | let below=0 177 | 178 | " Attempt to go to adjacent window 179 | call nerdtree#exec(back) 180 | 181 | let onlyOneWin = (winnr("$") ==# 1) 182 | 183 | " If no adjacent window, set splitright and splitbelow appropriately 184 | if onlyOneWin 185 | let &splitright=right 186 | let &splitbelow=below 187 | else 188 | " found adjacent window - invert split direction 189 | let &splitright=!right 190 | let &splitbelow=!below 191 | endif 192 | 193 | let splitMode = onlyOneWin ? "vertical" : "" 194 | 195 | " Open the new window 196 | try 197 | exec(splitMode." sp ") 198 | catch /^Vim\%((\a\+)\)\=:E37/ 199 | call g:NERDTree.CursorToTreeWin() 200 | throw "NERDTree.FileAlreadyOpenAndModifiedError: ". self._path.str() ." is already open and modified." 201 | catch /^Vim\%((\a\+)\)\=:/ 202 | "do nothing 203 | endtry 204 | 205 | "resize the tree window if no other window was open before 206 | if onlyOneWin 207 | let size = exists("b:NERDTreeOldWindowSize") ? b:NERDTreeOldWindowSize : g:NERDTreeWinSize 208 | call nerdtree#exec(there) 209 | exec("silent ". splitMode ." resize ". size) 210 | call nerdtree#exec('wincmd p') 211 | endif 212 | 213 | " Restore splitmode settings 214 | let &splitbelow=savesplitbelow 215 | let &splitright=savesplitright 216 | endfunction 217 | 218 | "FUNCTION: Opener._newVSplit() {{{1 219 | function! s:Opener._newVSplit() 220 | let winwidth = winwidth(".") 221 | if winnr("$")==#1 222 | let winwidth = g:NERDTreeWinSize 223 | endif 224 | 225 | call nerdtree#exec("wincmd p") 226 | vnew 227 | 228 | "resize the nerd tree back to the original size 229 | call g:NERDTree.CursorToTreeWin() 230 | exec("silent vertical resize ". winwidth) 231 | call nerdtree#exec('wincmd p') 232 | endfunction 233 | 234 | "FUNCTION: Opener.open(target) {{{1 235 | function! s:Opener.open(target) 236 | if self._path.isDirectory 237 | call self._openDirectory(a:target) 238 | else 239 | call self._openFile() 240 | endif 241 | endfunction 242 | 243 | "FUNCTION: Opener._openFile() {{{1 244 | function! s:Opener._openFile() 245 | if self._reuseWindow() 246 | return 247 | endif 248 | 249 | call self._gotoTargetWin() 250 | call self._path.edit() 251 | if self._stay 252 | call self._restoreCursorPos() 253 | endif 254 | endfunction 255 | 256 | "FUNCTION: Opener._openDirectory(node) {{{1 257 | function! s:Opener._openDirectory(node) 258 | if self._nerdtree.isWinTree() 259 | call self._gotoTargetWin() 260 | call g:NERDTreeCreator.CreateWindow(a:node.path.str()) 261 | else 262 | call self._gotoTargetWin() 263 | if empty(self._where) 264 | call b:NERDTree.changeRoot(a:node) 265 | elseif self._where == 't' 266 | call g:NERDTreeCreator.CreateTabTree(a:node.path.str()) 267 | else 268 | call g:NERDTreeCreator.CreateWindowTree(a:node.path.str()) 269 | endif 270 | endif 271 | 272 | if self._stay 273 | call self._restoreCursorPos() 274 | endif 275 | endfunction 276 | 277 | "FUNCTION: Opener._previousWindow() {{{1 278 | function! s:Opener._previousWindow() 279 | if !self._isWindowUsable(winnr("#")) && self._firstUsableWindow() ==# -1 280 | call self._newSplit() 281 | else 282 | try 283 | if !self._isWindowUsable(winnr("#")) 284 | call nerdtree#exec(self._firstUsableWindow() . "wincmd w") 285 | else 286 | call nerdtree#exec('wincmd p') 287 | endif 288 | catch /^Vim\%((\a\+)\)\=:E37/ 289 | call g:NERDTree.CursorToTreeWin() 290 | throw "NERDTree.FileAlreadyOpenAndModifiedError: ". self._path.str() ." is already open and modified." 291 | catch /^Vim\%((\a\+)\)\=:/ 292 | echo v:exception 293 | endtry 294 | endif 295 | endfunction 296 | 297 | "FUNCTION: Opener._restoreCursorPos(){{{1 298 | function! s:Opener._restoreCursorPos() 299 | call nerdtree#exec('normal ' . self._tabnr . 'gt') 300 | call nerdtree#exec(bufwinnr(self._bufnr) . 'wincmd w') 301 | endfunction 302 | 303 | "FUNCTION: Opener._reuseWindow(){{{1 304 | "put the cursor in the first window we find for this file 305 | " 306 | "return 1 if we were successful 307 | function! s:Opener._reuseWindow() 308 | if empty(self._reuse) 309 | return 0 310 | endif 311 | 312 | "check the current tab for the window 313 | let winnr = bufwinnr('^' . self._path.str() . '$') 314 | if winnr != -1 315 | call nerdtree#exec(winnr . "wincmd w") 316 | call self._checkToCloseTree(0) 317 | return 1 318 | endif 319 | 320 | if self._reuse == 'currenttab' 321 | return 0 322 | endif 323 | 324 | "check other tabs 325 | let tabnr = self._path.tabnr() 326 | if tabnr 327 | call self._checkToCloseTree(1) 328 | call nerdtree#exec('normal! ' . tabnr . 'gt') 329 | let winnr = bufwinnr('^' . self._path.str() . '$') 330 | call nerdtree#exec(winnr . "wincmd w") 331 | return 1 332 | endif 333 | 334 | return 0 335 | endfunction 336 | 337 | "FUNCTION: Opener._saveCursorPos(){{{1 338 | function! s:Opener._saveCursorPos() 339 | let self._bufnr = bufnr("") 340 | let self._tabnr = tabpagenr() 341 | endfunction 342 | 343 | " vim: set sw=4 sts=4 et fdm=marker: 344 | -------------------------------------------------------------------------------- /lib/nerdtree/bookmark.vim: -------------------------------------------------------------------------------- 1 | "CLASS: Bookmark 2 | "============================================================ 3 | let s:Bookmark = {} 4 | let g:NERDTreeBookmark = s:Bookmark 5 | 6 | " FUNCTION: Bookmark.activate(nerdtree) {{{1 7 | function! s:Bookmark.activate(nerdtree, ...) 8 | call self.open(a:nerdtree, a:0 ? a:1 : {}) 9 | endfunction 10 | 11 | " FUNCTION: Bookmark.AddBookmark(name, path) {{{1 12 | " Class method to add a new bookmark to the list, if a previous bookmark exists 13 | " with the same name, just update the path for that bookmark 14 | function! s:Bookmark.AddBookmark(name, path) 15 | for i in s:Bookmark.Bookmarks() 16 | if i.name ==# a:name 17 | let i.path = a:path 18 | return 19 | endif 20 | endfor 21 | call add(s:Bookmark.Bookmarks(), s:Bookmark.New(a:name, a:path)) 22 | if g:NERDTreeBookmarksSort ==# 1 23 | call s:Bookmark.Sort() 24 | endif 25 | endfunction 26 | 27 | " FUNCTION: Bookmark.Bookmarks() {{{1 28 | " Class method to get all bookmarks. Lazily initializes the bookmarks global 29 | " variable 30 | function! s:Bookmark.Bookmarks() 31 | if !exists("g:NERDTreeBookmarks") 32 | let g:NERDTreeBookmarks = [] 33 | endif 34 | return g:NERDTreeBookmarks 35 | endfunction 36 | 37 | " FUNCTION: Bookmark.BookmarkExistsFor(name) {{{1 38 | " class method that returns 1 if a bookmark with the given name is found, 0 39 | " otherwise 40 | function! s:Bookmark.BookmarkExistsFor(name) 41 | try 42 | call s:Bookmark.BookmarkFor(a:name) 43 | return 1 44 | catch /^NERDTree.BookmarkNotFoundError/ 45 | return 0 46 | endtry 47 | endfunction 48 | 49 | " FUNCTION: Bookmark.BookmarkFor(name) {{{1 50 | " Class method to get the bookmark that has the given name. {} is return if no 51 | " bookmark is found 52 | function! s:Bookmark.BookmarkFor(name) 53 | for i in s:Bookmark.Bookmarks() 54 | if i.name ==# a:name 55 | return i 56 | endif 57 | endfor 58 | throw "NERDTree.BookmarkNotFoundError: no bookmark found for name: \"". a:name .'"' 59 | endfunction 60 | 61 | " FUNCTION: Bookmark.BookmarkNames() {{{1 62 | " Class method to return an array of all bookmark names 63 | function! s:Bookmark.BookmarkNames() 64 | let names = [] 65 | for i in s:Bookmark.Bookmarks() 66 | call add(names, i.name) 67 | endfor 68 | return names 69 | endfunction 70 | 71 | " FUNCTION: Bookmark.CacheBookmarks(silent) {{{1 72 | " Class method to read all bookmarks from the bookmarks file initialize 73 | " bookmark objects for each one. 74 | " 75 | " Args: 76 | " silent - dont echo an error msg if invalid bookmarks are found 77 | function! s:Bookmark.CacheBookmarks(silent) 78 | if filereadable(g:NERDTreeBookmarksFile) 79 | let g:NERDTreeBookmarks = [] 80 | let g:NERDTreeInvalidBookmarks = [] 81 | let bookmarkStrings = readfile(g:NERDTreeBookmarksFile) 82 | let invalidBookmarksFound = 0 83 | for i in bookmarkStrings 84 | 85 | "ignore blank lines 86 | if i != '' 87 | 88 | let name = substitute(i, '^\(.\{-}\) .*$', '\1', '') 89 | let path = substitute(i, '^.\{-} \(.*\)$', '\1', '') 90 | let path = fnamemodify(path, ':p') 91 | 92 | try 93 | let bookmark = s:Bookmark.New(name, g:NERDTreePath.New(path)) 94 | call add(g:NERDTreeBookmarks, bookmark) 95 | catch /^NERDTree.InvalidArgumentsError/ 96 | call add(g:NERDTreeInvalidBookmarks, i) 97 | let invalidBookmarksFound += 1 98 | endtry 99 | endif 100 | endfor 101 | if invalidBookmarksFound 102 | call s:Bookmark.Write() 103 | if !a:silent 104 | call nerdtree#echo(invalidBookmarksFound . " invalid bookmarks were read. See :help NERDTreeInvalidBookmarks for info.") 105 | endif 106 | endif 107 | if g:NERDTreeBookmarksSort ==# 1 108 | call s:Bookmark.Sort() 109 | endif 110 | endif 111 | endfunction 112 | 113 | " FUNCTION: Bookmark.compareTo(otherbookmark) {{{1 114 | " Compare these two bookmarks for sorting purposes 115 | function! s:Bookmark.compareTo(otherbookmark) 116 | return a:otherbookmark.name < self.name 117 | endfunction 118 | " FUNCTION: Bookmark.ClearAll() {{{1 119 | " Class method to delete all bookmarks. 120 | function! s:Bookmark.ClearAll() 121 | for i in s:Bookmark.Bookmarks() 122 | call i.delete() 123 | endfor 124 | call s:Bookmark.Write() 125 | endfunction 126 | 127 | " FUNCTION: Bookmark.delete() {{{1 128 | " Delete this bookmark. If the node for this bookmark is under the current 129 | " root, then recache bookmarks for its Path object 130 | function! s:Bookmark.delete() 131 | call remove(s:Bookmark.Bookmarks(), index(s:Bookmark.Bookmarks(), self)) 132 | call s:Bookmark.Write() 133 | endfunction 134 | 135 | " FUNCTION: Bookmark.getNode(nerdtree, searchFromAbsoluteRoot) {{{1 136 | " Gets the treenode for this bookmark 137 | " 138 | " Args: 139 | " searchFromAbsoluteRoot: specifies whether we should search from the current 140 | " tree root, or the highest cached node 141 | function! s:Bookmark.getNode(nerdtree, searchFromAbsoluteRoot) 142 | let searchRoot = a:searchFromAbsoluteRoot ? a:nerdtree.root.AbsoluteTreeRoot() : a:nerdtree.root 143 | let targetNode = searchRoot.findNode(self.path) 144 | if empty(targetNode) 145 | throw "NERDTree.BookmarkedNodeNotFoundError: no node was found for bookmark: " . self.name 146 | endif 147 | return targetNode 148 | endfunction 149 | 150 | " FUNCTION: Bookmark.GetNodeForName(name, searchFromAbsoluteRoot, nerdtree) {{{1 151 | " Class method that finds the bookmark with the given name and returns the 152 | " treenode for it. 153 | function! s:Bookmark.GetNodeForName(name, searchFromAbsoluteRoot, nerdtree) 154 | let bookmark = s:Bookmark.BookmarkFor(a:name) 155 | return bookmark.getNode(nerdtree, a:searchFromAbsoluteRoot) 156 | endfunction 157 | 158 | " FUNCTION: Bookmark.GetSelected() {{{1 159 | " returns the Bookmark the cursor is over, or {} 160 | function! s:Bookmark.GetSelected() 161 | let line = getline(".") 162 | let name = substitute(line, '^>\(.\{-}\) .\+$', '\1', '') 163 | if name != line 164 | try 165 | return s:Bookmark.BookmarkFor(name) 166 | catch /^NERDTree.BookmarkNotFoundError/ 167 | return {} 168 | endtry 169 | endif 170 | return {} 171 | endfunction 172 | 173 | " FUNCTION: Bookmark.InvalidBookmarks() {{{1 174 | " Class method to get all invalid bookmark strings read from the bookmarks 175 | " file 176 | function! s:Bookmark.InvalidBookmarks() 177 | if !exists("g:NERDTreeInvalidBookmarks") 178 | let g:NERDTreeInvalidBookmarks = [] 179 | endif 180 | return g:NERDTreeInvalidBookmarks 181 | endfunction 182 | 183 | " FUNCTION: Bookmark.mustExist() {{{1 184 | function! s:Bookmark.mustExist() 185 | if !self.path.exists() 186 | call s:Bookmark.CacheBookmarks(1) 187 | throw "NERDTree.BookmarkPointsToInvalidLocationError: the bookmark \"". 188 | \ self.name ."\" points to a non existing location: \"". self.path.str() 189 | endif 190 | endfunction 191 | 192 | " FUNCTION: Bookmark.New(name, path) {{{1 193 | " Create a new bookmark object with the given name and path object 194 | function! s:Bookmark.New(name, path) 195 | if a:name =~# ' ' 196 | throw "NERDTree.IllegalBookmarkNameError: illegal name:" . a:name 197 | endif 198 | 199 | let newBookmark = copy(self) 200 | let newBookmark.name = a:name 201 | let newBookmark.path = a:path 202 | return newBookmark 203 | endfunction 204 | 205 | " FUNCTION: Bookmark.open(nerdtree, [options]) {{{1 206 | "Args: 207 | " 208 | "nerdtree: the tree to load open the bookmark in 209 | " 210 | "A dictionary containing the following keys (all optional): 211 | " 'where': Specifies whether the node should be opened in new split/tab or in 212 | " the previous window. Can be either 'v' (vertical split), 'h' 213 | " (horizontal split), 't' (new tab) or 'p' (previous window). 214 | " 'reuse': if a window is displaying the file then jump the cursor there 215 | " 'keepopen': dont close the tree window 216 | " 'stay': open the file, but keep the cursor in the tree win 217 | " 218 | function! s:Bookmark.open(nerdtree, ...) 219 | let opts = a:0 ? a:1 : {} 220 | 221 | if self.path.isDirectory && !has_key(opts, 'where') 222 | call self.toRoot(a:nerdtree) 223 | else 224 | let opener = g:NERDTreeOpener.New(self.path, opts) 225 | call opener.open(self) 226 | endif 227 | endfunction 228 | 229 | " FUNCTION: Bookmark.openInNewTab(options) {{{1 230 | " Create a new bookmark object with the given name and path object 231 | function! s:Bookmark.openInNewTab(options) 232 | call nerdtree#deprecated('Bookmark.openInNewTab', 'is deprecated, use open() instead') 233 | call self.open(a:options) 234 | endfunction 235 | 236 | " FUNCTION: Bookmark.setPath(path) {{{1 237 | " makes this bookmark point to the given path 238 | function! s:Bookmark.setPath(path) 239 | let self.path = a:path 240 | endfunction 241 | 242 | " FUNCTION: Bookmark.Sort() {{{1 243 | " Class method that sorts all bookmarks 244 | function! s:Bookmark.Sort() 245 | let CompareFunc = function("nerdtree#compareBookmarks") 246 | call sort(s:Bookmark.Bookmarks(), CompareFunc) 247 | endfunction 248 | 249 | " FUNCTION: Bookmark.str() {{{1 250 | " Get the string that should be rendered in the view for this bookmark 251 | function! s:Bookmark.str() 252 | let pathStrMaxLen = winwidth(g:NERDTree.GetWinNum()) - 4 - len(self.name) 253 | if &nu 254 | let pathStrMaxLen = pathStrMaxLen - &numberwidth 255 | endif 256 | 257 | let pathStr = self.path.str({'format': 'UI'}) 258 | if len(pathStr) > pathStrMaxLen 259 | let pathStr = '<' . strpart(pathStr, len(pathStr) - pathStrMaxLen) 260 | endif 261 | return '>' . self.name . ' ' . pathStr 262 | endfunction 263 | 264 | " FUNCTION: Bookmark.toRoot(nerdtree) {{{1 265 | " Make the node for this bookmark the new tree root 266 | function! s:Bookmark.toRoot(nerdtree) 267 | if self.validate() 268 | try 269 | let targetNode = self.getNode(a:nerdtree, 1) 270 | catch /^NERDTree.BookmarkedNodeNotFoundError/ 271 | let targetNode = g:NERDTreeFileNode.New(s:Bookmark.BookmarkFor(self.name).path, a:nerdtree) 272 | endtry 273 | call a:nerdtree.changeRoot(targetNode) 274 | endif 275 | endfunction 276 | 277 | " FUNCTION: Bookmark.ToRoot(name, nerdtree) {{{1 278 | " Make the node for this bookmark the new tree root 279 | function! s:Bookmark.ToRoot(name, nerdtree) 280 | let bookmark = s:Bookmark.BookmarkFor(a:name) 281 | call bookmark.toRoot(a:nerdtree) 282 | endfunction 283 | 284 | " FUNCTION: Bookmark.validate() {{{1 285 | function! s:Bookmark.validate() 286 | if self.path.exists() 287 | return 1 288 | else 289 | call s:Bookmark.CacheBookmarks(1) 290 | call nerdtree#echo(self.name . "now points to an invalid location. See :help NERDTreeInvalidBookmarks for info.") 291 | return 0 292 | endif 293 | endfunction 294 | 295 | " FUNCTION: Bookmark.Write() {{{1 296 | " Class method to write all bookmarks to the bookmarks file 297 | function! s:Bookmark.Write() 298 | let bookmarkStrings = [] 299 | for i in s:Bookmark.Bookmarks() 300 | call add(bookmarkStrings, i.name . ' ' . fnamemodify(i.path.str(), ':~')) 301 | endfor 302 | 303 | "add a blank line before the invalid ones 304 | call add(bookmarkStrings, "") 305 | 306 | for j in s:Bookmark.InvalidBookmarks() 307 | call add(bookmarkStrings, j) 308 | endfor 309 | call writefile(bookmarkStrings, g:NERDTreeBookmarksFile) 310 | endfunction 311 | 312 | " vim: set sw=4 sts=4 et fdm=marker: 313 | -------------------------------------------------------------------------------- /nerdtree_plugin/fs_menu.vim: -------------------------------------------------------------------------------- 1 | " ============================================================================ 2 | " File: fs_menu.vim 3 | " Description: plugin for the NERD Tree that provides a file system menu 4 | " Maintainer: Martin Grenfell 5 | " License: This program is free software. It comes without any warranty, 6 | " to the extent permitted by applicable law. You can redistribute 7 | " it and/or modify it under the terms of the Do What The Fuck You 8 | " Want To Public License, Version 2, as published by Sam Hocevar. 9 | " See http://sam.zoy.org/wtfpl/COPYING for more details. 10 | " 11 | " ============================================================================ 12 | if exists("g:loaded_nerdtree_fs_menu") 13 | finish 14 | endif 15 | let g:loaded_nerdtree_fs_menu = 1 16 | 17 | "Automatically delete the buffer after deleting or renaming a file 18 | if !exists("g:NERDTreeAutoDeleteBuffer") 19 | let g:NERDTreeAutoDeleteBuffer = 0 20 | endif 21 | 22 | call NERDTreeAddMenuItem({'text': '(a)dd a childnode', 'shortcut': 'a', 'callback': 'NERDTreeAddNode'}) 23 | call NERDTreeAddMenuItem({'text': '(m)ove the current node', 'shortcut': 'm', 'callback': 'NERDTreeMoveNode'}) 24 | call NERDTreeAddMenuItem({'text': '(d)elete the current node', 'shortcut': 'd', 'callback': 'NERDTreeDeleteNode'}) 25 | 26 | if has("gui_mac") || has("gui_macvim") || has("mac") 27 | call NERDTreeAddMenuItem({'text': '(r)eveal in Finder the current node', 'shortcut': 'r', 'callback': 'NERDTreeRevealInFinder'}) 28 | call NERDTreeAddMenuItem({'text': '(o)pen the current node with system editor', 'shortcut': 'o', 'callback': 'NERDTreeExecuteFile'}) 29 | call NERDTreeAddMenuItem({'text': '(q)uicklook the current node', 'shortcut': 'q', 'callback': 'NERDTreeQuickLook'}) 30 | endif 31 | 32 | if g:NERDTreePath.CopyingSupported() 33 | call NERDTreeAddMenuItem({'text': '(c)opy the current node', 'shortcut': 'c', 'callback': 'NERDTreeCopyNode'}) 34 | endif 35 | 36 | if has("unix") || has("osx") 37 | call NERDTreeAddMenuItem({'text': '(l)ist the current node', 'shortcut': 'l', 'callback': 'NERDTreeListNode'}) 38 | else 39 | call NERDTreeAddMenuItem({'text': '(l)ist the current node', 'shortcut': 'l', 'callback': 'NERDTreeListNodeWin32'}) 40 | endif 41 | 42 | "FUNCTION: s:promptToDelBuffer(bufnum, msg){{{1 43 | "prints out the given msg and, if the user responds by pushing 'y' then the 44 | "buffer with the given bufnum is deleted 45 | " 46 | "Args: 47 | "bufnum: the buffer that may be deleted 48 | "msg: a message that will be echoed to the user asking them if they wish to 49 | " del the buffer 50 | function! s:promptToDelBuffer(bufnum, msg) 51 | echo a:msg 52 | if g:NERDTreeAutoDeleteBuffer || nr2char(getchar()) ==# 'y' 53 | " 1. ensure that all windows which display the just deleted filename 54 | " now display an empty buffer (so a layout is preserved). 55 | " Is not it better to close single tabs with this file only ? 56 | let s:originalTabNumber = tabpagenr() 57 | let s:originalWindowNumber = winnr() 58 | exec "tabdo windo if winbufnr(0) == " . a:bufnum . " | exec ':enew! ' | endif" 59 | exec "tabnext " . s:originalTabNumber 60 | exec s:originalWindowNumber . "wincmd w" 61 | " 3. We don't need a previous buffer anymore 62 | exec "bwipeout! " . a:bufnum 63 | endif 64 | endfunction 65 | 66 | "FUNCTION: s:promptToRenameBuffer(bufnum, msg){{{1 67 | "prints out the given msg and, if the user responds by pushing 'y' then the 68 | "buffer with the given bufnum is replaced with a new one 69 | " 70 | "Args: 71 | "bufnum: the buffer that may be deleted 72 | "msg: a message that will be echoed to the user asking them if they wish to 73 | " del the buffer 74 | function! s:promptToRenameBuffer(bufnum, msg, newFileName) 75 | echo a:msg 76 | if g:NERDTreeAutoDeleteBuffer || nr2char(getchar()) ==# 'y' 77 | let quotedFileName = "'" . a:newFileName . "'" 78 | " 1. ensure that a new buffer is loaded 79 | exec "badd " . quotedFileName 80 | " 2. ensure that all windows which display the just deleted filename 81 | " display a buffer for a new filename. 82 | let s:originalTabNumber = tabpagenr() 83 | let s:originalWindowNumber = winnr() 84 | let editStr = g:NERDTreePath.New(a:newFileName).str({'format': 'Edit'}) 85 | exec "tabdo windo if winbufnr(0) == " . a:bufnum . " | exec ':e! " . editStr . "' | endif" 86 | exec "tabnext " . s:originalTabNumber 87 | exec s:originalWindowNumber . "wincmd w" 88 | " 3. We don't need a previous buffer anymore 89 | exec "bwipeout! " . a:bufnum 90 | endif 91 | endfunction 92 | "FUNCTION: NERDTreeAddNode(){{{1 93 | function! NERDTreeAddNode() 94 | let curDirNode = g:NERDTreeDirNode.GetSelected() 95 | 96 | let newNodeName = input("Add a childnode\n". 97 | \ "==========================================================\n". 98 | \ "Enter the dir/file name to be created. Dirs end with a '/'\n" . 99 | \ "", curDirNode.path.str() . g:NERDTreePath.Slash(), "file") 100 | 101 | if newNodeName ==# '' 102 | call nerdtree#echo("Node Creation Aborted.") 103 | return 104 | endif 105 | 106 | try 107 | let newPath = g:NERDTreePath.Create(newNodeName) 108 | let parentNode = b:NERDTree.root.findNode(newPath.getParent()) 109 | 110 | let newTreeNode = g:NERDTreeFileNode.New(newPath, b:NERDTree) 111 | if empty(parentNode) 112 | call b:NERDTree.root.refresh() 113 | call b:NERDTree.render() 114 | elseif parentNode.isOpen || !empty(parentNode.children) 115 | call parentNode.addChild(newTreeNode, 1) 116 | call NERDTreeRender() 117 | call newTreeNode.putCursorHere(1, 0) 118 | endif 119 | catch /^NERDTree/ 120 | call nerdtree#echoWarning("Node Not Created.") 121 | endtry 122 | endfunction 123 | 124 | "FUNCTION: NERDTreeMoveNode(){{{1 125 | function! NERDTreeMoveNode() 126 | let curNode = g:NERDTreeFileNode.GetSelected() 127 | let newNodePath = input("Rename the current node\n" . 128 | \ "==========================================================\n" . 129 | \ "Enter the new path for the node: \n" . 130 | \ "", curNode.path.str(), "file") 131 | 132 | if newNodePath ==# '' 133 | call nerdtree#echo("Node Renaming Aborted.") 134 | return 135 | endif 136 | 137 | try 138 | let bufnum = bufnr("^".curNode.path.str()."$") 139 | 140 | call curNode.rename(newNodePath) 141 | call NERDTreeRender() 142 | 143 | "if the node is open in a buffer, ask the user if they want to 144 | "close that buffer 145 | if bufnum != -1 146 | let prompt = "\nNode renamed.\n\nThe old file is open in buffer ". bufnum . (bufwinnr(bufnum) ==# -1 ? " (hidden)" : "") .". Replace this buffer with a new file? (yN)" 147 | call s:promptToRenameBuffer(bufnum, prompt, newNodePath) 148 | endif 149 | 150 | call curNode.putCursorHere(1, 0) 151 | 152 | redraw 153 | catch /^NERDTree/ 154 | call nerdtree#echoWarning("Node Not Renamed.") 155 | endtry 156 | endfunction 157 | 158 | " FUNCTION: NERDTreeDeleteNode() {{{1 159 | function! NERDTreeDeleteNode() 160 | let currentNode = g:NERDTreeFileNode.GetSelected() 161 | let confirmed = 0 162 | 163 | if currentNode.path.isDirectory 164 | let choice =input("Delete the current node\n" . 165 | \ "==========================================================\n" . 166 | \ "STOP! To delete this entire directory, type 'yes'\n" . 167 | \ "" . currentNode.path.str() . ": ") 168 | let confirmed = choice ==# 'yes' 169 | else 170 | echo "Delete the current node\n" . 171 | \ "==========================================================\n". 172 | \ "Are you sure you wish to delete the node:\n" . 173 | \ "" . currentNode.path.str() . " (yN):" 174 | let choice = nr2char(getchar()) 175 | let confirmed = choice ==# 'y' 176 | endif 177 | 178 | 179 | if confirmed 180 | try 181 | call currentNode.delete() 182 | call NERDTreeRender() 183 | 184 | "if the node is open in a buffer, ask the user if they want to 185 | "close that buffer 186 | let bufnum = bufnr("^".currentNode.path.str()."$") 187 | if buflisted(bufnum) 188 | let prompt = "\nNode deleted.\n\nThe file is open in buffer ". bufnum . (bufwinnr(bufnum) ==# -1 ? " (hidden)" : "") .". Delete this buffer? (yN)" 189 | call s:promptToDelBuffer(bufnum, prompt) 190 | endif 191 | 192 | redraw 193 | catch /^NERDTree/ 194 | call nerdtree#echoWarning("Could not remove node") 195 | endtry 196 | else 197 | call nerdtree#echo("delete aborted") 198 | endif 199 | 200 | endfunction 201 | 202 | " FUNCTION: NERDTreeListNode() {{{1 203 | function! NERDTreeListNode() 204 | let treenode = g:NERDTreeFileNode.GetSelected() 205 | if treenode != {} 206 | let metadata = split(system('ls -ld ' . shellescape(treenode.path.str())), '\n') 207 | call nerdtree#echo(metadata[0]) 208 | else 209 | call nerdtree#echo("No information avaialable") 210 | endif 211 | endfunction 212 | 213 | " FUNCTION: NERDTreeListNodeWin32() {{{1 214 | function! NERDTreeListNodeWin32() 215 | let treenode = g:NERDTreeFileNode.GetSelected() 216 | if treenode != {} 217 | let metadata = split(system('DIR /Q ' . shellescape(treenode.path.str()) . ' | FINDSTR "^[012][0-9]/[0-3][0-9]/[12][0-9][0-9][0-9]"'), '\n') 218 | call nerdtree#echo(metadata[0]) 219 | else 220 | call nerdtree#echo("No information avaialable") 221 | endif 222 | 223 | endfunction 224 | 225 | " FUNCTION: NERDTreeCopyNode() {{{1 226 | function! NERDTreeCopyNode() 227 | let currentNode = g:NERDTreeFileNode.GetSelected() 228 | let newNodePath = input("Copy the current node\n" . 229 | \ "==========================================================\n" . 230 | \ "Enter the new path to copy the node to: \n" . 231 | \ "", currentNode.path.str(), "file") 232 | 233 | if newNodePath != "" 234 | "strip trailing slash 235 | let newNodePath = substitute(newNodePath, '\/$', '', '') 236 | 237 | let confirmed = 1 238 | if currentNode.path.copyingWillOverwrite(newNodePath) 239 | call nerdtree#echo("Warning: copying may overwrite files! Continue? (yN)") 240 | let choice = nr2char(getchar()) 241 | let confirmed = choice ==# 'y' 242 | endif 243 | 244 | if confirmed 245 | try 246 | let newNode = currentNode.copy(newNodePath) 247 | if empty(newNode) 248 | call b:NERDTree.root.refresh() 249 | call b:NERDTree.render() 250 | else 251 | call NERDTreeRender() 252 | call newNode.putCursorHere(0, 0) 253 | endif 254 | catch /^NERDTree/ 255 | call nerdtree#echoWarning("Could not copy node") 256 | endtry 257 | endif 258 | else 259 | call nerdtree#echo("Copy aborted.") 260 | endif 261 | redraw 262 | endfunction 263 | 264 | " FUNCTION: NERDTreeQuickLook() {{{1 265 | function! NERDTreeQuickLook() 266 | let treenode = g:NERDTreeFileNode.GetSelected() 267 | if treenode != {} 268 | call system("qlmanage -p 2>/dev/null '" . treenode.path.str() . "'") 269 | endif 270 | endfunction 271 | 272 | " FUNCTION: NERDTreeRevealInFinder() {{{1 273 | function! NERDTreeRevealInFinder() 274 | let treenode = g:NERDTreeFileNode.GetSelected() 275 | if treenode != {} 276 | call system("open -R '" . treenode.path.str() . "'") 277 | endif 278 | endfunction 279 | 280 | " FUNCTION: NERDTreeExecuteFile() {{{1 281 | function! NERDTreeExecuteFile() 282 | let treenode = g:NERDTreeFileNode.GetSelected() 283 | if treenode != {} 284 | call system("open '" . treenode.path.str() . "'") 285 | endif 286 | endfunction 287 | " vim: set sw=4 sts=4 et fdm=marker: 288 | -------------------------------------------------------------------------------- /lib/nerdtree/creator.vim: -------------------------------------------------------------------------------- 1 | "CLASS: Creator 2 | "Creates tab/window/mirror nerdtree windows. Sets up all the window and 3 | "buffer options and key mappings etc. 4 | "============================================================ 5 | let s:Creator = {} 6 | let g:NERDTreeCreator = s:Creator 7 | 8 | "FUNCTION: s:Creator._bindMappings() {{{1 9 | function! s:Creator._bindMappings() 10 | "make do the same as the activate node mapping 11 | nnoremap :call nerdtree#ui_glue#invokeKeyMap(g:NERDTreeMapActivateNode) 12 | 13 | call g:NERDTreeKeyMap.BindAll() 14 | 15 | command! -buffer -nargs=? Bookmark :call nerdtree#ui_glue#bookmarkNode('') 16 | command! -buffer -complete=customlist,nerdtree#completeBookmarks -nargs=1 RevealBookmark :call nerdtree#ui_glue#revealBookmark('') 17 | command! -buffer -complete=customlist,nerdtree#completeBookmarks -nargs=1 OpenBookmark :call nerdtree#ui_glue#openBookmark('') 18 | command! -buffer -complete=customlist,nerdtree#completeBookmarks -nargs=* ClearBookmarks call nerdtree#ui_glue#clearBookmarks('') 19 | command! -buffer -complete=customlist,nerdtree#completeBookmarks -nargs=+ BookmarkToRoot call g:NERDTreeBookmark.ToRoot('', b:NERDTree) 20 | command! -buffer -nargs=0 ClearAllBookmarks call g:NERDTreeBookmark.ClearAll() call b:NERDTree.render() 21 | command! -buffer -nargs=0 ReadBookmarks call g:NERDTreeBookmark.CacheBookmarks(0) call b:NERDTree.render() 22 | command! -buffer -nargs=0 WriteBookmarks call g:NERDTreeBookmark.Write() 23 | endfunction 24 | 25 | "FUNCTION: s:Creator._broadcastInitEvent() {{{1 26 | function! s:Creator._broadcastInitEvent() 27 | silent doautocmd User NERDTreeInit 28 | endfunction 29 | 30 | " FUNCTION: s:Creator.BufNamePrefix() {{{2 31 | function! s:Creator.BufNamePrefix() 32 | return 'NERD_tree_' 33 | endfunction 34 | 35 | "FUNCTION: s:Creator.CreateTabTree(a:name) {{{1 36 | function! s:Creator.CreateTabTree(name) 37 | let creator = s:Creator.New() 38 | call creator.createTabTree(a:name) 39 | endfunction 40 | 41 | "FUNCTION: s:Creator.createTabTree(a:name) {{{1 42 | "name: the name of a bookmark or a directory 43 | function! s:Creator.createTabTree(name) 44 | let path = self._pathForString(a:name) 45 | 46 | "abort if exception was thrown (bookmark/dir doesn't exist) 47 | if empty(path) 48 | return 49 | endif 50 | 51 | if path == {} 52 | return 53 | endif 54 | 55 | "if instructed to, then change the vim CWD to the dir the NERDTree is 56 | "inited in 57 | if g:NERDTreeChDirMode != 0 58 | call path.changeToDir() 59 | endif 60 | 61 | if g:NERDTree.ExistsForTab() 62 | if g:NERDTree.IsOpen() 63 | call g:NERDTree.Close() 64 | endif 65 | 66 | call self._removeTreeBufForTab() 67 | endif 68 | 69 | call self._createTreeWin() 70 | call self._createNERDTree(path, "tab") 71 | call b:NERDTree.render() 72 | call b:NERDTree.root.putCursorHere(0, 0) 73 | 74 | call self._broadcastInitEvent() 75 | endfunction 76 | 77 | "FUNCTION: s:Creator.CreateWindowTree(dir) {{{1 78 | function! s:Creator.CreateWindowTree(dir) 79 | let creator = s:Creator.New() 80 | call creator.createWindowTree(a:dir) 81 | endfunction 82 | 83 | "FUNCTION: s:Creator.createWindowTree(dir) {{{1 84 | function! s:Creator.createWindowTree(dir) 85 | try 86 | let path = g:NERDTreePath.New(a:dir) 87 | catch /^NERDTree.InvalidArgumentsError/ 88 | call nerdtree#echo("Invalid directory name:" . a:name) 89 | return 90 | endtry 91 | 92 | "we want the directory buffer to disappear when we do the :edit below 93 | setlocal bufhidden=wipe 94 | 95 | let previousBuf = expand("#") 96 | 97 | "we need a unique name for each window tree buffer to ensure they are 98 | "all independent 99 | exec g:NERDTreeCreatePrefix . " edit " . self._nextBufferName() 100 | 101 | call self._createNERDTree(path, "window") 102 | let b:NERDTree._previousBuf = bufnr(previousBuf) 103 | call self._setCommonBufOptions() 104 | 105 | call b:NERDTree.render() 106 | 107 | call self._broadcastInitEvent() 108 | endfunction 109 | 110 | " FUNCTION: s:Creator._createNERDTree(path) {{{1 111 | function! s:Creator._createNERDTree(path, type) 112 | let b:NERDTree = g:NERDTree.New(a:path, a:type) 113 | "TODO: This is kept for compatability only since many things use 114 | "b:NERDTreeRoot instead of the new NERDTree.root 115 | "Remove this one day 116 | let b:NERDTreeRoot = b:NERDTree.root 117 | 118 | call b:NERDTree.root.open() 119 | endfunction 120 | 121 | " FUNCTION: s:Creator.CreateMirror() {{{1 122 | function! s:Creator.CreateMirror() 123 | let creator = s:Creator.New() 124 | call creator.createMirror() 125 | endfunction 126 | 127 | " FUNCTION: s:Creator.createMirror() {{{1 128 | function! s:Creator.createMirror() 129 | "get the names off all the nerd tree buffers 130 | let treeBufNames = [] 131 | for i in range(1, tabpagenr("$")) 132 | let nextName = self._tabpagevar(i, 'NERDTreeBufName') 133 | if nextName != -1 && (!exists("t:NERDTreeBufName") || nextName != t:NERDTreeBufName) 134 | call add(treeBufNames, nextName) 135 | endif 136 | endfor 137 | let treeBufNames = self._uniq(treeBufNames) 138 | 139 | "map the option names (that the user will be prompted with) to the nerd 140 | "tree buffer names 141 | let options = {} 142 | let i = 0 143 | while i < len(treeBufNames) 144 | let bufName = treeBufNames[i] 145 | let treeRoot = getbufvar(bufName, "NERDTree").root 146 | let options[i+1 . '. ' . treeRoot.path.str() . ' (buf name: ' . bufName . ')'] = bufName 147 | let i = i + 1 148 | endwhile 149 | 150 | "work out which tree to mirror, if there is more than 1 then ask the user 151 | let bufferName = '' 152 | if len(keys(options)) > 1 153 | let choices = ["Choose a tree to mirror"] 154 | let choices = extend(choices, sort(keys(options))) 155 | let choice = inputlist(choices) 156 | if choice < 1 || choice > len(options) || choice ==# '' 157 | return 158 | endif 159 | 160 | let bufferName = options[sort(keys(options))[choice-1]] 161 | elseif len(keys(options)) ==# 1 162 | let bufferName = values(options)[0] 163 | else 164 | call nerdtree#echo("No trees to mirror") 165 | return 166 | endif 167 | 168 | if g:NERDTree.ExistsForTab() && g:NERDTree.IsOpen() 169 | call g:NERDTree.Close() 170 | endif 171 | 172 | let t:NERDTreeBufName = bufferName 173 | call self._createTreeWin() 174 | exec 'buffer ' . bufferName 175 | if !&hidden 176 | call b:NERDTree.render() 177 | endif 178 | endfunction 179 | 180 | "FUNCTION: s:Creator._createTreeWin() {{{1 181 | "Inits the NERD tree window. ie. opens it, sizes it, sets all the local 182 | "options etc 183 | function! s:Creator._createTreeWin() 184 | "create the nerd tree window 185 | let splitLocation = g:NERDTreeWinPos ==# "left" ? "topleft " : "botright " 186 | let splitSize = g:NERDTreeWinSize 187 | 188 | if !exists('t:NERDTreeBufName') 189 | let t:NERDTreeBufName = self._nextBufferName() 190 | silent! exec splitLocation . 'vertical ' . splitSize . ' new' 191 | silent! exec "edit " . t:NERDTreeBufName 192 | else 193 | silent! exec splitLocation . 'vertical ' . splitSize . ' split' 194 | silent! exec "buffer " . t:NERDTreeBufName 195 | endif 196 | 197 | setlocal winfixwidth 198 | call self._setCommonBufOptions() 199 | endfunction 200 | 201 | "FUNCTION: s:Creator._isBufHidden(nr) {{{1 202 | function! s:Creator._isBufHidden(nr) 203 | redir => bufs 204 | silent ls! 205 | redir END 206 | 207 | return bufs =~ a:nr . '..h' 208 | endfunction 209 | 210 | "FUNCTION: s:Creator.New() {{{1 211 | function! s:Creator.New() 212 | let newCreator = copy(self) 213 | return newCreator 214 | endfunction 215 | 216 | " FUNCTION: s:Creator._nextBufferName() {{{2 217 | " returns the buffer name for the next nerd tree 218 | function! s:Creator._nextBufferName() 219 | let name = s:Creator.BufNamePrefix() . self._nextBufferNumber() 220 | return name 221 | endfunction 222 | 223 | " FUNCTION: s:Creator._nextBufferNumber() {{{2 224 | " the number to add to the nerd tree buffer name to make the buf name unique 225 | function! s:Creator._nextBufferNumber() 226 | if !exists("s:Creator._NextBufNum") 227 | let s:Creator._NextBufNum = 1 228 | else 229 | let s:Creator._NextBufNum += 1 230 | endif 231 | 232 | return s:Creator._NextBufNum 233 | endfunction 234 | 235 | "FUNCTION: s:Creator._pathForString(str) {{{1 236 | "find a bookmark or adirectory for the given string 237 | function! s:Creator._pathForString(str) 238 | let path = {} 239 | if g:NERDTreeBookmark.BookmarkExistsFor(a:str) 240 | let path = g:NERDTreeBookmark.BookmarkFor(a:str).path 241 | else 242 | let dir = a:str ==# '' ? getcwd() : a:str 243 | 244 | "hack to get an absolute path if a relative path is given 245 | if dir =~# '^\.' 246 | let dir = getcwd() . g:NERDTreePath.Slash() . dir 247 | endif 248 | let dir = g:NERDTreePath.Resolve(dir) 249 | 250 | try 251 | let path = g:NERDTreePath.New(dir) 252 | catch /^NERDTree.InvalidArgumentsError/ 253 | call nerdtree#echo("No bookmark or directory found for: " . a:str) 254 | return {} 255 | endtry 256 | endif 257 | if !path.isDirectory 258 | let path = path.getParent() 259 | endif 260 | 261 | return path 262 | endfunction 263 | 264 | " Function: s:Creator._removeTreeBufForTab() {{{1 265 | function! s:Creator._removeTreeBufForTab() 266 | let buf = bufnr(t:NERDTreeBufName) 267 | 268 | "if &hidden is not set then it will already be gone 269 | if buf != -1 270 | 271 | "nerdtree buf may be mirrored/displayed elsewhere 272 | if self._isBufHidden(buf) 273 | exec "bwipeout " . buf 274 | endif 275 | 276 | endif 277 | 278 | unlet t:NERDTreeBufName 279 | endfunction 280 | 281 | "FUNCTION: s:Creator._setCommonBufOptions() {{{1 282 | function! s:Creator._setCommonBufOptions() 283 | "throwaway buffer options 284 | setlocal noswapfile 285 | setlocal buftype=nofile 286 | setlocal bufhidden=hide 287 | setlocal nowrap 288 | setlocal foldcolumn=0 289 | setlocal foldmethod=manual 290 | setlocal nofoldenable 291 | setlocal nobuflisted 292 | setlocal nospell 293 | if g:NERDTreeShowLineNumbers 294 | setlocal nu 295 | else 296 | setlocal nonu 297 | if v:version >= 703 298 | setlocal nornu 299 | endif 300 | endif 301 | 302 | iabc 303 | 304 | if g:NERDTreeHighlightCursorline 305 | setlocal cursorline 306 | endif 307 | 308 | call self._setupStatusline() 309 | call self._bindMappings() 310 | setlocal filetype=nerdtree 311 | endfunction 312 | 313 | "FUNCTION: s:Creator._setupStatusline() {{{1 314 | function! s:Creator._setupStatusline() 315 | if g:NERDTreeStatusline != -1 316 | let &l:statusline = g:NERDTreeStatusline 317 | endif 318 | endfunction 319 | 320 | " FUNCTION: s:Creator._tabpagevar(tabnr, var) {{{1 321 | function! s:Creator._tabpagevar(tabnr, var) 322 | let currentTab = tabpagenr() 323 | let old_ei = &ei 324 | set ei=all 325 | 326 | exec "tabnext " . a:tabnr 327 | let v = -1 328 | if exists('t:' . a:var) 329 | exec 'let v = t:' . a:var 330 | endif 331 | exec "tabnext " . currentTab 332 | 333 | let &ei = old_ei 334 | 335 | return v 336 | endfunction 337 | 338 | "FUNCTION: s:Creator.ToggleTabTree(dir) {{{1 339 | function! s:Creator.ToggleTabTree(dir) 340 | let creator = s:Creator.New() 341 | call creator.toggleTabTree(a:dir) 342 | endfunction 343 | 344 | "FUNCTION: s:Creator.toggleTabTree(dir) {{{1 345 | "Toggles the NERD tree. I.e the NERD tree is open, it is closed, if it is 346 | "closed it is restored or initialized (if it doesnt exist) 347 | " 348 | "Args: 349 | "dir: the full path for the root node (is only used if the NERD tree is being 350 | "initialized. 351 | function! s:Creator.toggleTabTree(dir) 352 | if g:NERDTree.ExistsForTab() 353 | if !g:NERDTree.IsOpen() 354 | call self._createTreeWin() 355 | if !&hidden 356 | call b:NERDTree.render() 357 | endif 358 | call b:NERDTree.ui.restoreScreenState() 359 | else 360 | call g:NERDTree.Close() 361 | endif 362 | else 363 | call self.createTabTree(a:dir) 364 | endif 365 | endfunction 366 | 367 | " Function: s:Creator._uniq(list) {{{1 368 | " returns a:list without duplicates 369 | function! s:Creator._uniq(list) 370 | let uniqlist = [] 371 | for elem in a:list 372 | if index(uniqlist, elem) ==# -1 373 | let uniqlist += [elem] 374 | endif 375 | endfor 376 | return uniqlist 377 | endfunction 378 | 379 | " vim: set sw=4 sts=4 et fdm=marker: 380 | -------------------------------------------------------------------------------- /lib/nerdtree/tree_file_node.vim: -------------------------------------------------------------------------------- 1 | "CLASS: TreeFileNode 2 | "This class is the parent of the TreeDirNode class and is the 3 | "'Component' part of the composite design pattern between the treenode 4 | "classes. 5 | "============================================================ 6 | let s:TreeFileNode = {} 7 | let g:NERDTreeFileNode = s:TreeFileNode 8 | 9 | "FUNCTION: TreeFileNode.activate(...) {{{1 10 | function! s:TreeFileNode.activate(...) 11 | call self.open(a:0 ? a:1 : {}) 12 | endfunction 13 | 14 | "FUNCTION: TreeFileNode.bookmark(name) {{{1 15 | "bookmark this node with a:name 16 | function! s:TreeFileNode.bookmark(name) 17 | 18 | "if a bookmark exists with the same name and the node is cached then save 19 | "it so we can update its display string 20 | let oldMarkedNode = {} 21 | try 22 | let oldMarkedNode = g:NERDTreeBookmark.GetNodeForName(a:name, 1, self.getNerdtree()) 23 | catch /^NERDTree.BookmarkNotFoundError/ 24 | catch /^NERDTree.BookmarkedNodeNotFoundError/ 25 | endtry 26 | 27 | call g:NERDTreeBookmark.AddBookmark(a:name, self.path) 28 | call self.path.cacheDisplayString() 29 | call g:NERDTreeBookmark.Write() 30 | 31 | if !empty(oldMarkedNode) 32 | call oldMarkedNode.path.cacheDisplayString() 33 | endif 34 | endfunction 35 | 36 | "FUNCTION: TreeFileNode.cacheParent() {{{1 37 | "initializes self.parent if it isnt already 38 | function! s:TreeFileNode.cacheParent() 39 | if empty(self.parent) 40 | let parentPath = self.path.getParent() 41 | if parentPath.equals(self.path) 42 | throw "NERDTree.CannotCacheParentError: already at root" 43 | endif 44 | let self.parent = s:TreeFileNode.New(parentPath, self.getNerdtree()) 45 | endif 46 | endfunction 47 | 48 | "FUNCTION: TreeFileNode.clearBookmarks() {{{1 49 | function! s:TreeFileNode.clearBookmarks() 50 | for i in g:NERDTreeBookmark.Bookmarks() 51 | if i.path.equals(self.path) 52 | call i.delete() 53 | end 54 | endfor 55 | call self.path.cacheDisplayString() 56 | endfunction 57 | 58 | "FUNCTION: TreeFileNode.copy(dest) {{{1 59 | function! s:TreeFileNode.copy(dest) 60 | call self.path.copy(a:dest) 61 | let newPath = g:NERDTreePath.New(a:dest) 62 | let parent = self.getNerdtree().root.findNode(newPath.getParent()) 63 | if !empty(parent) 64 | call parent.refresh() 65 | return parent.findNode(newPath) 66 | else 67 | return {} 68 | endif 69 | endfunction 70 | 71 | "FUNCTION: TreeFileNode.delete {{{1 72 | "Removes this node from the tree and calls the Delete method for its path obj 73 | function! s:TreeFileNode.delete() 74 | call self.path.delete() 75 | call self.parent.removeChild(self) 76 | endfunction 77 | 78 | "FUNCTION: TreeFileNode.displayString() {{{1 79 | " 80 | "Returns a string that specifies how the node should be represented as a 81 | "string 82 | " 83 | "Return: 84 | "a string that can be used in the view to represent this node 85 | function! s:TreeFileNode.displayString() 86 | return self.path.flagSet.renderToString() . self.path.displayString() 87 | endfunction 88 | 89 | "FUNCTION: TreeFileNode.equals(treenode) {{{1 90 | " 91 | "Compares this treenode to the input treenode and returns 1 if they are the 92 | "same node. 93 | " 94 | "Use this method instead of == because sometimes when the treenodes contain 95 | "many children, vim seg faults when doing == 96 | " 97 | "Args: 98 | "treenode: the other treenode to compare to 99 | function! s:TreeFileNode.equals(treenode) 100 | return self.path.str() ==# a:treenode.path.str() 101 | endfunction 102 | 103 | "FUNCTION: TreeFileNode.findNode(path) {{{1 104 | "Returns self if this node.path.Equals the given path. 105 | "Returns {} if not equal. 106 | " 107 | "Args: 108 | "path: the path object to compare against 109 | function! s:TreeFileNode.findNode(path) 110 | if a:path.equals(self.path) 111 | return self 112 | endif 113 | return {} 114 | endfunction 115 | 116 | "FUNCTION: TreeFileNode.findOpenDirSiblingWithVisibleChildren(direction) {{{1 117 | " 118 | "Finds the next sibling for this node in the indicated direction. This sibling 119 | "must be a directory and may/may not have children as specified. 120 | " 121 | "Args: 122 | "direction: 0 if you want to find the previous sibling, 1 for the next sibling 123 | " 124 | "Return: 125 | "a treenode object or {} if no appropriate sibling could be found 126 | function! s:TreeFileNode.findOpenDirSiblingWithVisibleChildren(direction) 127 | "if we have no parent then we can have no siblings 128 | if self.parent != {} 129 | let nextSibling = self.findSibling(a:direction) 130 | 131 | while nextSibling != {} 132 | if nextSibling.path.isDirectory && nextSibling.hasVisibleChildren() && nextSibling.isOpen 133 | return nextSibling 134 | endif 135 | let nextSibling = nextSibling.findSibling(a:direction) 136 | endwhile 137 | endif 138 | 139 | return {} 140 | endfunction 141 | 142 | "FUNCTION: TreeFileNode.findSibling(direction) {{{1 143 | " 144 | "Finds the next sibling for this node in the indicated direction 145 | " 146 | "Args: 147 | "direction: 0 if you want to find the previous sibling, 1 for the next sibling 148 | " 149 | "Return: 150 | "a treenode object or {} if no sibling could be found 151 | function! s:TreeFileNode.findSibling(direction) 152 | "if we have no parent then we can have no siblings 153 | if self.parent != {} 154 | 155 | "get the index of this node in its parents children 156 | let siblingIndx = self.parent.getChildIndex(self.path) 157 | 158 | if siblingIndx != -1 159 | "move a long to the next potential sibling node 160 | let siblingIndx = a:direction ==# 1 ? siblingIndx+1 : siblingIndx-1 161 | 162 | "keep moving along to the next sibling till we find one that is valid 163 | let numSiblings = self.parent.getChildCount() 164 | while siblingIndx >= 0 && siblingIndx < numSiblings 165 | 166 | "if the next node is not an ignored node (i.e. wont show up in the 167 | "view) then return it 168 | if self.parent.children[siblingIndx].path.ignore(self.getNerdtree()) ==# 0 169 | return self.parent.children[siblingIndx] 170 | endif 171 | 172 | "go to next node 173 | let siblingIndx = a:direction ==# 1 ? siblingIndx+1 : siblingIndx-1 174 | endwhile 175 | endif 176 | endif 177 | 178 | return {} 179 | endfunction 180 | 181 | "FUNCTION: TreeFileNode.getNerdtree(){{{1 182 | function! s:TreeFileNode.getNerdtree() 183 | return self._nerdtree 184 | endfunction 185 | 186 | "FUNCTION: TreeFileNode.GetRootForTab(){{{1 187 | "get the root node for this tab 188 | function! s:TreeFileNode.GetRootForTab() 189 | if g:NERDTree.ExistsForTab() 190 | return getbufvar(t:NERDTreeBufName, 'NERDTree').root 191 | end 192 | return {} 193 | endfunction 194 | 195 | "FUNCTION: TreeFileNode.GetSelected() {{{1 196 | "gets the treenode that the cursor is currently over 197 | function! s:TreeFileNode.GetSelected() 198 | try 199 | let path = b:NERDTree.ui.getPath(line(".")) 200 | if path ==# {} 201 | return {} 202 | endif 203 | return b:NERDTree.root.findNode(path) 204 | catch /^NERDTree/ 205 | return {} 206 | endtry 207 | endfunction 208 | 209 | "FUNCTION: TreeFileNode.isVisible() {{{1 210 | "returns 1 if this node should be visible according to the tree filters and 211 | "hidden file filters (and their on/off status) 212 | function! s:TreeFileNode.isVisible() 213 | return !self.path.ignore(self.getNerdtree()) 214 | endfunction 215 | 216 | "FUNCTION: TreeFileNode.isRoot() {{{1 217 | function! s:TreeFileNode.isRoot() 218 | if !g:NERDTree.ExistsForBuf() 219 | throw "NERDTree.NoTreeError: No tree exists for the current buffer" 220 | endif 221 | 222 | return self.equals(self.getNerdtree().root) 223 | endfunction 224 | 225 | "FUNCTION: TreeFileNode.New(path, nerdtree) {{{1 226 | "Returns a new TreeNode object with the given path and parent 227 | " 228 | "Args: 229 | "path: file/dir that the node represents 230 | "nerdtree: the tree the node belongs to 231 | function! s:TreeFileNode.New(path, nerdtree) 232 | if a:path.isDirectory 233 | return g:NERDTreeDirNode.New(a:path, a:nerdtree) 234 | else 235 | let newTreeNode = copy(self) 236 | let newTreeNode.path = a:path 237 | let newTreeNode.parent = {} 238 | let newTreeNode._nerdtree = a:nerdtree 239 | return newTreeNode 240 | endif 241 | endfunction 242 | 243 | "FUNCTION: TreeFileNode.open() {{{1 244 | function! s:TreeFileNode.open(...) 245 | let opts = a:0 ? a:1 : {} 246 | let opener = g:NERDTreeOpener.New(self.path, opts) 247 | call opener.open(self) 248 | endfunction 249 | 250 | "FUNCTION: TreeFileNode.openSplit() {{{1 251 | "Open this node in a new window 252 | function! s:TreeFileNode.openSplit() 253 | call nerdtree#deprecated('TreeFileNode.openSplit', 'is deprecated, use .open() instead.') 254 | call self.open({'where': 'h'}) 255 | endfunction 256 | 257 | "FUNCTION: TreeFileNode.openVSplit() {{{1 258 | "Open this node in a new vertical window 259 | function! s:TreeFileNode.openVSplit() 260 | call nerdtree#deprecated('TreeFileNode.openVSplit', 'is deprecated, use .open() instead.') 261 | call self.open({'where': 'v'}) 262 | endfunction 263 | 264 | "FUNCTION: TreeFileNode.openInNewTab(options) {{{1 265 | function! s:TreeFileNode.openInNewTab(options) 266 | echomsg 'TreeFileNode.openInNewTab is deprecated' 267 | call self.open(extend({'where': 't'}, a:options)) 268 | endfunction 269 | 270 | "FUNCTION: TreeFileNode.putCursorHere(isJump, recurseUpward){{{1 271 | "Places the cursor on the line number this node is rendered on 272 | " 273 | "Args: 274 | "isJump: 1 if this cursor movement should be counted as a jump by vim 275 | "recurseUpward: try to put the cursor on the parent if the this node isnt 276 | "visible 277 | function! s:TreeFileNode.putCursorHere(isJump, recurseUpward) 278 | let ln = self.getNerdtree().ui.getLineNum(self) 279 | if ln != -1 280 | if a:isJump 281 | mark ' 282 | endif 283 | call cursor(ln, col(".")) 284 | else 285 | if a:recurseUpward 286 | let node = self 287 | while node != {} && self.getNerdtree().ui.getLineNum(node) ==# -1 288 | let node = node.parent 289 | call node.open() 290 | endwhile 291 | call self._nerdtree.render() 292 | call node.putCursorHere(a:isJump, 0) 293 | endif 294 | endif 295 | endfunction 296 | 297 | "FUNCTION: TreeFileNode.refresh() {{{1 298 | function! s:TreeFileNode.refresh() 299 | call self.path.refresh(self.getNerdtree()) 300 | endfunction 301 | 302 | "FUNCTION: TreeFileNode.refreshFlags() {{{1 303 | function! s:TreeFileNode.refreshFlags() 304 | call self.path.refreshFlags(self.getNerdtree()) 305 | endfunction 306 | 307 | "FUNCTION: TreeFileNode.rename() {{{1 308 | "Calls the rename method for this nodes path obj 309 | function! s:TreeFileNode.rename(newName) 310 | let newName = substitute(a:newName, '\(\\\|\/\)$', '', '') 311 | call self.path.rename(newName) 312 | call self.parent.removeChild(self) 313 | 314 | let parentPath = self.path.getParent() 315 | let newParent = self.getNerdtree().root.findNode(parentPath) 316 | 317 | if newParent != {} 318 | call newParent.createChild(self.path, 1) 319 | call newParent.refresh() 320 | endif 321 | endfunction 322 | 323 | "FUNCTION: TreeFileNode.renderToString {{{1 324 | "returns a string representation for this tree to be rendered in the view 325 | function! s:TreeFileNode.renderToString() 326 | return self._renderToString(0, 0) 327 | endfunction 328 | 329 | "Args: 330 | "depth: the current depth in the tree for this call 331 | "drawText: 1 if we should actually draw the line for this node (if 0 then the 332 | "child nodes are rendered only) 333 | "for each depth in the tree 334 | function! s:TreeFileNode._renderToString(depth, drawText) 335 | let output = "" 336 | if a:drawText ==# 1 337 | 338 | let treeParts = repeat(' ', a:depth - 1) 339 | 340 | if !self.path.isDirectory 341 | let treeParts = treeParts . ' ' 342 | endif 343 | 344 | let line = treeParts . self.displayString() 345 | 346 | let output = output . line . "\n" 347 | endif 348 | 349 | "if the node is an open dir, draw its children 350 | if self.path.isDirectory ==# 1 && self.isOpen ==# 1 351 | 352 | let childNodesToDraw = self.getVisibleChildren() 353 | 354 | if self.isCascadable() && a:depth > 0 355 | 356 | let output = output . childNodesToDraw[0]._renderToString(a:depth, 0) 357 | 358 | elseif len(childNodesToDraw) > 0 359 | for i in childNodesToDraw 360 | let output = output . i._renderToString(a:depth + 1, 1) 361 | endfor 362 | endif 363 | endif 364 | 365 | return output 366 | endfunction 367 | 368 | " vim: set sw=4 sts=4 et fdm=marker: 369 | -------------------------------------------------------------------------------- /lib/nerdtree/ui.vim: -------------------------------------------------------------------------------- 1 | "CLASS: UI 2 | "============================================================ 3 | let s:UI = {} 4 | let g:NERDTreeUI = s:UI 5 | 6 | "FUNCTION: s:UI.centerView() {{{2 7 | "centers the nerd tree window around the cursor (provided the nerd tree 8 | "options permit) 9 | function! s:UI.centerView() 10 | if g:NERDTreeAutoCenter 11 | let current_line = winline() 12 | let lines_to_top = current_line 13 | let lines_to_bottom = winheight(g:NERDTree.GetWinNum()) - current_line 14 | if lines_to_top < g:NERDTreeAutoCenterThreshold || lines_to_bottom < g:NERDTreeAutoCenterThreshold 15 | normal! zz 16 | endif 17 | endif 18 | endfunction 19 | 20 | "FUNCTION: s:UI._dumpHelp {{{1 21 | "prints out the quick help 22 | function! s:UI._dumpHelp() 23 | let old_h = @h 24 | if self.getShowHelp() 25 | let @h= "\" NERD tree (" . nerdtree#version() . ") quickhelp~\n" 26 | let @h=@h."\" ============================\n" 27 | let @h=@h."\" File node mappings~\n" 28 | let @h=@h."\" ". (g:NERDTreeMouseMode ==# 3 ? "single" : "double") ."-click,\n" 29 | let @h=@h."\" ,\n" 30 | if self.nerdtree.isTabTree() 31 | let @h=@h."\" ". g:NERDTreeMapActivateNode .": open in prev window\n" 32 | else 33 | let @h=@h."\" ". g:NERDTreeMapActivateNode .": open in current window\n" 34 | endif 35 | if self.nerdtree.isTabTree() 36 | let @h=@h."\" ". g:NERDTreeMapPreview .": preview\n" 37 | endif 38 | let @h=@h."\" ". g:NERDTreeMapOpenInTab.": open in new tab\n" 39 | let @h=@h."\" ". g:NERDTreeMapOpenInTabSilent .": open in new tab silently\n" 40 | let @h=@h."\" middle-click,\n" 41 | let @h=@h."\" ". g:NERDTreeMapOpenSplit .": open split\n" 42 | let @h=@h."\" ". g:NERDTreeMapPreviewSplit .": preview split\n" 43 | let @h=@h."\" ". g:NERDTreeMapOpenVSplit .": open vsplit\n" 44 | let @h=@h."\" ". g:NERDTreeMapPreviewVSplit .": preview vsplit\n" 45 | 46 | let @h=@h."\"\n\" ----------------------------\n" 47 | let @h=@h."\" Directory node mappings~\n" 48 | let @h=@h."\" ". (g:NERDTreeMouseMode ==# 1 ? "double" : "single") ."-click,\n" 49 | let @h=@h."\" ". g:NERDTreeMapActivateNode .": open & close node\n" 50 | let @h=@h."\" ". g:NERDTreeMapOpenRecursively .": recursively open node\n" 51 | let @h=@h."\" ". g:NERDTreeMapCloseDir .": close parent of node\n" 52 | let @h=@h."\" ". g:NERDTreeMapCloseChildren .": close all child nodes of\n" 53 | let @h=@h."\" current node recursively\n" 54 | let @h=@h."\" middle-click,\n" 55 | let @h=@h."\" ". g:NERDTreeMapOpenExpl.": explore selected dir\n" 56 | 57 | let @h=@h."\"\n\" ----------------------------\n" 58 | let @h=@h."\" Bookmark table mappings~\n" 59 | let @h=@h."\" double-click,\n" 60 | let @h=@h."\" ". g:NERDTreeMapActivateNode .": open bookmark\n" 61 | let @h=@h."\" ". g:NERDTreeMapOpenInTab.": open in new tab\n" 62 | let @h=@h."\" ". g:NERDTreeMapOpenInTabSilent .": open in new tab silently\n" 63 | let @h=@h."\" ". g:NERDTreeMapDeleteBookmark .": delete bookmark\n" 64 | 65 | let @h=@h."\"\n\" ----------------------------\n" 66 | let @h=@h."\" Tree navigation mappings~\n" 67 | let @h=@h."\" ". g:NERDTreeMapJumpRoot .": go to root\n" 68 | let @h=@h."\" ". g:NERDTreeMapJumpParent .": go to parent\n" 69 | let @h=@h."\" ". g:NERDTreeMapJumpFirstChild .": go to first child\n" 70 | let @h=@h."\" ". g:NERDTreeMapJumpLastChild .": go to last child\n" 71 | let @h=@h."\" ". g:NERDTreeMapJumpNextSibling .": go to next sibling\n" 72 | let @h=@h."\" ". g:NERDTreeMapJumpPrevSibling .": go to prev sibling\n" 73 | 74 | let @h=@h."\"\n\" ----------------------------\n" 75 | let @h=@h."\" Filesystem mappings~\n" 76 | let @h=@h."\" ". g:NERDTreeMapChangeRoot .": change tree root to the\n" 77 | let @h=@h."\" selected dir\n" 78 | let @h=@h."\" ". g:NERDTreeMapUpdir .": move tree root up a dir\n" 79 | let @h=@h."\" ". g:NERDTreeMapUpdirKeepOpen .": move tree root up a dir\n" 80 | let @h=@h."\" but leave old root open\n" 81 | let @h=@h."\" ". g:NERDTreeMapRefresh .": refresh cursor dir\n" 82 | let @h=@h."\" ". g:NERDTreeMapRefreshRoot .": refresh current root\n" 83 | let @h=@h."\" ". g:NERDTreeMapMenu .": Show menu\n" 84 | let @h=@h."\" ". g:NERDTreeMapChdir .":change the CWD to the\n" 85 | let @h=@h."\" selected dir\n" 86 | let @h=@h."\" ". g:NERDTreeMapCWD .":change tree root to CWD\n" 87 | 88 | let @h=@h."\"\n\" ----------------------------\n" 89 | let @h=@h."\" Tree filtering mappings~\n" 90 | let @h=@h."\" ". g:NERDTreeMapToggleHidden .": hidden files (" . (self.getShowHidden() ? "on" : "off") . ")\n" 91 | let @h=@h."\" ". g:NERDTreeMapToggleFilters .": file filters (" . (self.isIgnoreFilterEnabled() ? "on" : "off") . ")\n" 92 | let @h=@h."\" ". g:NERDTreeMapToggleFiles .": files (" . (self.getShowFiles() ? "on" : "off") . ")\n" 93 | let @h=@h."\" ". g:NERDTreeMapToggleBookmarks .": bookmarks (" . (self.getShowBookmarks() ? "on" : "off") . ")\n" 94 | 95 | "add quickhelp entries for each custom key map 96 | let @h=@h."\"\n\" ----------------------------\n" 97 | let @h=@h."\" Custom mappings~\n" 98 | for i in g:NERDTreeKeyMap.All() 99 | if !empty(i.quickhelpText) 100 | let @h=@h."\" ". i.key .": ". i.quickhelpText ."\n" 101 | endif 102 | endfor 103 | 104 | let @h=@h."\"\n\" ----------------------------\n" 105 | let @h=@h."\" Other mappings~\n" 106 | let @h=@h."\" ". g:NERDTreeMapQuit .": Close the NERDTree window\n" 107 | let @h=@h."\" ". g:NERDTreeMapToggleZoom .": Zoom (maximize-minimize)\n" 108 | let @h=@h."\" the NERDTree window\n" 109 | let @h=@h."\" ". g:NERDTreeMapHelp .": toggle help\n" 110 | let @h=@h."\"\n\" ----------------------------\n" 111 | let @h=@h."\" Bookmark commands~\n" 112 | let @h=@h."\" :Bookmark []\n" 113 | let @h=@h."\" :BookmarkToRoot \n" 114 | let @h=@h."\" :RevealBookmark \n" 115 | let @h=@h."\" :OpenBookmark \n" 116 | let @h=@h."\" :ClearBookmarks []\n" 117 | let @h=@h."\" :ClearAllBookmarks\n" 118 | silent! put h 119 | elseif !self.isMinimal() 120 | let @h="\" Press ". g:NERDTreeMapHelp ." for help\n" 121 | silent! put h 122 | endif 123 | 124 | let @h = old_h 125 | endfunction 126 | 127 | 128 | "FUNCTION: s:UI.new(nerdtree) {{{1 129 | function! s:UI.New(nerdtree) 130 | let newObj = copy(self) 131 | let newObj.nerdtree = a:nerdtree 132 | let newObj._showHelp = 0 133 | let newObj._ignoreEnabled = 1 134 | let newObj._showFiles = g:NERDTreeShowFiles 135 | let newObj._showHidden = g:NERDTreeShowHidden 136 | let newObj._showBookmarks = g:NERDTreeShowBookmarks 137 | 138 | return newObj 139 | endfunction 140 | 141 | "FUNCTION: s:UI.getPath(ln) {{{1 142 | "Gets the full path to the node that is rendered on the given line number 143 | " 144 | "Args: 145 | "ln: the line number to get the path for 146 | " 147 | "Return: 148 | "A path if a node was selected, {} if nothing is selected. 149 | "If the 'up a dir' line was selected then the path to the parent of the 150 | "current root is returned 151 | function! s:UI.getPath(ln) 152 | let line = getline(a:ln) 153 | 154 | let rootLine = self.getRootLineNum() 155 | 156 | "check to see if we have the root node 157 | if a:ln == rootLine 158 | return self.nerdtree.root.path 159 | endif 160 | 161 | if line ==# s:UI.UpDirLine() 162 | return self.nerdtree.root.path.getParent() 163 | endif 164 | 165 | let indent = self._indentLevelFor(line) 166 | 167 | "remove the tree parts and the leading space 168 | let curFile = self._stripMarkup(line, 0) 169 | 170 | let wasdir = 0 171 | if curFile =~# '/$' 172 | let wasdir = 1 173 | let curFile = substitute(curFile, '/\?$', '/', "") 174 | endif 175 | 176 | let dir = "" 177 | let lnum = a:ln 178 | while lnum > 0 179 | let lnum = lnum - 1 180 | let curLine = getline(lnum) 181 | let curLineStripped = self._stripMarkup(curLine, 1) 182 | 183 | "have we reached the top of the tree? 184 | if lnum == rootLine 185 | let dir = self.nerdtree.root.path.str({'format': 'UI'}) . dir 186 | break 187 | endif 188 | if curLineStripped =~# '/$' 189 | let lpindent = self._indentLevelFor(curLine) 190 | if lpindent < indent 191 | let indent = indent - 1 192 | 193 | let dir = substitute (curLineStripped,'^\\', "", "") . dir 194 | continue 195 | endif 196 | endif 197 | endwhile 198 | let curFile = self.nerdtree.root.path.drive . dir . curFile 199 | let toReturn = g:NERDTreePath.New(curFile) 200 | return toReturn 201 | endfunction 202 | 203 | "FUNCTION: s:UI.getLineNum(file_node){{{1 204 | "returns the line number this node is rendered on, or -1 if it isnt rendered 205 | function! s:UI.getLineNum(file_node) 206 | "if the node is the root then return the root line no. 207 | if a:file_node.isRoot() 208 | return self.getRootLineNum() 209 | endif 210 | 211 | let totalLines = line("$") 212 | 213 | "the path components we have matched so far 214 | let pathcomponents = [substitute(self.nerdtree.root.path.str({'format': 'UI'}), '/ *$', '', '')] 215 | "the index of the component we are searching for 216 | let curPathComponent = 1 217 | 218 | let fullpath = a:file_node.path.str({'format': 'UI'}) 219 | 220 | let lnum = self.getRootLineNum() 221 | while lnum > 0 222 | let lnum = lnum + 1 223 | "have we reached the bottom of the tree? 224 | if lnum ==# totalLines+1 225 | return -1 226 | endif 227 | 228 | let curLine = getline(lnum) 229 | 230 | let indent = self._indentLevelFor(curLine) 231 | if indent ==# curPathComponent 232 | let curLine = self._stripMarkup(curLine, 1) 233 | 234 | let curPath = join(pathcomponents, '/') . '/' . curLine 235 | if stridx(fullpath, curPath, 0) ==# 0 236 | if fullpath ==# curPath || strpart(fullpath, len(curPath)-1,1) ==# '/' 237 | let curLine = substitute(curLine, '/ *$', '', '') 238 | call add(pathcomponents, curLine) 239 | let curPathComponent = curPathComponent + 1 240 | 241 | if fullpath ==# curPath 242 | return lnum 243 | endif 244 | endif 245 | endif 246 | endif 247 | endwhile 248 | return -1 249 | endfunction 250 | 251 | "FUNCTION: s:UI.getRootLineNum(){{{1 252 | "gets the line number of the root node 253 | function! s:UI.getRootLineNum() 254 | let rootLine = 1 255 | while getline(rootLine) !~# '^\(/\|<\)' 256 | let rootLine = rootLine + 1 257 | endwhile 258 | return rootLine 259 | endfunction 260 | 261 | "FUNCTION: s:UI.getShowBookmarks() {{{1 262 | function! s:UI.getShowBookmarks() 263 | return self._showBookmarks 264 | endfunction 265 | 266 | "FUNCTION: s:UI.getShowFiles() {{{1 267 | function! s:UI.getShowFiles() 268 | return self._showFiles 269 | endfunction 270 | 271 | "FUNCTION: s:UI.getShowHelp() {{{1 272 | function! s:UI.getShowHelp() 273 | return self._showHelp 274 | endfunction 275 | 276 | "FUNCTION: s:UI.getShowHidden() {{{1 277 | function! s:UI.getShowHidden() 278 | return self._showHidden 279 | endfunction 280 | 281 | "FUNCTION: s:UI._indentLevelFor(line) {{{1 282 | function! s:UI._indentLevelFor(line) 283 | "have to do this work around because match() returns bytes, not chars 284 | let numLeadBytes = match(a:line, '\M\[^ '.g:NERDTreeDirArrowExpandable.g:NERDTreeDirArrowCollapsible.']') 285 | let leadChars = strchars(a:line[0:numLeadBytes-1]) 286 | 287 | return leadChars / s:UI.IndentWid() 288 | endfunction 289 | 290 | "FUNCTION: s:UI.IndentWid() {{{1 291 | function! s:UI.IndentWid() 292 | return 2 293 | endfunction 294 | 295 | "FUNCTION: s:UI.isIgnoreFilterEnabled() {{{1 296 | function! s:UI.isIgnoreFilterEnabled() 297 | return self._ignoreEnabled == 1 298 | endfunction 299 | 300 | "FUNCTION: s:UI.isMinimal() {{{1 301 | function! s:UI.isMinimal() 302 | return g:NERDTreeMinimalUI 303 | endfunction 304 | 305 | "FUNCTION: s:UI.MarkupReg() {{{1 306 | function! s:UI.MarkupReg() 307 | return '^\(['.g:NERDTreeDirArrowExpandable.g:NERDTreeDirArrowCollapsible.'] \| \+['.g:NERDTreeDirArrowExpandable.g:NERDTreeDirArrowCollapsible.'] \| \+\)' 308 | endfunction 309 | 310 | "FUNCTION: s:UI._renderBookmarks {{{1 311 | function! s:UI._renderBookmarks() 312 | 313 | if !self.isMinimal() 314 | call setline(line(".")+1, ">----------Bookmarks----------") 315 | call cursor(line(".")+1, col(".")) 316 | endif 317 | 318 | for i in g:NERDTreeBookmark.Bookmarks() 319 | call setline(line(".")+1, i.str()) 320 | call cursor(line(".")+1, col(".")) 321 | endfor 322 | 323 | call setline(line(".")+1, '') 324 | call cursor(line(".")+1, col(".")) 325 | endfunction 326 | 327 | "FUNCTION: s:UI.restoreScreenState() {{{1 328 | " 329 | "Sets the screen state back to what it was when nerdtree#saveScreenState was last 330 | "called. 331 | " 332 | "Assumes the cursor is in the NERDTree window 333 | function! s:UI.restoreScreenState() 334 | if !has_key(self, '_screenState') 335 | return 336 | endif 337 | exec("silent vertical resize " . self._screenState['oldWindowSize']) 338 | 339 | let old_scrolloff=&scrolloff 340 | let &scrolloff=0 341 | call cursor(self._screenState['oldTopLine'], 0) 342 | normal! zt 343 | call setpos(".", self._screenState['oldPos']) 344 | let &scrolloff=old_scrolloff 345 | endfunction 346 | 347 | "FUNCTION: s:UI.saveScreenState() {{{1 348 | "Saves the current cursor position in the current buffer and the window 349 | "scroll position 350 | function! s:UI.saveScreenState() 351 | let win = winnr() 352 | call g:NERDTree.CursorToTreeWin() 353 | let self._screenState = {} 354 | let self._screenState['oldPos'] = getpos(".") 355 | let self._screenState['oldTopLine'] = line("w0") 356 | let self._screenState['oldWindowSize']= winwidth("") 357 | call nerdtree#exec(win . "wincmd w") 358 | endfunction 359 | 360 | "FUNCTION: s:UI.setShowHidden(val) {{{1 361 | function! s:UI.setShowHidden(val) 362 | let self._showHidden = a:val 363 | endfunction 364 | 365 | "FUNCTION: s:UI._stripMarkup(line, removeLeadingSpaces){{{1 366 | "returns the given line with all the tree parts stripped off 367 | " 368 | "Args: 369 | "line: the subject line 370 | "removeLeadingSpaces: 1 if leading spaces are to be removed (leading spaces = 371 | "any spaces before the actual text of the node) 372 | function! s:UI._stripMarkup(line, removeLeadingSpaces) 373 | let line = a:line 374 | "remove the tree parts and the leading space 375 | let line = substitute (line, g:NERDTreeUI.MarkupReg(),"","") 376 | 377 | "strip off any read only flag 378 | let line = substitute (line, ' \['.g:NERDTreeGlyphReadOnly.'\]', "","") 379 | 380 | "strip off any bookmark flags 381 | let line = substitute (line, ' {[^}]*}', "","") 382 | 383 | "strip off any executable flags 384 | let line = substitute (line, '*\ze\($\| \)', "","") 385 | 386 | "strip off any generic flags 387 | let line = substitute (line, '\[[^]]*\]', "","") 388 | 389 | let wasdir = 0 390 | if line =~# '/$' 391 | let wasdir = 1 392 | endif 393 | let line = substitute (line,' -> .*',"","") " remove link to 394 | if wasdir ==# 1 395 | let line = substitute (line, '/\?$', '/', "") 396 | endif 397 | 398 | if a:removeLeadingSpaces 399 | let line = substitute (line, '^ *', '', '') 400 | endif 401 | 402 | return line 403 | endfunction 404 | 405 | "FUNCTION: s:UI.render() {{{1 406 | function! s:UI.render() 407 | setlocal modifiable 408 | 409 | "remember the top line of the buffer and the current line so we can 410 | "restore the view exactly how it was 411 | let curLine = line(".") 412 | let curCol = col(".") 413 | let topLine = line("w0") 414 | 415 | "delete all lines in the buffer (being careful not to clobber a register) 416 | silent 1,$delete _ 417 | 418 | call self._dumpHelp() 419 | 420 | "delete the blank line before the help and add one after it 421 | if !self.isMinimal() 422 | call setline(line(".")+1, "") 423 | call cursor(line(".")+1, col(".")) 424 | endif 425 | 426 | if self.getShowBookmarks() 427 | call self._renderBookmarks() 428 | endif 429 | 430 | "add the 'up a dir' line 431 | if !self.isMinimal() 432 | call setline(line(".")+1, s:UI.UpDirLine()) 433 | call cursor(line(".")+1, col(".")) 434 | endif 435 | 436 | "draw the header line 437 | let header = self.nerdtree.root.path.str({'format': 'UI', 'truncateTo': winwidth(0)}) 438 | call setline(line(".")+1, header) 439 | call cursor(line(".")+1, col(".")) 440 | 441 | "draw the tree 442 | let old_o = @o 443 | let @o = self.nerdtree.root.renderToString() 444 | silent put o 445 | let @o = old_o 446 | 447 | "delete the blank line at the top of the buffer 448 | silent 1,1delete _ 449 | 450 | "restore the view 451 | let old_scrolloff=&scrolloff 452 | let &scrolloff=0 453 | call cursor(topLine, 1) 454 | normal! zt 455 | call cursor(curLine, curCol) 456 | let &scrolloff = old_scrolloff 457 | 458 | setlocal nomodifiable 459 | endfunction 460 | 461 | 462 | "FUNCTION: UI.renderViewSavingPosition {{{1 463 | "Renders the tree and ensures the cursor stays on the current node or the 464 | "current nodes parent if it is no longer available upon re-rendering 465 | function! s:UI.renderViewSavingPosition() 466 | let currentNode = g:NERDTreeFileNode.GetSelected() 467 | 468 | "go up the tree till we find a node that will be visible or till we run 469 | "out of nodes 470 | while currentNode != {} && !currentNode.isVisible() && !currentNode.isRoot() 471 | let currentNode = currentNode.parent 472 | endwhile 473 | 474 | call self.render() 475 | 476 | if currentNode != {} 477 | call currentNode.putCursorHere(0, 0) 478 | endif 479 | endfunction 480 | 481 | "FUNCTION: s:UI.toggleHelp() {{{1 482 | function! s:UI.toggleHelp() 483 | let self._showHelp = !self._showHelp 484 | endfunction 485 | 486 | " FUNCTION: s:UI.toggleIgnoreFilter() {{{1 487 | " toggles the use of the NERDTreeIgnore option 488 | function! s:UI.toggleIgnoreFilter() 489 | let self._ignoreEnabled = !self._ignoreEnabled 490 | call self.renderViewSavingPosition() 491 | call self.centerView() 492 | endfunction 493 | 494 | " FUNCTION: s:UI.toggleShowBookmarks() {{{1 495 | " toggles the display of bookmarks 496 | function! s:UI.toggleShowBookmarks() 497 | let self._showBookmarks = !self._showBookmarks 498 | if self.getShowBookmarks() 499 | call self.nerdtree.render() 500 | call g:NERDTree.CursorToBookmarkTable() 501 | else 502 | call self.renderViewSavingPosition() 503 | endif 504 | call self.centerView() 505 | endfunction 506 | 507 | " FUNCTION: s:UI.toggleShowFiles() {{{1 508 | " toggles the display of hidden files 509 | function! s:UI.toggleShowFiles() 510 | let self._showFiles = !self._showFiles 511 | call self.renderViewSavingPosition() 512 | call self.centerView() 513 | endfunction 514 | 515 | " FUNCTION: s:UI.toggleShowHidden() {{{1 516 | " toggles the display of hidden files 517 | function! s:UI.toggleShowHidden() 518 | let self._showHidden = !self._showHidden 519 | call self.renderViewSavingPosition() 520 | call self.centerView() 521 | endfunction 522 | 523 | " FUNCTION: s:UI.toggleZoom() {{{1 524 | " zoom (maximize/minimize) the NERDTree window 525 | function! s:UI.toggleZoom() 526 | if exists("b:NERDTreeZoomed") && b:NERDTreeZoomed 527 | let size = exists("b:NERDTreeOldWindowSize") ? b:NERDTreeOldWindowSize : g:NERDTreeWinSize 528 | exec "silent vertical resize ". size 529 | let b:NERDTreeZoomed = 0 530 | else 531 | exec "vertical resize" 532 | let b:NERDTreeZoomed = 1 533 | endif 534 | endfunction 535 | 536 | "FUNCTION: s:UI.UpDirLine() {{{1 537 | function! s:UI.UpDirLine() 538 | return '.. (up a dir)' 539 | endfunction 540 | -------------------------------------------------------------------------------- /lib/nerdtree/tree_dir_node.vim: -------------------------------------------------------------------------------- 1 | "CLASS: TreeDirNode 2 | "A subclass of NERDTreeFileNode. 3 | " 4 | "The 'composite' part of the file/dir composite. 5 | "============================================================ 6 | let s:TreeDirNode = copy(g:NERDTreeFileNode) 7 | let g:NERDTreeDirNode = s:TreeDirNode 8 | 9 | "FUNCTION: TreeDirNode.AbsoluteTreeRoot(){{{1 10 | "class method that returns the highest cached ancestor of the current root 11 | function! s:TreeDirNode.AbsoluteTreeRoot() 12 | let currentNode = b:NERDTree.root 13 | while currentNode.parent != {} 14 | let currentNode = currentNode.parent 15 | endwhile 16 | return currentNode 17 | endfunction 18 | 19 | "FUNCTION: TreeDirNode.activate([options]) {{{1 20 | unlet s:TreeDirNode.activate 21 | function! s:TreeDirNode.activate(...) 22 | let opts = a:0 ? a:1 : {} 23 | call self.toggleOpen(opts) 24 | call self.getNerdtree().render() 25 | call self.putCursorHere(0, 0) 26 | endfunction 27 | 28 | "FUNCTION: TreeDirNode.addChild(treenode, inOrder) {{{1 29 | "Adds the given treenode to the list of children for this node 30 | " 31 | "Args: 32 | "-treenode: the node to add 33 | "-inOrder: 1 if the new node should be inserted in sorted order 34 | function! s:TreeDirNode.addChild(treenode, inOrder) 35 | call add(self.children, a:treenode) 36 | let a:treenode.parent = self 37 | 38 | if a:inOrder 39 | call self.sortChildren() 40 | endif 41 | endfunction 42 | 43 | "FUNCTION: TreeDirNode.close() {{{1 44 | "Closes this directory 45 | function! s:TreeDirNode.close() 46 | let self.isOpen = 0 47 | endfunction 48 | 49 | "FUNCTION: TreeDirNode.closeChildren() {{{1 50 | "Closes all the child dir nodes of this node 51 | function! s:TreeDirNode.closeChildren() 52 | for i in self.children 53 | if i.path.isDirectory 54 | call i.close() 55 | call i.closeChildren() 56 | endif 57 | endfor 58 | endfunction 59 | 60 | "FUNCTION: TreeDirNode.createChild(path, inOrder) {{{1 61 | "Instantiates a new child node for this node with the given path. The new 62 | "nodes parent is set to this node. 63 | " 64 | "Args: 65 | "path: a Path object that this node will represent/contain 66 | "inOrder: 1 if the new node should be inserted in sorted order 67 | " 68 | "Returns: 69 | "the newly created node 70 | function! s:TreeDirNode.createChild(path, inOrder) 71 | let newTreeNode = g:NERDTreeFileNode.New(a:path, self.getNerdtree()) 72 | call self.addChild(newTreeNode, a:inOrder) 73 | return newTreeNode 74 | endfunction 75 | 76 | "FUNCTION: TreeDirNode.displayString() {{{1 77 | unlet s:TreeDirNode.displayString 78 | function! s:TreeDirNode.displayString() 79 | let cascade = self.getCascade() 80 | let rv = "" 81 | for node in cascade 82 | let rv = rv . node.path.displayString() 83 | endfor 84 | 85 | let sym = cascade[-1].isOpen ? g:NERDTreeDirArrowCollapsible : g:NERDTreeDirArrowExpandable 86 | 87 | let flags = cascade[-1].path.flagSet.renderToString() 88 | 89 | return sym . ' ' . flags . rv 90 | endfunction 91 | 92 | "FUNCTION: TreeDirNode.findNode(path) {{{1 93 | "Will find one of the children (recursively) that has the given path 94 | " 95 | "Args: 96 | "path: a path object 97 | unlet s:TreeDirNode.findNode 98 | function! s:TreeDirNode.findNode(path) 99 | if a:path.equals(self.path) 100 | return self 101 | endif 102 | if stridx(a:path.str(), self.path.str(), 0) ==# -1 103 | return {} 104 | endif 105 | 106 | if self.path.isDirectory 107 | for i in self.children 108 | let retVal = i.findNode(a:path) 109 | if retVal != {} 110 | return retVal 111 | endif 112 | endfor 113 | endif 114 | return {} 115 | endfunction 116 | 117 | "FUNCTION: TreeDirNode.getCascade() {{{1 118 | "Return an array of dir nodes (starting from self) that can be cascade opened. 119 | function! s:TreeDirNode.getCascade() 120 | 121 | let rv = [self] 122 | let node = self 123 | 124 | while 1 125 | let vc = node.getVisibleChildren() 126 | if len(vc) != 1 127 | break 128 | endif 129 | 130 | let visChild = vc[0] 131 | 132 | "TODO: optimize 133 | if !visChild.path.isDirectory 134 | break 135 | endif 136 | 137 | call add(rv, visChild) 138 | let node = visChild 139 | endwhile 140 | 141 | return rv 142 | endfunction 143 | 144 | "FUNCTION: TreeDirNode.getChildCount() {{{1 145 | "Returns the number of children this node has 146 | function! s:TreeDirNode.getChildCount() 147 | return len(self.children) 148 | endfunction 149 | 150 | "FUNCTION: TreeDirNode.getChild(path) {{{1 151 | "Returns child node of this node that has the given path or {} if no such node 152 | "exists. 153 | " 154 | "This function doesnt not recurse into child dir nodes 155 | " 156 | "Args: 157 | "path: a path object 158 | function! s:TreeDirNode.getChild(path) 159 | if stridx(a:path.str(), self.path.str(), 0) ==# -1 160 | return {} 161 | endif 162 | 163 | let index = self.getChildIndex(a:path) 164 | if index ==# -1 165 | return {} 166 | else 167 | return self.children[index] 168 | endif 169 | 170 | endfunction 171 | 172 | "FUNCTION: TreeDirNode.getChildByIndex(indx, visible) {{{1 173 | "returns the child at the given index 174 | "Args: 175 | "indx: the index to get the child from 176 | "visible: 1 if only the visible children array should be used, 0 if all the 177 | "children should be searched. 178 | function! s:TreeDirNode.getChildByIndex(indx, visible) 179 | let array_to_search = a:visible? self.getVisibleChildren() : self.children 180 | if a:indx > len(array_to_search) 181 | throw "NERDTree.InvalidArgumentsError: Index is out of bounds." 182 | endif 183 | return array_to_search[a:indx] 184 | endfunction 185 | 186 | "FUNCTION: TreeDirNode.getChildIndex(path) {{{1 187 | "Returns the index of the child node of this node that has the given path or 188 | "-1 if no such node exists. 189 | " 190 | "This function doesnt not recurse into child dir nodes 191 | " 192 | "Args: 193 | "path: a path object 194 | function! s:TreeDirNode.getChildIndex(path) 195 | if stridx(a:path.str(), self.path.str(), 0) ==# -1 196 | return -1 197 | endif 198 | 199 | "do a binary search for the child 200 | let a = 0 201 | let z = self.getChildCount() 202 | while a < z 203 | let mid = (a+z)/2 204 | let diff = a:path.compareTo(self.children[mid].path) 205 | 206 | if diff ==# -1 207 | let z = mid 208 | elseif diff ==# 1 209 | let a = mid+1 210 | else 211 | return mid 212 | endif 213 | endwhile 214 | return -1 215 | endfunction 216 | 217 | "FUNCTION: TreeDirNode.getDirChildren() {{{1 218 | "Get all children that are directories 219 | function! s:TreeDirNode.getDirChildren() 220 | return filter(self.children, 'v:val.path.isDirectory == 1') 221 | endfunction 222 | 223 | "FUNCTION: TreeDirNode.GetSelected() {{{1 224 | "Returns the current node if it is a dir node, or else returns the current 225 | "nodes parent 226 | unlet s:TreeDirNode.GetSelected 227 | function! s:TreeDirNode.GetSelected() 228 | let currentDir = g:NERDTreeFileNode.GetSelected() 229 | if currentDir != {} && !currentDir.isRoot() 230 | if currentDir.path.isDirectory ==# 0 231 | let currentDir = currentDir.parent 232 | endif 233 | endif 234 | return currentDir 235 | endfunction 236 | 237 | "FUNCTION: TreeDirNode.getVisibleChildCount() {{{1 238 | "Returns the number of visible children this node has 239 | function! s:TreeDirNode.getVisibleChildCount() 240 | return len(self.getVisibleChildren()) 241 | endfunction 242 | 243 | "FUNCTION: TreeDirNode.getVisibleChildren() {{{1 244 | "Returns a list of children to display for this node, in the correct order 245 | " 246 | "Return: 247 | "an array of treenodes 248 | function! s:TreeDirNode.getVisibleChildren() 249 | let toReturn = [] 250 | for i in self.children 251 | if i.path.ignore(self.getNerdtree()) ==# 0 252 | call add(toReturn, i) 253 | endif 254 | endfor 255 | return toReturn 256 | endfunction 257 | 258 | "FUNCTION: TreeDirNode.hasVisibleChildren() {{{1 259 | "returns 1 if this node has any childre, 0 otherwise.. 260 | function! s:TreeDirNode.hasVisibleChildren() 261 | return self.getVisibleChildCount() != 0 262 | endfunction 263 | 264 | "FUNCTION: TreeDirNode.isCascadable() {{{1 265 | "true if this dir has only one visible child - which is also a dir 266 | function! s:TreeDirNode.isCascadable() 267 | let c = self.getVisibleChildren() 268 | return len(c) == 1 && c[0].path.isDirectory 269 | endfunction 270 | 271 | "FUNCTION: TreeDirNode._initChildren() {{{1 272 | "Removes all childen from this node and re-reads them 273 | " 274 | "Args: 275 | "silent: 1 if the function should not echo any "please wait" messages for 276 | "large directories 277 | " 278 | "Return: the number of child nodes read 279 | function! s:TreeDirNode._initChildren(silent) 280 | "remove all the current child nodes 281 | let self.children = [] 282 | 283 | "get an array of all the files in the nodes dir 284 | let dir = self.path 285 | let globDir = dir.str({'format': 'Glob'}) 286 | 287 | if version >= 703 288 | let filesStr = globpath(globDir, '*', !g:NERDTreeRespectWildIgnore) . "\n" . globpath(globDir, '.*', !g:NERDTreeRespectWildIgnore) 289 | else 290 | let filesStr = globpath(globDir, '*') . "\n" . globpath(globDir, '.*') 291 | endif 292 | 293 | let files = split(filesStr, "\n") 294 | 295 | if !a:silent && len(files) > g:NERDTreeNotificationThreshold 296 | call nerdtree#echo("Please wait, caching a large dir ...") 297 | endif 298 | 299 | let invalidFilesFound = 0 300 | for i in files 301 | 302 | "filter out the .. and . directories 303 | "Note: we must match .. AND ../ since sometimes the globpath returns 304 | "../ for path with strange chars (eg $) 305 | if i[len(i)-3:2] != ".." && i[len(i)-2:2] != ".." && 306 | \ i[len(i)-2:1] != "." && i[len(i)-1] != "." 307 | "put the next file in a new node and attach it 308 | try 309 | let path = g:NERDTreePath.New(i) 310 | call self.createChild(path, 0) 311 | call g:NERDTreePathNotifier.NotifyListeners('init', path, self.getNerdtree(), {}) 312 | catch /^NERDTree.\(InvalidArguments\|InvalidFiletype\)Error/ 313 | let invalidFilesFound += 1 314 | endtry 315 | endif 316 | endfor 317 | 318 | call self.sortChildren() 319 | 320 | if !a:silent && len(files) > g:NERDTreeNotificationThreshold 321 | call nerdtree#echo("Please wait, caching a large dir ... DONE (". self.getChildCount() ." nodes cached).") 322 | endif 323 | 324 | if invalidFilesFound 325 | call nerdtree#echoWarning(invalidFilesFound . " file(s) could not be loaded into the NERD tree") 326 | endif 327 | return self.getChildCount() 328 | endfunction 329 | 330 | "FUNCTION: TreeDirNode.New(path, nerdtree) {{{1 331 | "Returns a new TreeNode object with the given path and parent 332 | " 333 | "Args: 334 | "path: dir that the node represents 335 | "nerdtree: the tree the node belongs to 336 | function! s:TreeDirNode.New(path, nerdtree) 337 | if a:path.isDirectory != 1 338 | throw "NERDTree.InvalidArgumentsError: A TreeDirNode object must be instantiated with a directory Path object." 339 | endif 340 | 341 | let newTreeNode = copy(self) 342 | let newTreeNode.path = a:path 343 | 344 | let newTreeNode.isOpen = 0 345 | let newTreeNode.children = [] 346 | 347 | let newTreeNode.parent = {} 348 | let newTreeNode._nerdtree = a:nerdtree 349 | 350 | return newTreeNode 351 | endfunction 352 | 353 | "FUNCTION: TreeDirNode.open([opts]) {{{1 354 | "Open the dir in the current tree or in a new tree elsewhere. 355 | " 356 | "If opening in the current tree, return the number of cached nodes. 357 | unlet s:TreeDirNode.open 358 | function! s:TreeDirNode.open(...) 359 | let opts = a:0 ? a:1 : {} 360 | 361 | if has_key(opts, 'where') && !empty(opts['where']) 362 | let opener = g:NERDTreeOpener.New(self.path, opts) 363 | call opener.open(self) 364 | else 365 | let self.isOpen = 1 366 | if self.children ==# [] 367 | return self._initChildren(0) 368 | else 369 | return 0 370 | endif 371 | endif 372 | endfunction 373 | 374 | "FUNCTION: TreeDirNode.openAlong([opts]) {{{1 375 | "recursive open the dir if it has only one directory child. 376 | " 377 | "return the level of opened directories. 378 | function! s:TreeDirNode.openAlong(...) 379 | let opts = a:0 ? a:1 : {} 380 | let level = 0 381 | 382 | let node = self 383 | while node.path.isDirectory 384 | call node.open(opts) 385 | let level += 1 386 | if node.getVisibleChildCount() == 1 387 | let node = node.getChildByIndex(0, 1) 388 | else 389 | break 390 | endif 391 | endwhile 392 | return level 393 | endfunction 394 | 395 | " FUNCTION: TreeDirNode.openExplorer() {{{1 396 | " opens an explorer window for this node in the previous window (could be a 397 | " nerd tree or a netrw) 398 | function! s:TreeDirNode.openExplorer() 399 | call self.open({'where': 'p'}) 400 | endfunction 401 | 402 | "FUNCTION: TreeDirNode.openInNewTab(options) {{{1 403 | unlet s:TreeDirNode.openInNewTab 404 | function! s:TreeDirNode.openInNewTab(options) 405 | call nerdtree#deprecated('TreeDirNode.openInNewTab', 'is deprecated, use open() instead') 406 | call self.open({'where': 't'}) 407 | endfunction 408 | 409 | "FUNCTION: TreeDirNode._openInNewTab() {{{1 410 | function! s:TreeDirNode._openInNewTab() 411 | tabnew 412 | call g:NERDTreeCreator.CreateTabTree(self.path.str()) 413 | endfunction 414 | 415 | "FUNCTION: TreeDirNode.openRecursively() {{{1 416 | "Opens this treenode and all of its children whose paths arent 'ignored' 417 | "because of the file filters. 418 | " 419 | "This method is actually a wrapper for the OpenRecursively2 method which does 420 | "the work. 421 | function! s:TreeDirNode.openRecursively() 422 | call self._openRecursively2(1) 423 | endfunction 424 | 425 | "FUNCTION: TreeDirNode._openRecursively2() {{{1 426 | "Opens this all children of this treenode recursively if either: 427 | " *they arent filtered by file filters 428 | " *a:forceOpen is 1 429 | " 430 | "Args: 431 | "forceOpen: 1 if this node should be opened regardless of file filters 432 | function! s:TreeDirNode._openRecursively2(forceOpen) 433 | if self.path.ignore(self.getNerdtree()) ==# 0 || a:forceOpen 434 | let self.isOpen = 1 435 | if self.children ==# [] 436 | call self._initChildren(1) 437 | endif 438 | 439 | for i in self.children 440 | if i.path.isDirectory ==# 1 441 | call i._openRecursively2(0) 442 | endif 443 | endfor 444 | endif 445 | endfunction 446 | 447 | "FUNCTION: TreeDirNode.refresh() {{{1 448 | unlet s:TreeDirNode.refresh 449 | function! s:TreeDirNode.refresh() 450 | call self.path.refresh(self.getNerdtree()) 451 | 452 | "if this node was ever opened, refresh its children 453 | if self.isOpen || !empty(self.children) 454 | "go thru all the files/dirs under this node 455 | let newChildNodes = [] 456 | let invalidFilesFound = 0 457 | let dir = self.path 458 | let globDir = dir.str({'format': 'Glob'}) 459 | let filesStr = globpath(globDir, '*') . "\n" . globpath(globDir, '.*') 460 | let files = split(filesStr, "\n") 461 | for i in files 462 | "filter out the .. and . directories 463 | "Note: we must match .. AND ../ cos sometimes the globpath returns 464 | "../ for path with strange chars (eg $) 465 | "if i !~# '\/\.\.\/\?$' && i !~# '\/\.\/\?$' 466 | 467 | " Regular expression is too expensive. Use simply string comparison 468 | " instead 469 | if i[len(i)-3:2] != ".." && i[len(i)-2:2] != ".." && 470 | \ i[len(i)-2:1] != "." && i[len(i)-1] != "." 471 | try 472 | "create a new path and see if it exists in this nodes children 473 | let path = g:NERDTreePath.New(i) 474 | let newNode = self.getChild(path) 475 | if newNode != {} 476 | call newNode.refresh() 477 | call add(newChildNodes, newNode) 478 | 479 | "the node doesnt exist so create it 480 | else 481 | let newNode = g:NERDTreeFileNode.New(path, self.getNerdtree()) 482 | let newNode.parent = self 483 | call add(newChildNodes, newNode) 484 | endif 485 | 486 | 487 | catch /^NERDTree.\(InvalidArguments\|InvalidFiletype\)Error/ 488 | let invalidFilesFound = 1 489 | endtry 490 | endif 491 | endfor 492 | 493 | "swap this nodes children out for the children we just read/refreshed 494 | let self.children = newChildNodes 495 | call self.sortChildren() 496 | 497 | if invalidFilesFound 498 | call nerdtree#echoWarning("some files could not be loaded into the NERD tree") 499 | endif 500 | endif 501 | endfunction 502 | 503 | "FUNCTION: TreeDirNode.refreshFlags() {{{1 504 | unlet s:TreeDirNode.refreshFlags 505 | function! s:TreeDirNode.refreshFlags() 506 | call self.path.refreshFlags(self.getNerdtree()) 507 | for i in self.children 508 | call i.refreshFlags() 509 | endfor 510 | endfunction 511 | 512 | "FUNCTION: TreeDirNode.refreshDirFlags() {{{1 513 | function! s:TreeDirNode.refreshDirFlags() 514 | call self.path.refreshFlags(self.getNerdtree()) 515 | endfunction 516 | 517 | "FUNCTION: TreeDirNode.reveal(path) {{{1 518 | "reveal the given path, i.e. cache and open all treenodes needed to display it 519 | "in the UI 520 | "Returns the revealed node 521 | function! s:TreeDirNode.reveal(path, ...) 522 | let opts = a:0 ? a:1 : {} 523 | 524 | if !a:path.isUnder(self.path) 525 | throw "NERDTree.InvalidArgumentsError: " . a:path.str() . " should be under " . self.path.str() 526 | endif 527 | 528 | call self.open() 529 | 530 | if self.path.equals(a:path.getParent()) 531 | let n = self.findNode(a:path) 532 | if has_key(opts, "open") 533 | call n.open() 534 | endif 535 | return n 536 | endif 537 | 538 | let p = a:path 539 | while !p.getParent().equals(self.path) 540 | let p = p.getParent() 541 | endwhile 542 | 543 | let n = self.findNode(p) 544 | return n.reveal(a:path, opts) 545 | endfunction 546 | 547 | "FUNCTION: TreeDirNode.removeChild(treenode) {{{1 548 | " 549 | "Removes the given treenode from this nodes set of children 550 | " 551 | "Args: 552 | "treenode: the node to remove 553 | " 554 | "Throws a NERDTree.ChildNotFoundError if the given treenode is not found 555 | function! s:TreeDirNode.removeChild(treenode) 556 | for i in range(0, self.getChildCount()-1) 557 | if self.children[i].equals(a:treenode) 558 | call remove(self.children, i) 559 | return 560 | endif 561 | endfor 562 | 563 | throw "NERDTree.ChildNotFoundError: child node was not found" 564 | endfunction 565 | 566 | "FUNCTION: TreeDirNode.sortChildren() {{{1 567 | " 568 | "Sorts the children of this node according to alphabetical order and the 569 | "directory priority. 570 | " 571 | function! s:TreeDirNode.sortChildren() 572 | let CompareFunc = function("nerdtree#compareNodesBySortKey") 573 | call sort(self.children, CompareFunc) 574 | endfunction 575 | 576 | "FUNCTION: TreeDirNode.toggleOpen([options]) {{{1 577 | "Opens this directory if it is closed and vice versa 578 | function! s:TreeDirNode.toggleOpen(...) 579 | let opts = a:0 ? a:1 : {} 580 | if self.isOpen ==# 1 581 | call self.close() 582 | else 583 | if g:NERDTreeCascadeOpenSingleChildDir == 0 584 | call self.open(opts) 585 | else 586 | call self.openAlong(opts) 587 | endif 588 | endif 589 | endfunction 590 | 591 | "FUNCTION: TreeDirNode.transplantChild(newNode) {{{1 592 | "Replaces the child of this with the given node (where the child node's full 593 | "path matches a:newNode's fullpath). The search for the matching node is 594 | "non-recursive 595 | " 596 | "Arg: 597 | "newNode: the node to graft into the tree 598 | function! s:TreeDirNode.transplantChild(newNode) 599 | for i in range(0, self.getChildCount()-1) 600 | if self.children[i].equals(a:newNode) 601 | let self.children[i] = a:newNode 602 | let a:newNode.parent = self 603 | break 604 | endif 605 | endfor 606 | endfunction 607 | 608 | " vim: set sw=4 sts=4 et fdm=marker: 609 | -------------------------------------------------------------------------------- /lib/nerdtree/path.vim: -------------------------------------------------------------------------------- 1 | "we need to use this number many times for sorting... so we calculate it only 2 | "once here 3 | let s:NERDTreeSortStarIndex = index(g:NERDTreeSortOrder, '*') 4 | " used in formating sortKey, e.g. '%04d' 5 | if exists("log10") 6 | let s:sortKeyFormat = "%0" . float2nr(ceil(log10(len(g:NERDTreeSortOrder)))) . "d" 7 | else 8 | let s:sortKeyFormat = "%04d" 9 | endif 10 | 11 | "CLASS: Path 12 | "============================================================ 13 | let s:Path = {} 14 | let g:NERDTreePath = s:Path 15 | 16 | "FUNCTION: Path.AbsolutePathFor(str) {{{1 17 | function! s:Path.AbsolutePathFor(str) 18 | let prependCWD = 0 19 | if nerdtree#runningWindows() 20 | let prependCWD = a:str !~# '^.:\(\\\|\/\)' && a:str !~# '^\(\\\\\|\/\/\)' 21 | else 22 | let prependCWD = a:str !~# '^/' 23 | endif 24 | 25 | let toReturn = a:str 26 | if prependCWD 27 | let toReturn = getcwd() . s:Path.Slash() . a:str 28 | endif 29 | 30 | return toReturn 31 | endfunction 32 | 33 | "FUNCTION: Path.bookmarkNames() {{{1 34 | function! s:Path.bookmarkNames() 35 | if !exists("self._bookmarkNames") 36 | call self.cacheDisplayString() 37 | endif 38 | return self._bookmarkNames 39 | endfunction 40 | 41 | "FUNCTION: Path.cacheDisplayString() {{{1 42 | function! s:Path.cacheDisplayString() abort 43 | let self.cachedDisplayString = self.getLastPathComponent(1) 44 | 45 | if self.isExecutable 46 | let self.cachedDisplayString = self.cachedDisplayString . '*' 47 | endif 48 | 49 | let self._bookmarkNames = [] 50 | for i in g:NERDTreeBookmark.Bookmarks() 51 | if i.path.equals(self) 52 | call add(self._bookmarkNames, i.name) 53 | endif 54 | endfor 55 | if !empty(self._bookmarkNames) 56 | let self.cachedDisplayString .= ' {' . join(self._bookmarkNames) . '}' 57 | endif 58 | 59 | if self.isSymLink 60 | let self.cachedDisplayString .= ' -> ' . self.symLinkDest 61 | endif 62 | 63 | if self.isReadOnly 64 | let self.cachedDisplayString .= ' ['.g:NERDTreeGlyphReadOnly.']' 65 | endif 66 | endfunction 67 | 68 | "FUNCTION: Path.changeToDir() {{{1 69 | function! s:Path.changeToDir() 70 | let dir = self.str({'format': 'Cd'}) 71 | if self.isDirectory ==# 0 72 | let dir = self.getParent().str({'format': 'Cd'}) 73 | endif 74 | 75 | try 76 | execute "cd " . dir 77 | call nerdtree#echo("CWD is now: " . getcwd()) 78 | catch 79 | throw "NERDTree.PathChangeError: cannot change CWD to " . dir 80 | endtry 81 | endfunction 82 | 83 | "FUNCTION: Path.compareTo() {{{1 84 | " 85 | "Compares this Path to the given path and returns 0 if they are equal, -1 if 86 | "this Path is "less than" the given path, or 1 if it is "greater". 87 | " 88 | "Args: 89 | "path: the path object to compare this to 90 | " 91 | "Return: 92 | "1, -1 or 0 93 | function! s:Path.compareTo(path) 94 | let thisPath = self.getLastPathComponent(1) 95 | let thatPath = a:path.getLastPathComponent(1) 96 | 97 | "if the paths are the same then clearly we return 0 98 | if thisPath ==# thatPath 99 | return 0 100 | endif 101 | 102 | let thisSS = self.getSortOrderIndex() 103 | let thatSS = a:path.getSortOrderIndex() 104 | 105 | "compare the sort sequences, if they are different then the return 106 | "value is easy 107 | if thisSS < thatSS 108 | return -1 109 | elseif thisSS > thatSS 110 | return 1 111 | else 112 | if !g:NERDTreeSortHiddenFirst 113 | let thisPath = substitute(thisPath, '^[._]', '', '') 114 | let thatPath = substitute(thatPath, '^[._]', '', '') 115 | endif 116 | "if the sort sequences are the same then compare the paths 117 | "alphabetically 118 | let pathCompare = g:NERDTreeCaseSensitiveSort ? thisPath <# thatPath : thisPath " 302 | endif 303 | 304 | return " \\`\|\"#%&,?()\*^<>[]" 305 | endfunction 306 | 307 | "FUNCTION: Path.getDir() {{{1 308 | " 309 | "Returns this path if it is a directory, else this paths parent. 310 | " 311 | "Return: 312 | "a Path object 313 | function! s:Path.getDir() 314 | if self.isDirectory 315 | return self 316 | else 317 | return self.getParent() 318 | endif 319 | endfunction 320 | 321 | "FUNCTION: Path.getParent() {{{1 322 | " 323 | "Returns a new path object for this paths parent 324 | " 325 | "Return: 326 | "a new Path object 327 | function! s:Path.getParent() 328 | if nerdtree#runningWindows() 329 | let path = self.drive . '\' . join(self.pathSegments[0:-2], '\') 330 | else 331 | let path = '/'. join(self.pathSegments[0:-2], '/') 332 | endif 333 | 334 | return s:Path.New(path) 335 | endfunction 336 | 337 | "FUNCTION: Path.getLastPathComponent(dirSlash) {{{1 338 | " 339 | "Gets the last part of this path. 340 | " 341 | "Args: 342 | "dirSlash: if 1 then a trailing slash will be added to the returned value for 343 | "directory nodes. 344 | function! s:Path.getLastPathComponent(dirSlash) 345 | if empty(self.pathSegments) 346 | return '' 347 | endif 348 | let toReturn = self.pathSegments[-1] 349 | if a:dirSlash && self.isDirectory 350 | let toReturn = toReturn . '/' 351 | endif 352 | return toReturn 353 | endfunction 354 | 355 | "FUNCTION: Path.getSortOrderIndex() {{{1 356 | "returns the index of the pattern in g:NERDTreeSortOrder that this path matches 357 | function! s:Path.getSortOrderIndex() 358 | let i = 0 359 | while i < len(g:NERDTreeSortOrder) 360 | if self.getLastPathComponent(1) =~# g:NERDTreeSortOrder[i] 361 | return i 362 | endif 363 | let i = i + 1 364 | endwhile 365 | return s:NERDTreeSortStarIndex 366 | endfunction 367 | 368 | "FUNCTION: Path.getSortKey() {{{1 369 | "returns a string used in compare function for sorting 370 | function! s:Path.getSortKey() 371 | if !exists("self._sortKey") 372 | let path = self.getLastPathComponent(1) 373 | if !g:NERDTreeSortHiddenFirst 374 | let path = substitute(path, '^[._]', '', '') 375 | endif 376 | if !g:NERDTreeCaseSensitiveSort 377 | let path = tolower(path) 378 | endif 379 | let self._sortKey = printf(s:sortKeyFormat, self.getSortOrderIndex()) . path 380 | endif 381 | 382 | return self._sortKey 383 | endfunction 384 | 385 | 386 | "FUNCTION: Path.isUnixHiddenFile() {{{1 387 | "check for unix hidden files 388 | function! s:Path.isUnixHiddenFile() 389 | return self.getLastPathComponent(0) =~# '^\.' 390 | endfunction 391 | 392 | "FUNCTION: Path.isUnixHiddenPath() {{{1 393 | "check for unix path with hidden components 394 | function! s:Path.isUnixHiddenPath() 395 | if self.getLastPathComponent(0) =~# '^\.' 396 | return 1 397 | else 398 | for segment in self.pathSegments 399 | if segment =~# '^\.' 400 | return 1 401 | endif 402 | endfor 403 | return 0 404 | endif 405 | endfunction 406 | 407 | "FUNCTION: Path.ignore(nerdtree) {{{1 408 | "returns true if this path should be ignored 409 | function! s:Path.ignore(nerdtree) 410 | "filter out the user specified paths to ignore 411 | if a:nerdtree.ui.isIgnoreFilterEnabled() 412 | for i in g:NERDTreeIgnore 413 | if self._ignorePatternMatches(i) 414 | return 1 415 | endif 416 | endfor 417 | 418 | for callback in g:NERDTree.PathFilters() 419 | if {callback}({'path': self, 'nerdtree': a:nerdtree}) 420 | return 1 421 | endif 422 | endfor 423 | endif 424 | 425 | "dont show hidden files unless instructed to 426 | if !a:nerdtree.ui.getShowHidden() && self.isUnixHiddenFile() 427 | return 1 428 | endif 429 | 430 | if a:nerdtree.ui.getShowFiles() ==# 0 && self.isDirectory ==# 0 431 | return 1 432 | endif 433 | 434 | return 0 435 | endfunction 436 | 437 | "FUNCTION: Path._ignorePatternMatches(pattern) {{{1 438 | "returns true if this path matches the given ignore pattern 439 | function! s:Path._ignorePatternMatches(pattern) 440 | let pat = a:pattern 441 | if strpart(pat,len(pat)-7) == '[[dir]]' 442 | if !self.isDirectory 443 | return 0 444 | endif 445 | let pat = strpart(pat,0, len(pat)-7) 446 | elseif strpart(pat,len(pat)-8) == '[[file]]' 447 | if self.isDirectory 448 | return 0 449 | endif 450 | let pat = strpart(pat,0, len(pat)-8) 451 | endif 452 | 453 | return self.getLastPathComponent(0) =~# pat 454 | endfunction 455 | 456 | "FUNCTION: Path.isAncestor(path) {{{1 457 | "return 1 if this path is somewhere above the given path in the filesystem. 458 | " 459 | "a:path should be a dir 460 | function! s:Path.isAncestor(path) 461 | if !self.isDirectory 462 | return 0 463 | endif 464 | 465 | let this = self.str() 466 | let that = a:path.str() 467 | return stridx(that, this) == 0 468 | endfunction 469 | 470 | "FUNCTION: Path.isUnder(path) {{{1 471 | "return 1 if this path is somewhere under the given path in the filesystem. 472 | function! s:Path.isUnder(path) 473 | if a:path.isDirectory == 0 474 | return 0 475 | endif 476 | 477 | let this = self.str() 478 | let that = a:path.str() 479 | return stridx(this, that . s:Path.Slash()) == 0 480 | endfunction 481 | 482 | "FUNCTION: Path.JoinPathStrings(...) {{{1 483 | function! s:Path.JoinPathStrings(...) 484 | let components = [] 485 | for i in a:000 486 | let components = extend(components, split(i, '/')) 487 | endfor 488 | return '/' . join(components, '/') 489 | endfunction 490 | 491 | "FUNCTION: Path.equals() {{{1 492 | " 493 | "Determines whether 2 path objects are "equal". 494 | "They are equal if the paths they represent are the same 495 | " 496 | "Args: 497 | "path: the other path obj to compare this with 498 | function! s:Path.equals(path) 499 | return self.str() ==# a:path.str() 500 | endfunction 501 | 502 | "FUNCTION: Path.New() {{{1 503 | "The Constructor for the Path object 504 | function! s:Path.New(path) 505 | let newPath = copy(self) 506 | 507 | call newPath.readInfoFromDisk(s:Path.AbsolutePathFor(a:path)) 508 | 509 | let newPath.cachedDisplayString = "" 510 | let newPath.flagSet = g:NERDTreeFlagSet.New() 511 | 512 | return newPath 513 | endfunction 514 | 515 | "FUNCTION: Path.Slash() {{{1 516 | "return the slash to use for the current OS 517 | function! s:Path.Slash() 518 | return nerdtree#runningWindows() ? '\' : '/' 519 | endfunction 520 | 521 | "FUNCTION: Path.Resolve() {{{1 522 | "Invoke the vim resolve() function and return the result 523 | "This is necessary because in some versions of vim resolve() removes trailing 524 | "slashes while in other versions it doesn't. This always removes the trailing 525 | "slash 526 | function! s:Path.Resolve(path) 527 | let tmp = resolve(a:path) 528 | return tmp =~# '.\+/$' ? substitute(tmp, '/$', '', '') : tmp 529 | endfunction 530 | 531 | "FUNCTION: Path.readInfoFromDisk(fullpath) {{{1 532 | " 533 | " 534 | "Throws NERDTree.Path.InvalidArguments exception. 535 | function! s:Path.readInfoFromDisk(fullpath) 536 | call self.extractDriveLetter(a:fullpath) 537 | 538 | let fullpath = s:Path.WinToUnixPath(a:fullpath) 539 | 540 | if getftype(fullpath) ==# "fifo" 541 | throw "NERDTree.InvalidFiletypeError: Cant handle FIFO files: " . a:fullpath 542 | endif 543 | 544 | let self.pathSegments = split(fullpath, '/') 545 | 546 | let self.isReadOnly = 0 547 | if isdirectory(a:fullpath) 548 | let self.isDirectory = 1 549 | elseif filereadable(a:fullpath) 550 | let self.isDirectory = 0 551 | let self.isReadOnly = filewritable(a:fullpath) ==# 0 552 | else 553 | throw "NERDTree.InvalidArgumentsError: Invalid path = " . a:fullpath 554 | endif 555 | 556 | let self.isExecutable = 0 557 | if !self.isDirectory 558 | let self.isExecutable = getfperm(a:fullpath) =~# 'x' 559 | endif 560 | 561 | "grab the last part of the path (minus the trailing slash) 562 | let lastPathComponent = self.getLastPathComponent(0) 563 | 564 | "get the path to the new node with the parent dir fully resolved 565 | let hardPath = s:Path.Resolve(self.strTrunk()) . '/' . lastPathComponent 566 | 567 | "if the last part of the path is a symlink then flag it as such 568 | let self.isSymLink = (s:Path.Resolve(hardPath) != hardPath) 569 | if self.isSymLink 570 | let self.symLinkDest = s:Path.Resolve(fullpath) 571 | 572 | "if the link is a dir then slap a / on the end of its dest 573 | if isdirectory(self.symLinkDest) 574 | 575 | "we always wanna treat MS windows shortcuts as files for 576 | "simplicity 577 | if hardPath !~# '\.lnk$' 578 | 579 | let self.symLinkDest = self.symLinkDest . '/' 580 | endif 581 | endif 582 | endif 583 | endfunction 584 | 585 | "FUNCTION: Path.refresh(nerdtree) {{{1 586 | function! s:Path.refresh(nerdtree) 587 | call self.readInfoFromDisk(self.str()) 588 | call g:NERDTreePathNotifier.NotifyListeners('refresh', self, a:nerdtree, {}) 589 | call self.cacheDisplayString() 590 | endfunction 591 | 592 | "FUNCTION: Path.refreshFlags(nerdtree) {{{1 593 | function! s:Path.refreshFlags(nerdtree) 594 | call g:NERDTreePathNotifier.NotifyListeners('refreshFlags', self, a:nerdtree, {}) 595 | call self.cacheDisplayString() 596 | endfunction 597 | 598 | "FUNCTION: Path.rename() {{{1 599 | " 600 | "Renames this node on the filesystem 601 | function! s:Path.rename(newPath) 602 | if a:newPath ==# '' 603 | throw "NERDTree.InvalidArgumentsError: Invalid newPath for renaming = ". a:newPath 604 | endif 605 | 606 | let success = rename(self.str(), a:newPath) 607 | if success != 0 608 | throw "NERDTree.PathRenameError: Could not rename: '" . self.str() . "'" . 'to:' . a:newPath 609 | endif 610 | call self.readInfoFromDisk(a:newPath) 611 | 612 | for i in self.bookmarkNames() 613 | let b = g:NERDTreeBookmark.BookmarkFor(i) 614 | call b.setPath(copy(self)) 615 | endfor 616 | call g:NERDTreeBookmark.Write() 617 | endfunction 618 | 619 | "FUNCTION: Path.str() {{{1 620 | " 621 | "Returns a string representation of this Path 622 | " 623 | "Takes an optional dictionary param to specify how the output should be 624 | "formatted. 625 | " 626 | "The dict may have the following keys: 627 | " 'format' 628 | " 'escape' 629 | " 'truncateTo' 630 | " 631 | "The 'format' key may have a value of: 632 | " 'Cd' - a string to be used with the :cd command 633 | " 'Edit' - a string to be used with :e :sp :new :tabedit etc 634 | " 'UI' - a string used in the NERD tree UI 635 | " 636 | "The 'escape' key, if specified will cause the output to be escaped with 637 | "shellescape() 638 | " 639 | "The 'truncateTo' key causes the resulting string to be truncated to the value 640 | "'truncateTo' maps to. A '<' char will be prepended. 641 | function! s:Path.str(...) 642 | let options = a:0 ? a:1 : {} 643 | let toReturn = "" 644 | 645 | if has_key(options, 'format') 646 | let format = options['format'] 647 | if has_key(self, '_strFor' . format) 648 | exec 'let toReturn = self._strFor' . format . '()' 649 | else 650 | throw 'NERDTree.UnknownFormatError: unknown format "'. format .'"' 651 | endif 652 | else 653 | let toReturn = self._str() 654 | endif 655 | 656 | if nerdtree#has_opt(options, 'escape') 657 | let toReturn = shellescape(toReturn) 658 | endif 659 | 660 | if has_key(options, 'truncateTo') 661 | let limit = options['truncateTo'] 662 | if len(toReturn) > limit-1 663 | let toReturn = toReturn[(len(toReturn)-limit+1):] 664 | if len(split(toReturn, '/')) > 1 665 | let toReturn = '' . s:SID() . '_' 9 | 10 | call NERDTreeAddKeyMap({ 'key': '', 'scope': "all", 'callback': s."handleMiddleMouse" }) 11 | call NERDTreeAddKeyMap({ 'key': '', 'scope': "all", 'callback': s."handleLeftClick" }) 12 | call NERDTreeAddKeyMap({ 'key': '<2-LeftMouse>', 'scope': "DirNode", 'callback': s."activateDirNode" }) 13 | call NERDTreeAddKeyMap({ 'key': '<2-LeftMouse>', 'scope': "FileNode", 'callback': s."activateFileNode" }) 14 | call NERDTreeAddKeyMap({ 'key': '<2-LeftMouse>', 'scope': "Bookmark", 'callback': s."activateBookmark" }) 15 | call NERDTreeAddKeyMap({ 'key': '<2-LeftMouse>', 'scope': "all", 'callback': s."activateAll" }) 16 | 17 | 18 | call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapActivateNode, 'scope': "DirNode", 'callback': s."activateDirNode" }) 19 | call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapActivateNode, 'scope': "FileNode", 'callback': s."activateFileNode" }) 20 | call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapActivateNode, 'scope': "Bookmark", 'callback': s."activateBookmark" }) 21 | call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapActivateNode, 'scope': "all", 'callback': s."activateAll" }) 22 | 23 | call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapOpenSplit, 'scope': "Node", 'callback': s."openHSplit" }) 24 | call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapOpenVSplit, 'scope': "Node", 'callback': s."openVSplit" }) 25 | 26 | call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapOpenSplit, 'scope': "Bookmark", 'callback': s."openHSplit" }) 27 | call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapOpenVSplit, 'scope': "Bookmark", 'callback': s."openVSplit" }) 28 | 29 | call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapPreview, 'scope': "Node", 'callback': s."previewNodeCurrent" }) 30 | call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapPreviewVSplit, 'scope': "Node", 'callback': s."previewNodeVSplit" }) 31 | call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapPreviewSplit, 'scope': "Node", 'callback': s."previewNodeHSplit" }) 32 | 33 | call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapPreview, 'scope': "Bookmark", 'callback': s."previewNodeCurrent" }) 34 | call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapPreviewVSplit, 'scope': "Bookmark", 'callback': s."previewNodeVSplit" }) 35 | call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapPreviewSplit, 'scope': "Bookmark", 'callback': s."previewNodeHSplit" }) 36 | 37 | call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapOpenRecursively, 'scope': "DirNode", 'callback': s."openNodeRecursively" }) 38 | 39 | call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapUpdir, 'scope': "all", 'callback': s."upDirCurrentRootClosed" }) 40 | call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapUpdirKeepOpen, 'scope': "all", 'callback': s."upDirCurrentRootOpen" }) 41 | call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapChangeRoot, 'scope': "Node", 'callback': s."chRoot" }) 42 | 43 | call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapChdir, 'scope': "Node", 'callback': s."chCwd" }) 44 | 45 | call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapQuit, 'scope': "all", 'callback': s."closeTreeWindow" }) 46 | 47 | call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapCWD, 'scope': "all", 'callback': "nerdtree#ui_glue#chRootCwd" }) 48 | 49 | call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapRefreshRoot, 'scope': "all", 'callback': s."refreshRoot" }) 50 | call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapRefresh, 'scope': "Node", 'callback': s."refreshCurrent" }) 51 | 52 | call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapHelp, 'scope': "all", 'callback': s."displayHelp" }) 53 | call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapToggleZoom, 'scope': "all", 'callback': s."toggleZoom" }) 54 | call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapToggleHidden, 'scope': "all", 'callback': s."toggleShowHidden" }) 55 | call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapToggleFilters, 'scope': "all", 'callback': s."toggleIgnoreFilter" }) 56 | call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapToggleFiles, 'scope': "all", 'callback': s."toggleShowFiles" }) 57 | call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapToggleBookmarks, 'scope': "all", 'callback': s."toggleShowBookmarks" }) 58 | 59 | call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapCloseDir, 'scope': "Node", 'callback': s."closeCurrentDir" }) 60 | call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapCloseChildren, 'scope': "DirNode", 'callback': s."closeChildren" }) 61 | 62 | call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapMenu, 'scope': "Node", 'callback': s."showMenu" }) 63 | 64 | call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapJumpParent, 'scope': "Node", 'callback': s."jumpToParent" }) 65 | call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapJumpFirstChild, 'scope': "Node", 'callback': s."jumpToFirstChild" }) 66 | call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapJumpLastChild, 'scope': "Node", 'callback': s."jumpToLastChild" }) 67 | call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapJumpRoot, 'scope': "all", 'callback': s."jumpToRoot" }) 68 | call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapJumpNextSibling, 'scope': "Node", 'callback': s."jumpToNextSibling" }) 69 | call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapJumpPrevSibling, 'scope': "Node", 'callback': s."jumpToPrevSibling" }) 70 | 71 | call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapOpenInTab, 'scope': "Node", 'callback': s."openInNewTab" }) 72 | call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapOpenInTabSilent, 'scope': "Node", 'callback': s."openInNewTabSilent" }) 73 | call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapOpenInTab, 'scope': "Bookmark", 'callback': s."openInNewTab" }) 74 | call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapOpenInTabSilent, 'scope': "Bookmark", 'callback': s."openInNewTabSilent" }) 75 | 76 | call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapOpenExpl, 'scope': "DirNode", 'callback': s."openExplorer" }) 77 | 78 | call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapDeleteBookmark, 'scope': "Bookmark", 'callback': s."deleteBookmark" }) 79 | endfunction 80 | 81 | 82 | "SECTION: Interface bindings {{{1 83 | "============================================================ 84 | 85 | "FUNCTION: s:activateAll() {{{1 86 | "handle the user activating the updir line 87 | function! s:activateAll() 88 | if getline(".") ==# g:NERDTreeUI.UpDirLine() 89 | return nerdtree#ui_glue#upDir(0) 90 | endif 91 | endfunction 92 | 93 | "FUNCTION: s:activateDirNode() {{{1 94 | "handle the user activating a tree node 95 | function! s:activateDirNode(node) 96 | call a:node.activate() 97 | endfunction 98 | 99 | "FUNCTION: s:activateFileNode() {{{1 100 | "handle the user activating a tree node 101 | function! s:activateFileNode(node) 102 | call a:node.activate({'reuse': 'all', 'where': 'p'}) 103 | endfunction 104 | 105 | "FUNCTION: s:activateBookmark() {{{1 106 | "handle the user activating a bookmark 107 | function! s:activateBookmark(bm) 108 | call a:bm.activate(b:NERDTree, !a:bm.path.isDirectory ? {'where': 'p'} : {}) 109 | endfunction 110 | 111 | " FUNCTION: nerdtree#ui_glue#bookmarkNode(name) {{{1 112 | " Associate the current node with the given name 113 | function! nerdtree#ui_glue#bookmarkNode(...) 114 | let currentNode = g:NERDTreeFileNode.GetSelected() 115 | if currentNode != {} 116 | let name = a:1 117 | if empty(name) 118 | let name = currentNode.path.getLastPathComponent(0) 119 | endif 120 | try 121 | call currentNode.bookmark(name) 122 | call b:NERDTree.render() 123 | catch /^NERDTree.IllegalBookmarkNameError/ 124 | call nerdtree#echo("bookmark names must not contain spaces") 125 | endtry 126 | else 127 | call nerdtree#echo("select a node first") 128 | endif 129 | endfunction 130 | 131 | " FUNCTION: s:chCwd(node) {{{1 132 | function! s:chCwd(node) 133 | try 134 | call a:node.path.changeToDir() 135 | catch /^NERDTree.PathChangeError/ 136 | call nerdtree#echoWarning("could not change cwd") 137 | endtry 138 | endfunction 139 | 140 | " FUNCTION: s:chRoot(node) {{{1 141 | " changes the current root to the selected one 142 | function! s:chRoot(node) 143 | call b:NERDTree.changeRoot(a:node) 144 | endfunction 145 | 146 | " FUNCTION: s:nerdtree#ui_glue#chRootCwd() {{{1 147 | " changes the current root to CWD 148 | function! nerdtree#ui_glue#chRootCwd() 149 | try 150 | let cwd = g:NERDTreePath.New(getcwd()) 151 | catch /^NERDTree.InvalidArgumentsError/ 152 | call nerdtree#echo("current directory does not exist.") 153 | return 154 | endtry 155 | if cwd.str() == g:NERDTreeFileNode.GetRootForTab().path.str() 156 | return 157 | endif 158 | call s:chRoot(g:NERDTreeDirNode.New(cwd, b:NERDTree)) 159 | endfunction 160 | 161 | " FUNCTION: nnerdtree#ui_glue#clearBookmarks(bookmarks) {{{1 162 | function! nerdtree#ui_glue#clearBookmarks(bookmarks) 163 | if a:bookmarks ==# '' 164 | let currentNode = g:NERDTreeFileNode.GetSelected() 165 | if currentNode != {} 166 | call currentNode.clearBookmarks() 167 | endif 168 | else 169 | for name in split(a:bookmarks, ' ') 170 | let bookmark = g:NERDTreeBookmark.BookmarkFor(name) 171 | call bookmark.delete() 172 | endfor 173 | endif 174 | call b:NERDTree.root.refresh() 175 | call b:NERDTree.render() 176 | endfunction 177 | 178 | " FUNCTION: s:closeChildren(node) {{{1 179 | " closes all childnodes of the current node 180 | function! s:closeChildren(node) 181 | call a:node.closeChildren() 182 | call b:NERDTree.render() 183 | call a:node.putCursorHere(0, 0) 184 | endfunction 185 | 186 | " FUNCTION: s:closeCurrentDir(node) {{{1 187 | " closes the parent dir of the current node 188 | function! s:closeCurrentDir(node) 189 | let parent = a:node.parent 190 | while g:NERDTreeCascadeOpenSingleChildDir && !parent.isRoot() 191 | let childNodes = parent.getVisibleChildren() 192 | if len(childNodes) == 1 && childNodes[0].path.isDirectory 193 | let parent = parent.parent 194 | else 195 | break 196 | endif 197 | endwhile 198 | if parent ==# {} || parent.isRoot() 199 | call nerdtree#echo("cannot close tree root") 200 | else 201 | call parent.close() 202 | call b:NERDTree.render() 203 | call parent.putCursorHere(0, 0) 204 | endif 205 | endfunction 206 | 207 | " FUNCTION: s:closeTreeWindow() {{{1 208 | " close the tree window 209 | function! s:closeTreeWindow() 210 | if b:NERDTree.isWinTree() && b:NERDTree.previousBuf() != -1 211 | exec "buffer " . b:NERDTree.previousBuf() 212 | else 213 | if winnr("$") > 1 214 | call g:NERDTree.Close() 215 | else 216 | call nerdtree#echo("Cannot close last window") 217 | endif 218 | endif 219 | endfunction 220 | 221 | " FUNCTION: s:deleteBookmark(bm) {{{1 222 | " if the cursor is on a bookmark, prompt to delete 223 | function! s:deleteBookmark(bm) 224 | echo "Are you sure you wish to delete the bookmark:\n\"" . a:bm.name . "\" (yN):" 225 | 226 | if nr2char(getchar()) ==# 'y' 227 | try 228 | call a:bm.delete() 229 | call b:NERDTree.root.refresh() 230 | call b:NERDTree.render() 231 | redraw 232 | catch /^NERDTree/ 233 | call nerdtree#echoWarning("Could not remove bookmark") 234 | endtry 235 | else 236 | call nerdtree#echo("delete aborted" ) 237 | endif 238 | 239 | endfunction 240 | 241 | " FUNCTION: s:displayHelp() {{{1 242 | " toggles the help display 243 | function! s:displayHelp() 244 | call b:NERDTree.ui.toggleHelp() 245 | call b:NERDTree.render() 246 | call b:NERDTree.ui.centerView() 247 | endfunction 248 | 249 | " FUNCTION: s:findAndRevealPath() {{{1 250 | function! s:findAndRevealPath() 251 | try 252 | let p = g:NERDTreePath.New(expand("%:p")) 253 | catch /^NERDTree.InvalidArgumentsError/ 254 | call nerdtree#echo("no file for the current buffer") 255 | return 256 | endtry 257 | 258 | if p.isUnixHiddenPath() 259 | let showhidden=g:NERDTreeShowHidden 260 | let g:NERDTreeShowHidden = 1 261 | endif 262 | 263 | if !g:NERDTree.ExistsForTab() 264 | try 265 | let cwd = g:NERDTreePath.New(getcwd()) 266 | catch /^NERDTree.InvalidArgumentsError/ 267 | call nerdtree#echo("current directory does not exist.") 268 | let cwd = p.getParent() 269 | endtry 270 | 271 | if p.isUnder(cwd) 272 | call g:NERDTreeCreator.CreateTabTree(cwd.str()) 273 | else 274 | call g:NERDTreeCreator.CreateTabTree(p.getParent().str()) 275 | endif 276 | else 277 | if !p.isUnder(g:NERDTreeFileNode.GetRootForTab().path) 278 | if !g:NERDTree.IsOpen() 279 | call g:NERDTreeCreator.ToggleTabTree('') 280 | else 281 | call g:NERDTree.CursorToTreeWin() 282 | endif 283 | call b:NERDTree.ui.setShowHidden(g:NERDTreeShowHidden) 284 | call s:chRoot(g:NERDTreeDirNode.New(p.getParent(), b:NERDTree)) 285 | else 286 | if !g:NERDTree.IsOpen() 287 | call g:NERDTreeCreator.ToggleTabTree("") 288 | endif 289 | endif 290 | endif 291 | call g:NERDTree.CursorToTreeWin() 292 | let node = b:NERDTree.root.reveal(p) 293 | call b:NERDTree.render() 294 | call node.putCursorHere(1,0) 295 | 296 | if p.isUnixHiddenFile() 297 | let g:NERDTreeShowHidden = showhidden 298 | endif 299 | endfunction 300 | 301 | "FUNCTION: s:handleLeftClick() {{{1 302 | "Checks if the click should open the current node 303 | function! s:handleLeftClick() 304 | let currentNode = g:NERDTreeFileNode.GetSelected() 305 | if currentNode != {} 306 | 307 | "the dir arrows are multibyte chars, and vim's string functions only 308 | "deal with single bytes - so split the line up with the hack below and 309 | "take the line substring manually 310 | let line = split(getline(line(".")), '\zs') 311 | let startToCur = "" 312 | for i in range(0,len(line)-1) 313 | let startToCur .= line[i] 314 | endfor 315 | 316 | if currentNode.path.isDirectory 317 | if startToCur =~# g:NERDTreeUI.MarkupReg() && startToCur =~# '[+~'.g:NERDTreeDirArrowExpandable.g:NERDTreeDirArrowCollapsible.'] \?$' 318 | call currentNode.activate() 319 | return 320 | endif 321 | endif 322 | 323 | if (g:NERDTreeMouseMode ==# 2 && currentNode.path.isDirectory) || g:NERDTreeMouseMode ==# 3 324 | let char = strpart(startToCur, strlen(startToCur)-1, 1) 325 | if char !~# g:NERDTreeUI.MarkupReg() 326 | if currentNode.path.isDirectory 327 | call currentNode.activate() 328 | else 329 | call currentNode.activate({'reuse': 'all', 'where': 'p'}) 330 | endif 331 | return 332 | endif 333 | endif 334 | endif 335 | endfunction 336 | 337 | " FUNCTION: s:handleMiddleMouse() {{{1 338 | function! s:handleMiddleMouse() 339 | let curNode = g:NERDTreeFileNode.GetSelected() 340 | if curNode ==# {} 341 | call nerdtree#echo("Put the cursor on a node first" ) 342 | return 343 | endif 344 | 345 | if curNode.path.isDirectory 346 | call nerdtree#openExplorer(curNode) 347 | else 348 | call curNode.open({'where': 'h'}) 349 | endif 350 | endfunction 351 | 352 | " FUNCTION: s:jumpToChild(direction) {{{2 353 | " Args: 354 | " direction: 0 if going to first child, 1 if going to last 355 | function! s:jumpToChild(currentNode, direction) 356 | if a:currentNode.isRoot() 357 | return nerdtree#echo("cannot jump to " . (a:direction ? "last" : "first") . " child") 358 | end 359 | let dirNode = a:currentNode.parent 360 | let childNodes = dirNode.getVisibleChildren() 361 | 362 | let targetNode = childNodes[0] 363 | if a:direction 364 | let targetNode = childNodes[len(childNodes) - 1] 365 | endif 366 | 367 | if targetNode.equals(a:currentNode) 368 | let siblingDir = a:currentNode.parent.findOpenDirSiblingWithVisibleChildren(a:direction) 369 | if siblingDir != {} 370 | let indx = a:direction ? siblingDir.getVisibleChildCount()-1 : 0 371 | let targetNode = siblingDir.getChildByIndex(indx, 1) 372 | endif 373 | endif 374 | 375 | call targetNode.putCursorHere(1, 0) 376 | 377 | call b:NERDTree.ui.centerView() 378 | endfunction 379 | 380 | 381 | " FUNCTION: nerdtree#ui_glue#invokeKeyMap(key) {{{1 382 | "this is needed since I cant figure out how to invoke dict functions from a 383 | "key map 384 | function! nerdtree#ui_glue#invokeKeyMap(key) 385 | call g:NERDTreeKeyMap.Invoke(a:key) 386 | endfunction 387 | 388 | " FUNCTION: s:jumpToFirstChild() {{{1 389 | " wrapper for the jump to child method 390 | function! s:jumpToFirstChild(node) 391 | call s:jumpToChild(a:node, 0) 392 | endfunction 393 | 394 | " FUNCTION: s:jumpToLastChild() {{{1 395 | " wrapper for the jump to child method 396 | function! s:jumpToLastChild(node) 397 | call s:jumpToChild(a:node, 1) 398 | endfunction 399 | 400 | " FUNCTION: s:jumpToParent(node) {{{1 401 | " moves the cursor to the parent of the current node 402 | function! s:jumpToParent(node) 403 | if !empty(a:node.parent) 404 | call a:node.parent.putCursorHere(1, 0) 405 | call b:NERDTree.ui.centerView() 406 | else 407 | call nerdtree#echo("cannot jump to parent") 408 | endif 409 | endfunction 410 | 411 | " FUNCTION: s:jumpToRoot() {{{1 412 | " moves the cursor to the root node 413 | function! s:jumpToRoot() 414 | call b:NERDTree.root.putCursorHere(1, 0) 415 | call b:NERDTree.ui.centerView() 416 | endfunction 417 | 418 | " FUNCTION: s:jumpToNextSibling(node) {{{1 419 | function! s:jumpToNextSibling(node) 420 | call s:jumpToSibling(a:node, 1) 421 | endfunction 422 | 423 | " FUNCTION: s:jumpToPrevSibling(node) {{{1 424 | function! s:jumpToPrevSibling(node) 425 | call s:jumpToSibling(a:node, 0) 426 | endfunction 427 | 428 | " FUNCTION: s:jumpToSibling(currentNode, forward) {{{2 429 | " moves the cursor to the sibling of the current node in the given direction 430 | " 431 | " Args: 432 | " forward: 1 if the cursor should move to the next sibling, 0 if it should 433 | " move back to the previous sibling 434 | function! s:jumpToSibling(currentNode, forward) 435 | let sibling = a:currentNode.findSibling(a:forward) 436 | 437 | if !empty(sibling) 438 | call sibling.putCursorHere(1, 0) 439 | call b:NERDTree.ui.centerView() 440 | endif 441 | endfunction 442 | 443 | " FUNCTION: nerdtree#ui_glue#openBookmark(name) {{{1 444 | " put the cursor on the given bookmark and, if its a file, open it 445 | function! nerdtree#ui_glue#openBookmark(name) 446 | try 447 | let targetNode = g:NERDTreeBookmark.GetNodeForName(a:name, 0, b:NERDTree) 448 | call targetNode.putCursorHere(0, 1) 449 | redraw! 450 | catch /^NERDTree.BookmarkedNodeNotFoundError/ 451 | call nerdtree#echo("note - target node is not cached") 452 | let bookmark = g:NERDTreeBookmark.BookmarkFor(a:name) 453 | let targetNode = g:NERDTreeFileNode.New(bookmark.path, b:NERDTree) 454 | endtry 455 | if targetNode.path.isDirectory 456 | call targetNode.openExplorer() 457 | else 458 | call targetNode.open({'where': 'p'}) 459 | endif 460 | endfunction 461 | 462 | " FUNCTION: s:openHSplit(target) {{{1 463 | function! s:openHSplit(target) 464 | call a:target.activate({'where': 'h'}) 465 | endfunction 466 | 467 | " FUNCTION: s:openVSplit(target) {{{1 468 | function! s:openVSplit(target) 469 | call a:target.activate({'where': 'v'}) 470 | endfunction 471 | 472 | " FUNCTION: s:openExplorer(node) {{{1 473 | function! s:openExplorer(node) 474 | call a:node.openExplorer() 475 | endfunction 476 | 477 | " FUNCTION: s:openInNewTab(target) {{{1 478 | function! s:openInNewTab(target) 479 | call a:target.activate({'where': 't'}) 480 | endfunction 481 | 482 | " FUNCTION: s:openInNewTabSilent(target) {{{1 483 | function! s:openInNewTabSilent(target) 484 | call a:target.activate({'where': 't', 'stay': 1}) 485 | endfunction 486 | 487 | " FUNCTION: s:openNodeRecursively(node) {{{1 488 | function! s:openNodeRecursively(node) 489 | call nerdtree#echo("Recursively opening node. Please wait...") 490 | call a:node.openRecursively() 491 | call b:NERDTree.render() 492 | redraw 493 | call nerdtree#echo("Recursively opening node. Please wait... DONE") 494 | endfunction 495 | 496 | "FUNCTION: s:previewNodeCurrent(node) {{{1 497 | function! s:previewNodeCurrent(node) 498 | call a:node.open({'stay': 1, 'where': 'p', 'keepopen': 1}) 499 | endfunction 500 | 501 | "FUNCTION: s:previewNodeHSplit(node) {{{1 502 | function! s:previewNodeHSplit(node) 503 | call a:node.open({'stay': 1, 'where': 'h', 'keepopen': 1}) 504 | endfunction 505 | 506 | "FUNCTION: s:previewNodeVSplit(node) {{{1 507 | function! s:previewNodeVSplit(node) 508 | call a:node.open({'stay': 1, 'where': 'v', 'keepopen': 1}) 509 | endfunction 510 | 511 | " FUNCTION: nerdtree#ui_glue#revealBookmark(name) {{{1 512 | " put the cursor on the node associate with the given name 513 | function! nerdtree#ui_glue#revealBookmark(name) 514 | try 515 | let targetNode = g:NERDTreeBookmark.GetNodeForName(a:name, 0, b:NERDTree) 516 | call targetNode.putCursorHere(0, 1) 517 | catch /^NERDTree.BookmarkNotFoundError/ 518 | call nerdtree#echo("Bookmark isnt cached under the current root") 519 | endtry 520 | endfunction 521 | 522 | " FUNCTION: s:refreshRoot() {{{1 523 | " Reloads the current root. All nodes below this will be lost and the root dir 524 | " will be reloaded. 525 | function! s:refreshRoot() 526 | call nerdtree#echo("Refreshing the root node. This could take a while...") 527 | call b:NERDTree.root.refresh() 528 | call b:NERDTree.render() 529 | redraw 530 | call nerdtree#echo("Refreshing the root node. This could take a while... DONE") 531 | endfunction 532 | 533 | " FUNCTION: s:refreshCurrent(node) {{{1 534 | " refreshes the root for the current node 535 | function! s:refreshCurrent(node) 536 | let node = a:node 537 | if !node.path.isDirectory 538 | let node = node.parent 539 | endif 540 | 541 | call nerdtree#echo("Refreshing node. This could take a while...") 542 | call node.refresh() 543 | call b:NERDTree.render() 544 | redraw 545 | call nerdtree#echo("Refreshing node. This could take a while... DONE") 546 | endfunction 547 | 548 | " FUNCTION: nerdtree#ui_glue#setupCommands() {{{1 549 | function! nerdtree#ui_glue#setupCommands() 550 | command! -n=? -complete=dir -bar NERDTree :call g:NERDTreeCreator.CreateTabTree('') 551 | command! -n=? -complete=dir -bar NERDTreeToggle :call g:NERDTreeCreator.ToggleTabTree('') 552 | command! -n=0 -bar NERDTreeClose :call g:NERDTree.Close() 553 | command! -n=1 -complete=customlist,nerdtree#completeBookmarks -bar NERDTreeFromBookmark call g:NERDTreeCreator.CreateTabTree('') 554 | command! -n=0 -bar NERDTreeMirror call g:NERDTreeCreator.CreateMirror() 555 | command! -n=0 -bar NERDTreeFind call s:findAndRevealPath() 556 | command! -n=0 -bar NERDTreeFocus call NERDTreeFocus() 557 | command! -n=0 -bar NERDTreeCWD call NERDTreeCWD() 558 | endfunction 559 | 560 | " Function: s:SID() {{{1 561 | function s:SID() 562 | if !exists("s:sid") 563 | let s:sid = matchstr(expand(''), '\zs\d\+\ze_SID$') 564 | endif 565 | return s:sid 566 | endfun 567 | 568 | " FUNCTION: s:showMenu(node) {{{1 569 | function! s:showMenu(node) 570 | let mc = g:NERDTreeMenuController.New(g:NERDTreeMenuItem.AllEnabled()) 571 | call mc.showMenu() 572 | endfunction 573 | 574 | " FUNCTION: s:toggleIgnoreFilter() {{{1 575 | function! s:toggleIgnoreFilter() 576 | call b:NERDTree.ui.toggleIgnoreFilter() 577 | endfunction 578 | 579 | " FUNCTION: s:toggleShowBookmarks() {{{1 580 | function! s:toggleShowBookmarks() 581 | call b:NERDTree.ui.toggleShowBookmarks() 582 | endfunction 583 | 584 | " FUNCTION: s:toggleShowFiles() {{{1 585 | function! s:toggleShowFiles() 586 | call b:NERDTree.ui.toggleShowFiles() 587 | endfunction 588 | 589 | " FUNCTION: s:toggleShowHidden() {{{1 590 | " toggles the display of hidden files 591 | function! s:toggleShowHidden() 592 | call b:NERDTree.ui.toggleShowHidden() 593 | endfunction 594 | 595 | " FUNCTION: s:toggleZoom() {{{1 596 | function! s:toggleZoom() 597 | call b:NERDTree.ui.toggleZoom() 598 | endfunction 599 | 600 | "FUNCTION: nerdtree#ui_glue#upDir(keepState) {{{1 601 | "moves the tree up a level 602 | " 603 | "Args: 604 | "keepState: 1 if the current root should be left open when the tree is 605 | "re-rendered 606 | function! nerdtree#ui_glue#upDir(keepState) 607 | let cwd = b:NERDTree.root.path.str({'format': 'UI'}) 608 | if cwd ==# "/" || cwd =~# '^[^/]..$' 609 | call nerdtree#echo("already at top dir") 610 | else 611 | if !a:keepState 612 | call b:NERDTree.root.close() 613 | endif 614 | 615 | let oldRoot = b:NERDTree.root 616 | 617 | if empty(b:NERDTree.root.parent) 618 | let path = b:NERDTree.root.path.getParent() 619 | let newRoot = g:NERDTreeDirNode.New(path, b:NERDTree) 620 | call newRoot.open() 621 | call newRoot.transplantChild(b:NERDTree.root) 622 | let b:NERDTree.root = newRoot 623 | else 624 | let b:NERDTree.root = b:NERDTree.root.parent 625 | endif 626 | 627 | if g:NERDTreeChDirMode ==# 2 628 | call b:NERDTree.root.path.changeToDir() 629 | endif 630 | 631 | call b:NERDTree.render() 632 | call oldRoot.putCursorHere(0, 0) 633 | endif 634 | endfunction 635 | 636 | " FUNCTION: s:upDirCurrentRootOpen() {{{1 637 | function! s:upDirCurrentRootOpen() 638 | call nerdtree#ui_glue#upDir(1) 639 | endfunction 640 | 641 | " FUNCTION: s:upDirCurrentRootClosed() {{{1 642 | function! s:upDirCurrentRootClosed() 643 | call nerdtree#ui_glue#upDir(0) 644 | endfunction 645 | 646 | " vim: set sw=4 sts=4 et fdm=marker: 647 | --------------------------------------------------------------------------------