" === plugin configuration variables === {{{
"
" open NERDTree on gvim/macvim startup
if !exists('g:nerdtree_tabs_open_on_gui_startup')
  let g:nerdtree_tabs_open_on_gui_startup = 1
endif

" open NERDTree on console vim startup (off by default)
if !exists('g:nerdtree_tabs_open_on_console_startup')
  let g:nerdtree_tabs_open_on_console_startup = 0
endif

" do not open NERDTree if vim starts in diff mode
if !exists('g:nerdtree_tabs_no_startup_for_diff')
    let g:nerdtree_tabs_no_startup_for_diff = 1
endif

" On startup - focus NERDTree when opening a directory, focus the file if
" editing a specified file. When set to `2`, always focus file after startup.
if !exists('g:nerdtree_tabs_smart_startup_focus')
  let g:nerdtree_tabs_smart_startup_focus = 1
endif

" Open NERDTree on new tab creation if NERDTree was globally opened
" by :NERDTreeTabsToggle
if !exists('g:nerdtree_tabs_open_on_new_tab')
  let g:nerdtree_tabs_open_on_new_tab = 1
endif

" unfocus NERDTree when leaving a tab so that you have descriptive tab names
" and not names like 'NERD_tree_1'
if !exists('g:nerdtree_tabs_meaningful_tab_names')
  let g:nerdtree_tabs_meaningful_tab_names = 1
endif

" close current tab if there is only one window in it and it's NERDTree
if !exists('g:nerdtree_tabs_autoclose')
  let g:nerdtree_tabs_autoclose = 1
endif

" synchronize view of all NERDTree windows (scroll and cursor position)
if !exists('g:nerdtree_tabs_synchronize_view')
  let g:nerdtree_tabs_synchronize_view = 1
endif

" synchronize focus when switching tabs (focus NERDTree after tab switch
" if and only if it was focused before tab switch)
if !exists('g:nerdtree_tabs_synchronize_focus')
  let g:nerdtree_tabs_synchronize_focus = 1
endif

" when switching into a tab, make sure that focus will always be in file
" editing window, not in NERDTree window (off by default)
if !exists('g:nerdtree_tabs_focus_on_files')
  let g:nerdtree_tabs_focus_on_files = 0
endif

" when starting up with a directory name as a parameter, cd into it
if !exists('g:nerdtree_tabs_startup_cd')
  let g:nerdtree_tabs_startup_cd = 1
endif
"
" }}}
" === plugin mappings === {{{
"
noremap <silent> <script> <Plug>NERDTreeTabsOpen     :call <SID>NERDTreeOpenAllTabs()
noremap <silent> <script> <Plug>NERDTreeTabsClose    :call <SID>NERDTreeCloseAllTabs()
noremap <silent> <script> <Plug>NERDTreeTabsToggle   :call <SID>NERDTreeToggleAllTabs()
noremap <silent> <script> <Plug>NERDTreeMirrorOpen   :call <SID>NERDTreeMirrorOrCreate()
noremap <silent> <script> <Plug>NERDTreeMirrorToggle :call <SID>NERDTreeMirrorToggle()
noremap <silent> <script> <Plug>NERDTreeSteppedOpen  :call <SID>NERDTreeSteppedOpen()
noremap <silent> <script> <Plug>NERDTreeSteppedClose :call <SID>NERDTreeSteppedClose()
noremap <silent> <script> <Plug>NERDTreeFocusToggle :call <SID>NERDTreeFocusToggle()
"
" }}}
" === plugin commands === {{{
"
command! NERDTreeTabsOpen     call <SID>NERDTreeOpenAllTabs()
command! NERDTreeTabsClose    call <SID>NERDTreeCloseAllTabs()
command! NERDTreeTabsToggle   call <SID>NERDTreeToggleAllTabs()
command! NERDTreeMirrorOpen   call <SID>NERDTreeMirrorOrCreate()
command! NERDTreeMirrorToggle call <SID>NERDTreeMirrorToggle()
command! NERDTreeSteppedOpen  call <SID>NERDTreeSteppedOpen()
command! NERDTreeSteppedClose call <SID>NERDTreeSteppedClose()
command! NERDTreeFocusToggle  call <SID>NERDTreeFocusToggle()
"
" }}}
" === plugin functions === {{{
"
" === NERDTree manipulation (opening, closing etc.) === {{{
"
" s:NERDTreeMirrorOrCreate() {{{
"
" switch NERDTree on for current tab -- mirror it if possible, otherwise create it
fun! s:NERDTreeMirrorOrCreate()
  let l:nerdtree_open = s:IsNERDTreeOpenInCurrentTab()

  " if NERDTree is not active in the current tab, try to mirror it
  if !l:nerdtree_open
    let l:previous_winnr = winnr("$")

    silent NERDTreeMirror

    " if the window count of current tab didn't increase after NERDTreeMirror,
    " it means NERDTreeMirror was unsuccessful and a new NERDTree has to be created
    if l:previous_winnr == winnr("$")
      silent NERDTreeToggle
    endif
  endif
