vim-nerdtree-tabs.vim 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  1. " === plugin configuration variables === {{{
  2. "
  3. " open NERDTree on gvim/macvim startup
  4. if !exists('g:nerdtree_tabs_open_on_gui_startup')
  5. let g:nerdtree_tabs_open_on_gui_startup = 1
  6. endif
  7. " open NERDTree on console vim startup (off by default)
  8. if !exists('g:nerdtree_tabs_open_on_console_startup')
  9. let g:nerdtree_tabs_open_on_console_startup = 0
  10. endif
  11. " do not open NERDTree if vim starts in diff mode
  12. if !exists('g:nerdtree_tabs_no_startup_for_diff')
  13. let g:nerdtree_tabs_no_startup_for_diff = 1
  14. endif
  15. " On startup - focus NERDTree when opening a directory, focus the file if
  16. " editing a specified file. When set to `2`, always focus file after startup.
  17. if !exists('g:nerdtree_tabs_smart_startup_focus')
  18. let g:nerdtree_tabs_smart_startup_focus = 1
  19. endif
  20. " Open NERDTree on new tab creation if NERDTree was globally opened
  21. " by :NERDTreeTabsToggle
  22. if !exists('g:nerdtree_tabs_open_on_new_tab')
  23. let g:nerdtree_tabs_open_on_new_tab = 1
  24. endif
  25. " unfocus NERDTree when leaving a tab so that you have descriptive tab names
  26. " and not names like 'NERD_tree_1'
  27. if !exists('g:nerdtree_tabs_meaningful_tab_names')
  28. let g:nerdtree_tabs_meaningful_tab_names = 1
  29. endif
  30. " close current tab if there is only one window in it and it's NERDTree
  31. if !exists('g:nerdtree_tabs_autoclose')
  32. let g:nerdtree_tabs_autoclose = 1
  33. endif
  34. " synchronize view of all NERDTree windows (scroll and cursor position)
  35. if !exists('g:nerdtree_tabs_synchronize_view')
  36. let g:nerdtree_tabs_synchronize_view = 1
  37. endif
  38. " synchronize focus when switching tabs (focus NERDTree after tab switch
  39. " if and only if it was focused before tab switch)
  40. if !exists('g:nerdtree_tabs_synchronize_focus')
  41. let g:nerdtree_tabs_synchronize_focus = 1
  42. endif
  43. " when switching into a tab, make sure that focus will always be in file
  44. " editing window, not in NERDTree window (off by default)
  45. if !exists('g:nerdtree_tabs_focus_on_files')
  46. let g:nerdtree_tabs_focus_on_files = 0
  47. endif
  48. " when starting up with a directory name as a parameter, cd into it
  49. if !exists('g:nerdtree_tabs_startup_cd')
  50. let g:nerdtree_tabs_startup_cd = 1
  51. endif
  52. "
  53. " }}}
  54. " === plugin mappings === {{{
  55. "
  56. noremap <silent> <script> <Plug>NERDTreeTabsOpen :call <SID>NERDTreeOpenAllTabs()
  57. noremap <silent> <script> <Plug>NERDTreeTabsClose :call <SID>NERDTreeCloseAllTabs()
  58. noremap <silent> <script> <Plug>NERDTreeTabsToggle :call <SID>NERDTreeToggleAllTabs()
  59. noremap <silent> <script> <Plug>NERDTreeMirrorOpen :call <SID>NERDTreeMirrorOrCreate()
  60. noremap <silent> <script> <Plug>NERDTreeMirrorToggle :call <SID>NERDTreeMirrorToggle()
  61. noremap <silent> <script> <Plug>NERDTreeSteppedOpen :call <SID>NERDTreeSteppedOpen()
  62. noremap <silent> <script> <Plug>NERDTreeSteppedClose :call <SID>NERDTreeSteppedClose()
  63. noremap <silent> <script> <Plug>NERDTreeFocusToggle :call <SID>NERDTreeFocusToggle()
  64. "
  65. " }}}
  66. " === plugin commands === {{{
  67. "
  68. command! NERDTreeTabsOpen call <SID>NERDTreeOpenAllTabs()
  69. command! NERDTreeTabsClose call <SID>NERDTreeCloseAllTabs()
  70. command! NERDTreeTabsToggle call <SID>NERDTreeToggleAllTabs()
  71. command! NERDTreeMirrorOpen call <SID>NERDTreeMirrorOrCreate()
  72. command! NERDTreeMirrorToggle call <SID>NERDTreeMirrorToggle()
  73. command! NERDTreeSteppedOpen call <SID>NERDTreeSteppedOpen()
  74. command! NERDTreeSteppedClose call <SID>NERDTreeSteppedClose()
  75. command! NERDTreeFocusToggle call <SID>NERDTreeFocusToggle()
  76. "
  77. " }}}
  78. " === plugin functions === {{{
  79. "
  80. " === NERDTree manipulation (opening, closing etc.) === {{{
  81. "
  82. " s:NERDTreeMirrorOrCreate() {{{
  83. "
  84. " switch NERDTree on for current tab -- mirror it if possible, otherwise create it
  85. fun! s:NERDTreeMirrorOrCreate()
  86. let l:nerdtree_open = s:IsNERDTreeOpenInCurrentTab()
  87. " if NERDTree is not active in the current tab, try to mirror it
  88. if !l:nerdtree_open
  89. let l:previous_winnr = winnr("$")
  90. silent NERDTreeMirror
  91. " if the window count of current tab didn't increase after NERDTreeMirror,
  92. " it means NERDTreeMirror was unsuccessful and a new NERDTree has to be created
  93. if l:previous_winnr == winnr("$")
  94. silent NERDTreeToggle
  95. endif
  96. endif
  97. endfun
  98. " }}}
  99. " s:NERDTreeMirrorToggle() {{{
  100. "
  101. " toggle NERDTree in current tab, use mirror if possible
  102. fun! s:NERDTreeMirrorToggle()
  103. let l:nerdtree_open = s:IsNERDTreeOpenInCurrentTab()
  104. if l:nerdtree_open
  105. silent NERDTreeClose
  106. else
  107. call s:NERDTreeMirrorOrCreate()
  108. endif
  109. endfun
  110. " }}}
  111. " s:NERDTreeOpenAllTabs() {{{
  112. "
  113. " switch NERDTree on for all tabs while making sure there is only one NERDTree buffer
  114. fun! s:NERDTreeOpenAllTabs()
  115. let s:nerdtree_globally_active = 1
  116. " tabdo doesn't preserve current tab - save it and restore it afterwards
  117. let l:current_tab = tabpagenr()
  118. tabdo call s:NERDTreeMirrorOrCreate()
  119. exe 'tabn ' . l:current_tab
  120. endfun
  121. " }}}
  122. " s:NERDTreeCloseAllTabs() {{{
  123. "
  124. " close NERDTree across all tabs
  125. fun! s:NERDTreeCloseAllTabs()
  126. let s:nerdtree_globally_active = 0
  127. " tabdo doesn't preserve current tab - save it and restore it afterwards
  128. let l:current_tab = tabpagenr()
  129. tabdo silent NERDTreeClose
  130. exe 'tabn ' . l:current_tab
  131. endfun
  132. " }}}
  133. " s:NERDTreeToggleAllTabs() {{{
  134. "
  135. " toggle NERDTree in current tab and match the state in all other tabs
  136. fun! s:NERDTreeToggleAllTabs()
  137. let l:nerdtree_open = s:IsNERDTreeOpenInCurrentTab()
  138. let s:disable_handlers_for_tabdo = 1
  139. if l:nerdtree_open
  140. call s:NERDTreeCloseAllTabs()
  141. else
  142. call s:NERDTreeOpenAllTabs()
  143. " force focus to NERDTree in current tab
  144. if exists("t:NERDTreeBufName") && bufwinnr(t:NERDTreeBufName) != -1
  145. exe bufwinnr(t:NERDTreeBufName) . "wincmd w"
  146. endif
  147. endif
  148. let s:disable_handlers_for_tabdo = 0
  149. endfun
  150. " }}}
  151. " s:NERDTreeSteppedOpen() {{{
  152. "
  153. " focus the NERDTree view, creating one first if none is present
  154. fun! s:NERDTreeSteppedOpen()
  155. if !s:IsCurrentWindowNERDTree()
  156. if s:IsNERDTreeOpenInCurrentTab()
  157. call s:NERDTreeFocus()
  158. else
  159. call s:NERDTreeMirrorOrCreate()
  160. endif
  161. endif
  162. endfun
  163. " }}}
  164. " s:NERDTreeSteppedClose{() {{{
  165. "
  166. " unfocus the NERDTree view or closes it if it hadn't had focus at the time of
  167. " the call
  168. fun! s:NERDTreeSteppedClose()
  169. if s:IsCurrentWindowNERDTree()
  170. call s:NERDTreeUnfocus()
  171. else
  172. let l:nerdtree_open = s:IsNERDTreeOpenInCurrentTab()
  173. if l:nerdtree_open
  174. silent NERDTreeClose
  175. endif
  176. endif
  177. endfun
  178. " }}}
  179. " s:NERDTreeFocusToggle() {{{
  180. "
  181. " focus the NERDTree view or creates it if in a file,
  182. " or unfocus NERDTree view if in NERDTree
  183. fun! s:NERDTreeFocusToggle()
  184. let s:disable_handlers_for_tabdo = 1
  185. if s:IsCurrentWindowNERDTree()
  186. call s:NERDTreeUnfocus()
  187. else
  188. if !s:IsNERDTreeOpenInCurrentTab()
  189. call s:NERDTreeOpenAllTabs()
  190. endif
  191. call s:NERDTreeFocus()
  192. endif
  193. let s:disable_handlers_for_tabdo = 0
  194. endfun
  195. " }}}
  196. "
  197. " === NERDTree manipulation (opening, closing etc.) === }}}
  198. " === focus functions === {{{
  199. "
  200. " s:NERDTreeFocus() {{{
  201. "
  202. " if the current window is NERDTree, move focus to the next window
  203. fun! s:NERDTreeFocus()
  204. if !s:IsCurrentWindowNERDTree() && exists("t:NERDTreeBufName") && bufwinnr(t:NERDTreeBufName) != -1
  205. exe bufwinnr(t:NERDTreeBufName) . "wincmd w"
  206. endif
  207. endfun
  208. " }}}
  209. " s:NERDTreeUnfocus() {{{
  210. "
  211. " if the current window is NERDTree, move focus to the next window
  212. fun! s:NERDTreeUnfocus()
  213. " save current window so that it's focus can be restored after switching
  214. " back to this tab
  215. let t:NERDTreeTabLastWindow = winnr()
  216. if s:IsCurrentWindowNERDTree()
  217. let l:winNum = s:NextNormalWindow()
  218. if l:winNum != -1
  219. exec l:winNum.'wincmd w'
  220. else
  221. wincmd w
  222. endif
  223. endif
  224. endfun
  225. " }}}
  226. " s:NERDTreeRestoreFocus() {{{
  227. "
  228. " restore focus to the window that was focused before leaving current tab
  229. fun! s:NERDTreeRestoreFocus()
  230. if g:nerdtree_tabs_synchronize_focus
  231. if s:is_nerdtree_globally_focused
  232. call s:NERDTreeFocus()
  233. elseif exists("t:NERDTreeTabLastWindow") && exists("t:NERDTreeBufName") && t:NERDTreeTabLastWindow != bufwinnr(t:NERDTreeBufName)
  234. exe t:NERDTreeTabLastWindow . "wincmd w"
  235. endif
  236. elseif exists("t:NERDTreeTabLastWindow")
  237. exe t:NERDTreeTabLastWindow . "wincmd w"
  238. endif
  239. endfun
  240. " }}}
  241. " s:SaveGlobalFocus() {{{
  242. "
  243. fun! s:SaveGlobalFocus()
  244. let s:is_nerdtree_globally_focused = s:IsCurrentWindowNERDTree()
  245. endfun
  246. " }}}
  247. " s:IfFocusOnStartup() {{{
  248. "
  249. fun! s:IfFocusOnStartup()
  250. return strlen(bufname('$')) == 0 || !getbufvar('$', '&modifiable')
  251. endfun
  252. " }}}
  253. "
  254. " === focus functions === }}}
  255. " === utility functions === {{{
  256. "
  257. " s:NextNormalWindow() {{{
  258. "
  259. " find next window with a normal buffer
  260. fun! s:NextNormalWindow()
  261. let l:i = 1
  262. while(l:i <= winnr('$'))
  263. let l:buf = winbufnr(l:i)
  264. " skip unlisted buffers
  265. if buflisted(l:buf) == 0
  266. let l:i = l:i + 1
  267. continue
  268. endif
  269. " skip un-modifiable buffers
  270. if getbufvar(l:buf, '&modifiable') != 1
  271. let l:i = l:i + 1
  272. continue
  273. endif
  274. " skip temporary buffers with buftype set
  275. if empty(getbufvar(l:buf, "&buftype")) != 1
  276. let l:i = l:i + 1
  277. continue
  278. endif
  279. return l:i
  280. endwhile
  281. return -1
  282. endfun
  283. " }}}
  284. " s:CloseIfOnlyNerdTreeLeft() {{{
  285. "
  286. " Close all open buffers on entering a window if the only
  287. " buffer that's left is the NERDTree buffer
  288. fun! s:CloseIfOnlyNerdTreeLeft()
  289. if exists("t:NERDTreeBufName") && bufwinnr(t:NERDTreeBufName) != -1 && winnr("$") == 1
  290. q
  291. endif
  292. endfun
  293. " }}}
  294. " s:IsCurrentWindowNERDTree() {{{
  295. "
  296. " returns 1 if current window is NERDTree, false otherwise
  297. fun! s:IsCurrentWindowNERDTree()
  298. return exists("t:NERDTreeBufName") && bufwinnr(t:NERDTreeBufName) == winnr()
  299. endfun
  300. " }}}
  301. " s:IsNERDTreeOpenInCurrentTab() {{{
  302. "
  303. " check if NERDTree is open in current tab
  304. fun! s:IsNERDTreeOpenInCurrentTab()
  305. return exists("t:NERDTreeBufName") && bufwinnr(t:NERDTreeBufName) != -1
  306. endfun
  307. " }}}
  308. " s:IsNERDTreePresentInCurrentTab() {{{
  309. "
  310. " check if NERDTree is present in current tab (not necessarily visible)
  311. fun! s:IsNERDTreePresentInCurrentTab()
  312. return exists("t:NERDTreeBufName")
  313. endfun
  314. " }}}
  315. "
  316. " === utility functions === }}}
  317. " === NERDTree view manipulation (scroll and cursor positions) === {{{
  318. "
  319. " s:SaveNERDTreeViewIfPossible() {{{
  320. "
  321. fun! s:SaveNERDTreeViewIfPossible()
  322. if exists("t:NERDTreeBufName") && bufwinnr(t:NERDTreeBufName) == winnr()
  323. " save scroll and cursor etc.
  324. let s:nerdtree_view = winsaveview()
  325. " save NERDTree window width
  326. let s:nerdtree_width = winwidth(winnr())
  327. " save buffer name (to be able to correct desync by commands spawning
  328. " a new NERDTree instance)
  329. let s:nerdtree_buffer = bufname("%")
  330. endif
  331. endfun
  332. " }}}
  333. " s:RestoreNERDTreeViewIfPossible() {{{
  334. "
  335. fun! s:RestoreNERDTreeViewIfPossible()
  336. " if nerdtree exists in current tab, it is the current window and if saved
  337. " state is available, restore it
  338. let l:view_state_saved = exists('s:nerdtree_view') && exists('s:nerdtree_width')
  339. if s:IsNERDTreeOpenInCurrentTab() && l:view_state_saved
  340. let l:current_winnr = winnr()
  341. let l:nerdtree_winnr = bufwinnr(t:NERDTreeBufName)
  342. " switch to NERDTree window
  343. exe l:nerdtree_winnr . "wincmd w"
  344. " load the correct NERDTree buffer if not already loaded
  345. if exists('s:nerdtree_buffer') && t:NERDTreeBufName != s:nerdtree_buffer
  346. silent NERDTreeClose
  347. silent NERDTreeMirror
  348. endif
  349. " restore cursor, scroll and window width
  350. call winrestview(s:nerdtree_view)
  351. exe "vertical resize " . s:nerdtree_width
  352. " switch back to whatever window was focused before
  353. exe l:current_winnr . "wincmd w"
  354. endif
  355. endfun
  356. " }}}
  357. "
  358. " === NERDTree view manipulation (scroll and cursor positions) === }}}
  359. "
  360. " === plugin functions === }}}
  361. " === plugin event handlers === {{{
  362. "
  363. " s:LoadPlugin() {{{
  364. "
  365. fun! s:LoadPlugin()
  366. if exists('g:nerdtree_tabs_loaded')
  367. return
  368. endif
  369. let g:NERDTreeHijackNetrw = 0
  370. let s:disable_handlers_for_tabdo = 0
  371. " global on/off NERDTree state
  372. " the exists check is to enable script reloading without resetting the state
  373. if !exists('s:nerdtree_globally_active')
  374. let s:nerdtree_globally_active = 0
  375. endif
  376. " global focused/unfocused NERDTree state
  377. " the exists check is to enable script reloading without resetting the state
  378. if !exists('s:is_nerdtree_globally_focused')
  379. call s:SaveGlobalFocus()
  380. end
  381. augroup NERDTreeTabs
  382. autocmd!
  383. autocmd VimEnter * call <SID>VimEnterHandler()
  384. autocmd TabEnter * call <SID>TabEnterHandler()
  385. autocmd TabLeave * call <SID>TabLeaveHandler()
  386. autocmd WinEnter * call <SID>WinEnterHandler()
  387. autocmd WinLeave * call <SID>WinLeaveHandler()
  388. autocmd BufWinEnter * call <SID>BufWinEnterHandler()
  389. augroup END
  390. let g:nerdtree_tabs_loaded = 1
  391. endfun
  392. " }}}
  393. " s:VimEnterHandler() {{{
  394. "
  395. fun! s:VimEnterHandler()
  396. " if the argument to vim is a directory, cd into it
  397. if g:nerdtree_tabs_startup_cd && isdirectory(argv(0))
  398. exe 'cd ' . escape(argv(0), '\ ')
  399. endif
  400. let l:open_nerd_tree_on_startup = (g:nerdtree_tabs_open_on_console_startup && !has('gui_running')) ||
  401. \ (g:nerdtree_tabs_open_on_gui_startup && has('gui_running'))
  402. if g:nerdtree_tabs_no_startup_for_diff && &diff
  403. let l:open_nerd_tree_on_startup = 0
  404. endif
  405. " this makes sure that globally_active is true when using 'gvim .'
  406. let s:nerdtree_globally_active = l:open_nerd_tree_on_startup
  407. if l:open_nerd_tree_on_startup
  408. let l:focus_file = !s:IfFocusOnStartup()
  409. let l:main_bufnr = bufnr('%')
  410. if !s:IsNERDTreePresentInCurrentTab()
  411. call s:NERDTreeOpenAllTabs()
  412. endif
  413. if (l:focus_file && g:nerdtree_tabs_smart_startup_focus == 1) || g:nerdtree_tabs_smart_startup_focus == 2
  414. exe bufwinnr(l:main_bufnr) . "wincmd w"
  415. endif
  416. endif
  417. endfun
  418. " }}} s:NewTabCreated {{{
  419. "
  420. " A flag to indicate that a new tab has just been created.
  421. "
  422. " We will handle the remaining work for this newly created tab separately in
  423. " BufWinEnter event.
  424. "
  425. let s:NewTabCreated = 0
  426. " }}}
  427. " s:TabEnterHandler() {{{
  428. "
  429. fun! s:TabEnterHandler()
  430. if s:disable_handlers_for_tabdo
  431. return
  432. endif
  433. if g:nerdtree_tabs_open_on_new_tab && s:nerdtree_globally_active && !s:IsNERDTreeOpenInCurrentTab()
  434. call s:NERDTreeMirrorOrCreate()
  435. " move focus to the previous window
  436. wincmd p
  437. " Turn on the 'NewTabCreated' flag
  438. let s:NewTabCreated = 1
  439. endif
  440. if g:nerdtree_tabs_synchronize_view
  441. call s:RestoreNERDTreeViewIfPossible()
  442. endif
  443. if g:nerdtree_tabs_focus_on_files
  444. call s:NERDTreeUnfocus()
  445. " Do not restore focus on newly created tab here
  446. elseif !s:NewTabCreated
  447. call s:NERDTreeRestoreFocus()
  448. endif
  449. endfun
  450. " }}}
  451. " s:TabLeaveHandler() {{{
  452. "
  453. fun! s:TabLeaveHandler()
  454. if g:nerdtree_tabs_meaningful_tab_names
  455. call s:SaveGlobalFocus()
  456. call s:NERDTreeUnfocus()
  457. endif
  458. endfun
  459. " }}}
  460. " s:WinEnterHandler() {{{
  461. "
  462. fun! s:WinEnterHandler()
  463. if s:disable_handlers_for_tabdo
  464. return
  465. endif
  466. if g:nerdtree_tabs_autoclose
  467. call s:CloseIfOnlyNerdTreeLeft()
  468. endif
  469. endfun
  470. " }}}
  471. " s:WinLeaveHandler() {{{
  472. "
  473. fun! s:WinLeaveHandler()
  474. if s:disable_handlers_for_tabdo
  475. return
  476. endif
  477. if g:nerdtree_tabs_synchronize_view
  478. call s:SaveNERDTreeViewIfPossible()
  479. endif
  480. endfun
  481. " }}}
  482. " s:BufWinEnterHandler() {{{
  483. "
  484. " BufWinEnter event only gets triggered after a new buffer has been
  485. " successfully loaded, it is a proper time to finish the remaining
  486. " work for newly opened tab.
  487. "
  488. fun! s:BufWinEnterHandler()
  489. if s:NewTabCreated
  490. " Turn off the 'NewTabCreated' flag
  491. let s:NewTabCreated = 0
  492. " Restore focus to NERDTree if necessary
  493. if !g:nerdtree_tabs_focus_on_files
  494. call s:NERDTreeRestoreFocus()
  495. endif
  496. endif
  497. endfun
  498. " }}}
  499. "
  500. " === plugin event handlers === }}}
  501. call s:LoadPlugin()