endfun

" }}}
" s:NERDTreeMirrorToggle() {{{
"
" toggle NERDTree in current tab, use mirror if possible
fun! s:NERDTreeMirrorToggle()
  let l:nerdtree_open = s:IsNERDTreeOpenInCurrentTab()

  if l:nerdtree_open
    silent NERDTreeClose
  else
    call s:NERDTreeMirrorOrCreate()
  endif
endfun

" }}}
" s:NERDTreeOpenAllTabs() {{{
"
" switch NERDTree on for all tabs while making sure there is only one NERDTree buffer
fun! s:NERDTreeOpenAllTabs()
  let s:nerdtree_globally_active = 1

  " tabdo doesn't preserve current tab - save it and restore it afterwards
  let l:current_tab = tabpagenr()
  tabdo call s:NERDTreeMirrorOrCreate()
  exe 'tabn ' . l:current_tab
endfun

" }}}
" s:NERDTreeCloseAllTabs() {{{
"
" close NERDTree across all tabs
fun! s:NERDTreeCloseAllTabs()
  let s:nerdtree_globally_active = 0

  " tabdo doesn't preserve current tab - save it and restore it afterwards
  let l:current_tab = tabpagenr()
  tabdo silent NERDTreeClose
  exe 'tabn ' . l:current_tab
endfun

" }}}
" s:NERDTreeToggleAllTabs() {{{
"
" toggle NERDTree in current tab and match the state in all other tabs
fun! s:NERDTreeToggleAllTabs()
  let l:nerdtree_open = s:IsNERDTreeOpenInCurrentTab()
  let s:disable_handlers_for_tabdo = 1

  if l:nerdtree_open
    call s:NERDTreeCloseAllTabs()
  else
    call s:NERDTreeOpenAllTabs()
    " force focus to NERDTree in current tab
    if exists("t:NERDTreeBufName") && bufwinnr(t:NERDTreeBufName) != -1
      exe bufwinnr(t:NERDTreeBufName) . "wincmd w"
    endif
  endif

  let s:disable_handlers_for_tabdo = 0
endfun

" }}}
" s:NERDTreeSteppedOpen() {{{
"
" focus the NERDTree view, creating one first if none is present
fun! s:NERDTreeSteppedOpen()
  if !s:IsCurrentWindowNERDTree()
    if s:IsNERDTreeOpenInCurrentTab()
      call s:NERDTreeFocus()
    else
      call s:NERDTreeMirrorOrCreate()
    endif
  endif
endfun

" }}}
" s:NERDTreeSteppedClose{() {{{
"
" unfocus the NERDTree view or closes it if it hadn't had focus at the time of
" the call
fun! s:NERDTreeSteppedClose()
  if s:IsCurrentWindowNERDTree()
    call s:NERDTreeUnfocus()
  else
    let l:nerdtree_open = s:IsNERDTreeOpenInCurrentTab()

    if l:nerdtree_open
      silent NERDTreeClose
    endif
  endif
endfun

" }}}
" s:NERDTreeFocusToggle() {{{
"
" focus the NERDTree view or creates it if in a file, 
" or unfocus NERDTree view if in NERDTree
fun! s:NERDTreeFocusToggle()
  let s:disable_handlers_for_tabdo = 1
  if s:IsCurrentWindowNERDTree()
    call s:NERDTreeUnfocus()
  else
    if !s:IsNERDTreeOpenInCurrentTab()
      call s:NERDTreeOpenAllTabs()
    endif
    call s:NERDTreeFocus()
  endif
  let s:disable_handlers_for_tabdo = 0
endfun
" }}}
"
" === NERDTree manipulation (opening, closing etc.) === }}}
" === focus functions === {{{
"
" s:NERDTreeFocus() {{{
"
" if the current window is NERDTree, move focus to the next window
fun! s:NERDTreeFocus()
  if !s:IsCurrentWindowNERDTree() && exists("t:NERDTreeBufName") && bufwinnr(t:NERDTreeBufName) != -1
    exe bufwinnr(t:NERDTreeBufName) . "wincmd w"
  endif
endfun

" }}}
" s:NERDTreeUnfocus() {{{
"
" if the current window is NERDTree, move focus to the next window
fun! s:NERDTreeUnfocus()
  " save current window so that it's focus can be restored after switching
  " back to this tab
  let t:NERDTreeTabLastWindow = winnr()
  if s:IsCurrentWindowNERDTree()
    let l:winNum = s:NextNormalWindow()
    if l:winNum != -1
      exec l:winNum.'wincmd w'
    else
      wincmd w
    endif
  endif
endfun

" }}}
" s:NERDTreeRestoreFocus() {{{
"
" restore focus to the window that was focused before leaving current tab
fun! s:NERDTreeRestoreFocus()
  if g:nerdtree_tabs_synchronize_focus
    if s:is_nerdtree_globally_focused
      call s:NERDTreeFocus()
    elseif exists("t:NERDTreeTabLastWindow") && exists("t:NERDTreeBufName") && t:NERDTreeTabLastWindow != bufwinnr(t:NERDTreeBufName)
      exe t:NERDTreeTabLastWindow . "wincmd w"
    endif
  elseif exists("t:NERDTreeTabLastWindow")
    exe t:NERDTreeTabLastWindow . "wincmd w"
  endif
endfun

" }}}
" s:SaveGlobalFocus() {{{
"
fun! s:SaveGlobalFocus()
  let s:is_nerdtree_globally_focused = s:IsCurrentWindowNERDTree()
endfun

" }}}
" s:IfFocusOnStartup() {{{
"
fun! s:IfFocusOnStartup()
  return strlen(bufname('$')) == 0 || !getbufvar('$', '&modifiable')
endfun

" }}}
"
" === focus functions === }}}
" === utility functions === {{{
"
" s:NextNormalWindow() {{{
"
" find next window with a normal buffer
fun! s:NextNormalWindow()
  let l:i = 1
  while(l:i <= winnr('$'))
    let l:buf = winbufnr(l:i)

    " skip unlisted buffers
    if buflisted(l:buf) == 0
      let l:i = l:i + 1
      continue
    endif

    " skip un-modifiable buffers
    if getbufvar(l:buf, '&modifiable') != 1
      let l:i = l:i + 1
      continue
    endif

    " skip temporary buffers with buftype set
    if empty(getbufvar(l:buf, "&buftype")) != 1
      let l:i = l:i + 1
      continue
    endif

    return l:i
  endwhile
  return -1
endfun

" }}}
" s:CloseIfOnlyNerdTreeLeft() {{{
"
" Close all open buffers on entering a window if the only
" buffer that's left is the NERDTree buffer
fun! s:CloseIfOnlyNerdTreeLeft()
  if exists("t:NERDTreeBufName") && bufwinnr(t:NERDTreeBufName) != -1 && winnr("$") == 1
    q
  endif
endfun

" }}}
" s:IsCurrentWindowNERDTree() {{{
"
" returns 1 if current window is NERDTree, false otherwise
fun! s:IsCurrentWindowNERDTree()
  return exists("t:NERDTreeBufName") && bufwinnr(t:NERDTreeBufName) == winnr()
endfun

" }}}
" s:IsNERDTreeOpenInCurrentTab() {{{
"
" check if NERDTree is open in current tab
fun! s:IsNERDTreeOpenInCurrentTab()
  return exists("t:NERDTreeBufName") && bufwinnr(t:NERDTreeBufName) != -1
endfun

" }}}
" s:IsNERDTreePresentInCurrentTab() {{{
"
" check if NERDTree is present in current tab (not necessarily visible)
fun! s:IsNERDTreePresentInCurrentTab()
  return exists("t:NERDTreeBufName")
endfun

" }}}
"
" === utility functions === }}}
" === NERDTree view manipulation (scroll and cursor positions) === {{{
"
" s:SaveNERDTreeViewIfPossible() {{{
"
fun! s:SaveNERDTreeViewIfPossible()
  if exists("t:NERDTreeBufName") && bufwinnr(t:NERDTreeBufName) == winnr()
    " save scroll and cursor etc.
    let s:nerdtree_view = winsaveview()

    " save NERDTree window width
    let s:nerdtree_width = winwidth(winnr())

    " save buffer name (to be able to correct desync by commands spawning
    " a new NERDTree instance)
    let s:nerdtree_buffer = bufname("%")
  endif
endfun

" }}}
" s:RestoreNERDTreeViewIfPossible() {{{
"
fun! s:RestoreNERDTreeViewIfPossible()
  " if nerdtree exists in current tab, it is the current window and if saved
  " state is available, restore it
  let l:view_state_saved = exists('s:nerdtree_view') && exists('s:nerdtree_width')
  if s:IsNERDTreeOpenInCurrentTab() && l:view_state_saved
    let l:current_winnr = winnr()
    let l:nerdtree_winnr = bufwinnr(t:NERDTreeBufName)

    " switch to NERDTree window
    exe l:nerdtree_winnr . "wincmd w"
    " load the correct NERDTree buffer if not already loaded
    if exists('s:nerdtree_buffer') && t:NERDTreeBufName != s:nerdtree_buffer
      silent NERDTreeClose
      silent NERDTreeMirror
    endif
    " restore cursor, scroll and window width
    call winrestview(s:nerdtree_view)
    exe "vertical resize " . s:nerdtree_width

    " switch back to whatever window was focused before
    exe l:current_winnr . "wincmd w"
  endif
endfun

" }}}
"
" === NERDTree view manipulation (scroll and cursor positions) === }}}
"
" === plugin functions ===  }}}
" === plugin event handlers === {{{
"
" s:LoadPlugin() {{{
"
fun! s:LoadPlugin()
  if exists('g:nerdtree_tabs_loaded')
    return
  endif

  let g:NERDTreeHijackNetrw = 0

  let s:disable_handlers_for_tabdo = 0

  " global on/off NERDTree state
  " the exists check is to enable script reloading without resetting the state
  if !exists('s:nerdtree_globally_active')
    let s:nerdtree_globally_active = 0
  endif

  " global focused/unfocused NERDTree state
  " the exists check is to enable script reloading without resetting the state
  if !exists('s:is_nerdtree_globally_focused')
    call s:SaveGlobalFocus()
  end

  augroup NERDTreeTabs
    autocmd!
    autocmd VimEnter * call <SID>VimEnterHandler()
    autocmd TabEnter * call <SID>TabEnterHandler()
    autocmd TabLeave * call <SID>TabLeaveHandler()
    autocmd WinEnter * call <SID>WinEnterHandler()
    autocmd WinLeave * call <SID>WinLeaveHandler()
    autocmd BufWinEnter * call <SID>BufWinEnterHandler()
  augroup END

  let g:nerdtree_tabs_loaded = 1
endfun

" }}}
" s:VimEnterHandler() {{{
"
fun! s:VimEnterHandler()
  " if the argument to vim is a directory, cd into it
  if g:nerdtree_tabs_startup_cd && isdirectory(argv(0))
    exe 'cd ' . escape(argv(0), '\ ')
  endif

  let l:open_nerd_tree_on_startup = (g:nerdtree_tabs_open_on_console_startup && !has('gui_running')) ||
                                  \ (g:nerdtree_tabs_open_on_gui_startup && has('gui_running'))

  if g:nerdtree_tabs_no_startup_for_diff && &diff
      let l:open_nerd_tree_on_startup = 0
  endif

  " this makes sure that globally_active is true when using 'gvim .'
  let s:nerdtree_globally_active = l:open_nerd_tree_on_startup

  if l:open_nerd_tree_on_startup
    let l:focus_file = !s:IfFocusOnStartup()
    let l:main_bufnr = bufnr('%')

    if !s:IsNERDTreePresentInCurrentTab()
      call s:NERDTreeOpenAllTabs()
    endif

    if (l:focus_file && g:nerdtree_tabs_smart_startup_focus == 1) || g:nerdtree_tabs_smart_startup_focus == 2
      exe bufwinnr(l:main_bufnr) . "wincmd w"
    endif
  endif
endfun

" }}} s:NewTabCreated {{{
"
" A flag to indicate that a new tab has just been created.
"
" We will handle the remaining work for this newly created tab separately in
" BufWinEnter event.
"
let s:NewTabCreated = 0

" }}}
" s:TabEnterHandler() {{{
"
fun! s:TabEnterHandler()
  if s:disable_handlers_for_tabdo
    return
  endif

  if g:nerdtree_tabs_open_on_new_tab && s:nerdtree_globally_active && !s:IsNERDTreeOpenInCurrentTab()
    call s:NERDTreeMirrorOrCreate()

    " move focus to the previous window
    wincmd p

    " Turn on the 'NewTabCreated' flag
    let s:NewTabCreated = 1
  endif

  if g:nerdtree_tabs_synchronize_view
    call s:RestoreNERDTreeViewIfPossible()
  endif

  if g:nerdtree_tabs_focus_on_files
    call s:NERDTreeUnfocus()
  " Do not restore focus on newly created tab here
  elseif !s:NewTabCreated
    call s:NERDTreeRestoreFocus()
  endif
endfun

" }}}
" s:TabLeaveHandler() {{{
"
fun! s:TabLeaveHandler()
  if g:nerdtree_tabs_meaningful_tab_names
    call s:SaveGlobalFocus()
    call s:NERDTreeUnfocus()
  endif
endfun

" }}}
" s:WinEnterHandler() {{{
"
fun! s:WinEnterHandler()
  if s:disable_handlers_for_tabdo
    return
  endif

  if g:nerdtree_tabs_autoclose
    call s:CloseIfOnlyNerdTreeLeft()
  endif
endfun

" }}}
" s:WinLeaveHandler() {{{
"
fun! s:WinLeaveHandler()
  if s:disable_handlers_for_tabdo
    return
  endif

  if g:nerdtree_tabs_synchronize_view
    call s:SaveNERDTreeViewIfPossible()
  endif
endfun

" }}}
" s:BufWinEnterHandler() {{{
"
" BufWinEnter event only gets triggered after a new buffer has been
" successfully loaded, it is a proper time to finish the remaining
" work for newly opened tab.
"
fun! s:BufWinEnterHandler()
  if s:NewTabCreated
    " Turn off the 'NewTabCreated' flag
    let s:NewTabCreated = 0

    " Restore focus to NERDTree if necessary
    if !g:nerdtree_tabs_focus_on_files
      call s:NERDTreeRestoreFocus()
    endif
  endif
endfun

" }}}
"
" === plugin event handlers === }}}

call s:LoadPlugin()